From 3213d4c7188f055a89327a769bf09517c685650d Mon Sep 17 00:00:00 2001 From: ShaoHua <345265198@qqcom> Date: Sat, 8 Aug 2026 07:33:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90v1.3.0=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E5=AF=B9=E9=BD=90=E4=B8=8EWeb=E7=AB=AF=E6=A0=B9?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=89=98=E7=AE=A1=E5=85=A8=E9=87=8F=E5=BC=80?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构架构:抽离AddCloudSyncServer到扩展类,补全中间层文档对齐 - 修复根路径404与鉴权跳转:新增中间件处理浏览器请求302跳转 - 实现种子数据初始化:添加默认管理员账号自动创建逻辑 - 优化前端代理与构建:新增Vite代理配置与自动构建Target - 补全测试与文档:新增集成测试,更新开发与部署文档 --- .trae/rules/项目/01-项目架构.md | 116 ++++++-- .trae/rules/项目/04-即时状态记忆.md | 16 +- docs/manual/03-版本记录.md | 16 ++ docs/manual/06-开发环境与构建.md | 66 ++++- docs/project/研发工单-v1.2.0/00-工单总览.md | 2 + docs/project/研发工单-v1.3.0/00-工单总览.md | 69 ++++- .../05-架构文档与代码实际路径对齐.md | 105 ++++++++ .../研发工单-v1.3.0/06-Web端根路径托管前端.md | 89 ++++++ .../DataSeeder/AdminBootstrapOptions.cs | 27 ++ .../Services/DataSeeder/DataSeeder.cs | 46 ++++ .../Services/DataSeeder/IDataSeeder.cs | 18 ++ .../Services/TaskService.cs | 26 +- src/Hua.Todo.Host/Hua.Todo.Host.csproj | 87 ++++++ src/Hua.Todo.Host/Program.cs | 235 +++++++++++++++- src/Hua.Todo.Host/wwwroot/admin/index.html | 74 +++-- .../wwwroot/spa/assets/index-7aro5nrm.css | 1 + .../wwwroot/spa/assets/index-Ct35jSwq.js | 12 + .../wwwroot/spa/assets/index-xkexrwRb.js | 12 + src/Hua.Todo.Host/wwwroot/spa/favicon.svg | 1 + src/Hua.Todo.Host/wwwroot/spa/icons.svg | 24 ++ src/Hua.Todo.Host/wwwroot/spa/index.html | 14 + ...> CloudSyncServiceCollectionExtensions.cs} | 13 +- .../DynamicApi/DynamicApiMiddleware.cs | 91 ++++++- src/Hua.Todo.Web/package-lock.json | 1 + src/Hua.Todo.Web/src/api/client.ts | 12 + src/Hua.Todo.Web/vite.config.ts | 2 + .../CloudSyncAuthSecurityPolicyTests.cs | 2 +- .../CloudSyncEndpointRegistrationTests.cs | 2 +- .../HostBootstrapAndAuthRedirectTests.cs | 245 +++++++++++++++++ .../HostHttpPipelineAnonymousEndpointTests.cs | 155 +++++++++++ .../CloudSync/WebRootStaticFileTests.cs | 189 +++++++++++++ .../DataSeeder/DataSeederTests.cs | 255 ++++++++++++++++++ 32 files changed, 1937 insertions(+), 86 deletions(-) create mode 100644 docs/project/研发工单-v1.3.0/05-架构文档与代码实际路径对齐.md create mode 100644 docs/project/研发工单-v1.3.0/06-Web端根路径托管前端.md create mode 100644 src/Hua.Todo.Application/Services/DataSeeder/AdminBootstrapOptions.cs create mode 100644 src/Hua.Todo.Application/Services/DataSeeder/DataSeeder.cs create mode 100644 src/Hua.Todo.Application/Services/DataSeeder/IDataSeeder.cs create mode 100644 src/Hua.Todo.Host/wwwroot/spa/assets/index-7aro5nrm.css create mode 100644 src/Hua.Todo.Host/wwwroot/spa/assets/index-Ct35jSwq.js create mode 100644 src/Hua.Todo.Host/wwwroot/spa/assets/index-xkexrwRb.js create mode 100644 src/Hua.Todo.Host/wwwroot/spa/favicon.svg create mode 100644 src/Hua.Todo.Host/wwwroot/spa/icons.svg create mode 100644 src/Hua.Todo.Host/wwwroot/spa/index.html rename src/Hua.Todo.HttpApi.AspNetCore/CloudSync/{Services/CloudSyncServerService.cs => CloudSyncServiceCollectionExtensions.cs} (85%) create mode 100644 test/Hua.Todo.Host.Tests/CloudSync/HostBootstrapAndAuthRedirectTests.cs create mode 100644 test/Hua.Todo.Host.Tests/CloudSync/HostHttpPipelineAnonymousEndpointTests.cs create mode 100644 test/Hua.Todo.Host.Tests/CloudSync/WebRootStaticFileTests.cs create mode 100644 test/Hua.Todo.Host.Tests/DataSeeder/DataSeederTests.cs diff --git a/.trae/rules/项目/01-项目架构.md b/.trae/rules/项目/01-项目架构.md index d32fb45..7a3a591 100644 --- a/.trae/rules/项目/01-项目架构.md +++ b/.trae/rules/项目/01-项目架构.md @@ -10,45 +10,101 @@ | 项目 | 类型 | 职责 | 平台 | |---|---|---|---| | [Hua.Todo.Core](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Core) | 类库 | 领域实体、仓储接口(`TaskEntity`、`UserEntity`、`SecurityPolicyEntity`、`AuditLogEntity`、`ITaskRepository` 等) | netstandard / net | -| [Hua.Todo.Application](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Application) | 类库(共享层) | EF Core `TodoDbContext`、`TaskService`/`TaskRepository`、动态 API(`DynamicApi/`)、云同步(`CloudSync/`)、迁移 | net | -| [Hua.Todo.Host](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Host) | ASP.NET 服务端 | 独立服务端宿主,承载本地动态 API + 云同步端点 + Admin 管理后台静态资源 | Windows/Linux 等服务器 | +| [Hua.Todo.Application](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Application) | 类库(共享层) | EF Core `TodoDbContext`、`TaskService`/`TaskRepository`、CloudSync 业务服务(`Application/Services/CloudSync/`)、动态 API 属性层、迁移 | net | +| [Hua.Todo.HttpApi.AspNetCore](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.HttpApi.AspNetCore) | 类库(ASP.NET Core 专属 HTTP 层) | 承载依赖 ASP.NET Core 的实现:DynamicApiMiddleware、Mcp Server 端点、云同步服务端 DI(SessionAuthenticationHandler、CloudSyncServerService) | net(依赖 `Microsoft.AspNetCore.App` 共享框架) | +| [Hua.Todo.Host](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Host) | ASP.NET 服务端 | **Host 双角色统一部署**:(1) Web 端前端/后端同源部署(承载前端静态资源 + Dynamic API + Swagger + MCP);(2) 云同步权威服务端(供 MAUI/Avalonia 的 CloudSyncProxy 转发);不区分模式 | Windows/Linux 等服务器 | | [Hua.Todo.Maui](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Maui) | MAUI 客户端 | Windows / macOS / iOS / Android 入口;内嵌 WebServer + WebView 承载 Vue 前端 | 多端 | | [Hua.Todo.Avalonia](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Avalonia) | Avalonia 客户端 | Linux / Windows 桌面入口;内嵌 WebServer + `WebView.Avalonia` 承载 Vue 前端 | 桌面(含 Linux) | | [Hua.Todo.Web](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Web) | Vite + Vue 3 前端 | 同一份前端构建产物,被多个宿主以静态资源形式承载 | 浏览器/WebView | ## 二、依赖方向(必须保持单向) +> 本图为 2026-08-08 v1.3.0 工单 05 更新版(代码对齐时间戳)。后续修改架构章节时,必须更新本标记并走 Glob/Grep 校验(详见 v1.3.0 00-工单总览 §5「架构文档 ↔ 代码一致性强制策略」)。 + ``` -Hua.Todo.Core - ↑ -Hua.Todo.Application - ↑ - ├── Hua.Todo.Host (服务端注册 AddCloudSyncServer + 动态 API) - ├── Hua.Todo.Maui (客户端,仅注册 AddApplicationServices) - └── Hua.Todo.Avalonia (客户端,仅注册 AddApplicationServices) +Hua.Todo.Core (无依赖) + ↑ 单向引用 +Hua.Todo.Application (依赖 Core) + ↑ 单向引用 +Hua.Todo.HttpApi.AspNetCore (依赖 Application;依赖 Microsoft.AspNetCore.App 共享框架) + ↑ 单向引用 + ├── Hua.Todo.Host (注册 AddApplicationServices + AddCloudSyncServer + AddMcpServerServices) + ├── Hua.Todo.Maui (仅引用 Application + HttpApi.AspNetCore 的 CloudSyncProxy 子模块;不引用 AddCloudSyncServer) + └── Hua.Todo.Avalonia (同上,仅嵌入式 + CloudSyncProxy) Hua.Todo.Web (无 .NET 依赖;通过 HTTP/同源调用上述任一宿主) ``` -- **禁止反向依赖**:Core 不得引用 Application;Application 不得引用任何宿主项目。 -- **客户端不暴露云同步端点**:MAUI / Avalonia 只调用 `AddApplicationServices()`,不调用 `AddCloudSyncServer()`,详见 [docs/project/研发工单-v1.2.0/08-cloud_sync_refactor_plan.md](../../../docs/project/研发工单-v1.2.0/08-cloud_sync_refactor_plan.md)。 +- **禁止反向依赖**:Core 不得引用 Application;Application 不得引用 HttpApi.AspNetCore / 任何宿主;HttpApi.AspNetCore 不得引用宿主项目。 +- **MAUI / Avalonia 不得注册 AddCloudSyncServer**:只注册 `AddApplicationServices()` + `AddCloudSyncProxy()`,权威云同步端点只在 Host 暴露。 +- **MAUI / Avalonia 跨层引用边界**:只引用 HttpApi.AspNetCore 中与嵌入式相关的类型(CloudSyncProxyMiddleware、CloudSyncProxySettings 等),不引用 CloudSyncServerService 等 Host-only DI 扩展(实际通过命名空间自然隔离)。 ## 三、两种运行模式 -### 模式 A:嵌入式(MAUI / Avalonia + WebView) -- 启动内嵌 Kestrel WebServer(默认端口 5057),托管 Vue 前端的静态产物(`wwwroot/`)+ 本地 `/api/*` -- WebView 加载 `HostUrl`(生产)或 `ForEndUrl`(开发,例如 Vite dev server) -- 注入:`window.__API_BASE_URL__ = "${HostUrl}/api"`、`window.mauiInterop` -- 默认 SQLite 路径:`LocalApplicationData/Hua.Todo/Hua.Todo.db`(避免安装目录无写权限) -- 不暴露云同步端点;登录/同步走外部 Host +### 端口对照表(全局统一,禁止硬编码歧义值) -### 模式 B:独立服务端(Hua.Todo.Host) -- 独立部署的 ASP.NET 应用,端口 `:5173`(Vite proxy 目标) -- 同时注册 `AddApplicationServices()` + `AddCloudSyncServer()`,对外提供: - - `/api/*`:本地任务 API(动态 API) - - `/auth/*`、`/tasks/*`、`/sync`、`/security/policy`、`/cloud-sync/probe`:云同步端点 - - `/admin/*`:管理后台前端静态资源 -- 数据库:`src/Hua.Todo.Host/Hua.Todo.db`(开发/测试用) +| 端口 | 用途 | 所属模式 | 说明 | +|---|---|---|---| +| `:5057` | 嵌入式 Kestrel WebServer(HTTP) | 嵌入式 | MAUI / Avalonia 内嵌;Vite proxy 默认 target | +| `:5173` | Web 端 ASP.NET(HTTP) | Web 端 | 前端云同步默认填写的服务器地址;Host `Program.cs.UseUrls` 显式绑定 | +| `:7055` | Web 端 ASP.NET(HTTPS) | Web 端 | 开发环境可选;生产由反向代理(Nginx/Caddy)统一 TLS 终止 | +| `:5174` | Vite dev server(开发) | 两者均可 | WebView / 浏览器开发调试入口;`.env.development` 使用 | + +### 模式 A:嵌入式(MAUI / Avalonia + WebView) + +> 桌面/移动原生客户端,**数据先存本地 SQLite(本地缓存)**,再按需与 Web 端同步。 + +**请求链路(前端 → 本地 → 远端)**: + +``` +WebView(Hua.Todo.Web) + ├─ 本地 Todo 操作(增删改查) → 同源 /api/task* 等动态 API + │ → EmbeddedWebServerService(:5057) + │ → UseDynamicApi + │ → ITaskService / TaskRepository + │ → 本地 SQLite(Hua.Todo.db) + └─ 云同步操作(探测 / 登录 / 拉取 / 推送 / 策略) + → 同源 /api/auth、/api/tasks、/api/cloud-sync/*、/api/security/* + → EmbeddedWebServerService(:5057) + → UseCloudSyncProxy(CloudSyncProxyMiddleware) + → 后端 HttpClient 转发到「用户配置的 Web 端地址」(例 http://localhost:5173) + → Web 端 Hua.Todo.Host 的动态 API 做鉴权与同步 +``` + +**关键规则**: + +- 启动内嵌 Kestrel WebServer(端口 `:5057`),生产托管 Vue 前端静态产物(`wwwroot/`),开发加载 Vite dev server(`ForEndUrl=http://localhost:5174`) +- 注入:`window.__API_BASE_URL__ = "${HostUrl}/api"`(HostUrl=`http://localhost:5057`)、`window.mauiInterop` +- DI 只注册 `AddApplicationServices()` + `AddCloudSyncProxy()`,**不注册 `AddCloudSyncServer()`**:嵌入式端不做用户管理、不暴露云同步的权威端点 +- 默认 SQLite 路径:`LocalApplicationData/Hua.Todo/Hua.Todo.db`(避免安装目录无写权限) +- 本地用户 ID 固定为 `TodoUserIds.LocalUserId = "local"`,与云端用户隔离 + +### 模式 B:Web 端(Hua.Todo.Host,部署入口)= **Host 双角色统一部署** + +> ⚠️ Host 部署时不区分模式: +> - **对外角色**:用户浏览器直接访问 Host(`http://host-address:5173/`)→ 即"Web 端前端 + 后端同源部署" +> - **对内角色**:同时承担"云同步权威服务端"职责,接受 MAUI/Avalonia 嵌入式客户端通过 CloudSyncProxy 转发 `/api/*` 过来的请求 +> +> 因此 `Hua.Todo.Host/Program.cs` 必须同时注册:`AddApplicationServices` + `AddCloudSyncServer` + `AddMcpServerServices`。 +> +> 独立部署的 ASP.NET 服务端,既是**后端接口服务**,也是**前端静态站点托管**,部署产物是单个 `Hua.Todo.Host` 进程。 + +**端点划分(四个逻辑入口,同一进程)**: + +| URL 前缀 | 内容 | 来源 | +|---|---|---| +| `/`(根路径及所有 SPA 路由) | **Hua.Todo.Web 用户前端**(登录后 Todo 列表) | `src/Hua.Todo.Web` 的 `dist/` 构建产物复制到 `Hua.Todo.Host/wwwroot/`;若缺失则由 `MinimalBootstrapPageMiddleware` 返回最小引导页(中文错误提示 + 构建命令,不 404) | +| `/api/*` | **动态 API**(本地+云同步共用的一套接口) | `Hua.Todo.Application/DynamicApi` 扫描所有 `IDynamicApiService` 自动暴露(task / auth / tasks / security / cloud-sync / admin / voice)。权限检查时,浏览器 HTML 直访 Accept:text/html → 302 跳 `/`(前端接管登录弹窗);其他 API 请求(JSON)→ 401/403 JSON 错误 | +| `/mcp` | **MCP Server 端点**(Streamable HTTP) | `McpEndpointExtensions.cs` 映射,供 MCP 客户端调用 | +| `/swagger` + `/admin/*` | **Swagger UI** + **管理后台前端**(用户管理、审计、策略) | Swagger:开发环境启用;Admin:`Hua.Todo.Host/wwwroot/admin/`(当前占位,后续 Vue 3 构建产物填入) | + +**关键规则**: + +- 同时注册 `AddApplicationServices()` + `AddCloudSyncServer()`(后者额外注册 `IPasswordHasher`、云同步动态 API、安全策略校验器等) +- 中间件顺序必须严格遵守(见 `00-工单总览.md` §5):Swagger → MinimalBootstrapPageMiddleware → UseDefaultFiles/UseStaticFiles → CORS → Authentication → Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile → Run +- 端口:HTTP `:5173` + HTTPS `:7055`,`Program.cs` 显式 `UseUrls` 绑定 +- 开发环境**不启用 `UseHttpsRedirection`**(由反向代理统一做 TLS 终止),避免前端 axios 把 307 当错误 +- 数据库:开发/测试使用 `src/Hua.Todo.Host/Hua.Todo.db`(SQLite);生产可切换其他后端 ## 四、跨平台原则 @@ -60,11 +116,15 @@ Hua.Todo.Web (无 .NET 依赖;通过 HTTP/同源调用上述任一宿主) | 扩展点 | 位置 | |---|---| -| DI 注册总入口 | [Hua.Todo.Application/ServiceCollectionExtensions.cs](file:///d:/Proj/6.Hua.Todo/src/Hua.Todo.Application/ServiceCollectionExtensions.cs) | -| 云同步 DI | `Hua.Todo.Application/CloudSync/CloudSyncServiceCollectionExtensions.cs` | -| 云同步端点 | `Hua.Todo.Application/CloudSync/CloudSyncEndpointExtensions.cs` | -| 动态 API 中间件 | `Hua.Todo.Application/DynamicApi/DynamicApiMiddleware.cs` | +| DI 注册总入口 | `Hua.Todo.Application/Common/ServiceCollectionExtensions.cs`(`AddApplicationServices`) | +| 云同步服务端 DI(Host 专用) | `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs`(`AddCloudSyncServer`) | +| 云同步代理 DI / 中间件(嵌入式) | `Hua.Todo.Application/Services/CloudSync/Services/CloudSyncProxyService.cs`(`AddCloudSyncProxy` / `UseCloudSyncProxy`) | +| 云同步代理设置端点(嵌入式) | `Hua.Todo.Application/Services/CloudSync/Services/ICloudSyncProxySettingsService`(继承 `IDynamicApiService`,由 DynamicApi 自动暴露;仅在 AddCloudSyncProxy 时注册 DI) | +| 云同步接口(auth/tasks/security/admin/cloud-sync probe) | `Hua.Todo.Application/Services/CloudSync/Services/ICloud*Service` 实现 `IDynamicApiService`,由 DynamicApi 自动暴露 | +| 动态 API 中间件 + 路由/权限 | `Hua.Todo.HttpApi.AspNetCore/DynamicApi/DynamicApiMiddleware.cs`(含 `DynamicApiRouteAttribute` / `RequirePermissionAttribute`) | +| MCP Server(工具描述符自动生成 + 端点) | `Hua.Todo.HttpApi.AspNetCore/Mcp/DynamicMcpToolExtensions.cs` + `McpServiceCollectionExtensions.cs`(DI 注册 `AddMcpServerServices`) + `McpEndpointExtensions.cs`(端点 `MapMcpServer`) | | 嵌入式 WebServer | `Hua.Todo.{Maui,Avalonia}/Services/EmbeddedWebServerService.cs` | +| 种子数据初始化器(Web 端 DB Migrate 后) | `Hua.Todo.Application/Services/DataSeeder/IDataSeeder`(仅在 `AddCloudSyncServer` 时注册) | | 全局快捷键平台实现 | `Hua.Todo.{Maui,Avalonia}/Services/Platforms/*GlobalHotKeyService.cs` | ## 六、版本统一策略 diff --git a/.trae/rules/项目/04-即时状态记忆.md b/.trae/rules/项目/04-即时状态记忆.md index 540e3d8..4c155a6 100644 --- a/.trae/rules/项目/04-即时状态记忆.md +++ b/.trae/rules/项目/04-即时状态记忆.md @@ -9,9 +9,9 @@ --- -## 一、当前活跃版本 +## 一、当前活跃版本(2026-08-08 快照) -- **进行中版本**:v1.2.0(收尾中)、v1.3.0(规划中) +- **进行中版本**:v1.2.0(收尾中)、v1.3.0(开发中) - **v1.2.0 研发工单总览**:[docs/project/研发工单-v1.2.0/00-工单总览.md](../../../docs/project/研发工单-v1.2.0/00-工单总览.md) - **v1.3.0 研发工单总览**:[docs/project/研发工单-v1.3.0/00-工单总览.md](../../../docs/project/研发工单-v1.3.0/00-工单总览.md) - **PRD**:[docs/project/产品需求文档-1.2.0.md](../../../docs/project/产品需求文档-1.2.0.md) @@ -31,6 +31,7 @@ | 07 - 文档同步与验收 | 进行中 | 进行中 | README/docs 已基本对齐;术语对照刚完成 | | 08 - cloud_sync 重构 | 已设计 | 待实现 | "同源 Host"方案,Vite proxy 补 `/auth` `/tasks` `/sync` `/security` `/cloud-sync` | | 09 - CloudSync 同步策略改进 | 已实现 | 待验证 | TaskEntity 继承 ABP 基类;软删除修复(SaveChangesAsync);前端类型和 cloudSync.ts 已更新;新增 guid.ts | +| 10 - 数据库初始化默认管理员 | 待实现 | 待验证 | Host 服务端 DB Migrate 后自动创建默认管理员;仅 Host 模式;配置驱动 + 幂等 + 强制首登改密;作为 06.1 Bootstrap 自动前置项。详见 [10-数据库初始化默认管理员.md](../../../docs/project/研发工单-v1.2.0/10-数据库初始化默认管理员.md) | ## 三、v1.3.0 工单状态快照 @@ -44,6 +45,8 @@ | 03-03 - AI 任务拆分服务 | 待开始 | 待验证 | 会议专用 LLM prompt + 批量创建子任务 | | 03-04 | 任务建议与确认 UI | 已完成 | 待验证 | MeetingBreakdownDialog.vue + meeting.ts 拆分/确认 API;待集成到 TaskItem | | 04 | 富文本描述、附件与外部链接 | 已完成 | 待验证 | Description 字段 + AttachmentEntity 模型 + 附件 CRUD API + 外部链接 + 平台文件打开器(Maui/Avalonia)+ 前端类型和 API 模块完善 + EF 迁移 + 19 个单元测试通过 | +| 05 | 架构文档与代码实际路径对齐 | 已完成 | 已验证 | 01-项目架构.md §1 补 HttpApi.AspNetCore 条目、§2 ASCII 依赖图加中间层、§5 修复 3 个扩展点路径;代码侧:抽离 AddCloudSyncServer 到 CloudSyncServiceCollectionExtensions.cs;设计修正:MapCloudSyncProxySettings 手动端点已废弃,由 ICloudSyncProxySettingsService(IDynamicApiService)自动暴露,清理 v1.3.0 00-工单总览 Host 中间件顺序末尾的 MapCloudSyncProxySettings;模式 B 中间件顺序硬约束文档化为 Swagger→StaticFiles→CORS→Authentication→Authorization→UseDynamicApi→MapMcpServer→MapFallbackToFile→Run | +| 06 | Web 端根路径托管前端 | 已完成 | 已验证(4 场景集成测试) | Host Program.cs 加 UseDefaultFiles/UseStaticFiles/MapFallbackToFile("index.html"),顺序严格对齐硬约束;Host.csproj 加 BuildHuaTodoWebWebRoot Target(增量 npm build + 双 wwwroot 拷贝 + Node 缺失 Warning + SkipBuildHuaTodoWebWebRoot 开关);docs/manual/06-开发环境与构建.md 新增「模式 B 双开」「1.4 BuildHuaTodoWebWebRoot 自动构建说明」「3.0 部署构建流程」三节;v1.3.0 00-工单总览补充 6 条 Given-When-Then 验收表;WebRootStaticFileTests.cs 4 个集成测试覆盖根路径、SPA fallback、/api 不覆盖、Swagger 不覆盖 | ## 四、关键临时决策 @@ -54,7 +57,10 @@ ## 五、已知未完结事项 / 待办 +- [x] v1.3.0 工单 05 - 架构文档与代码实际路径对齐:已完成(文档 + 代码抽离 + 中间件顺序约束文档化) +- [x] v1.3.0 工单 06 - Web 端根路径托管前端:已完成(Program.cs 三中间件 + Host.csproj BuildHuaTodoWebWebRoot Target + docs 双开指南 + 4 场景集成测试) - [ ] 06 客户端"内存模式"在 `allowPersist=false` 时的端到端落盘清理(含 token、同步队列)尚未充分验证 +- [ ] 工单 10 - 数据库初始化默认管理员:待实现 `IDataSeeder`/`DataSeeder` + Host Program.cs 集成 + `AdminBootstrapOptions` 配置 + 测试先行(详见 [10-数据库初始化默认管理员.md](../../../docs/project/研发工单-v1.2.0/10-数据库初始化默认管理员.md)) - [ ] 06.1 设计中的 Admin 管理后台前端(位于 `Hua.Todo.Host/wwwroot/admin/`)当前仅有 `index.html` 占位,需 Vue 3 + Vite 实现 - [ ] 08 同源 Host 改造:`cloudClient.ts` 的 baseURL 解耦、Vite proxy 端点补全 - [ ] Linux Flatpak/AppImage 自包含产物在干净环境的实测验证(v1.2.0 验收 Linux 部分仍为"待验证") @@ -89,6 +95,12 @@ 7. 清理测试项目中的 SkipCloudSync 排除 - **验证**:Application(net10.0 + net10.0-android) + Host + MAUI(Windows + Android) 全部 0 错误;152 个测试全部通过 +- [x] Host 双角色统一部署路由与鉴权流修补(2026-08-08,对应用户反馈三条问题): + - **问题 1:根路径 404** → 修复:`Hua.Todo.Host/Program.cs` 新增 `MinimalBootstrapPageMiddleware`,前端 index.html 缺失时,访问 `/` 与 SPA 路由返回中文引导页(含三种修复方式 + 可继续使用的 API/Swagger/MCP 入口),不再 404 + - **问题 2:没权限应跳登录页** → 修复:`DynamicApiMiddleware` 新增 `WriteUnauthorizedResponse/WriteForbiddenResponse/IsBrowserHtmlRequest`,GET/HEAD + Accept:text/html 且非 XMLHttpRequest → 302 Location="/" 跳回 Host 前端 SPA,SPA 判断未登录 → 弹 `CloudSyncSettingsDialog`;Accept:json 或非 GET/HEAD → 保持原 401/403 JSON(不破坏 axios/fetch 拦截器) + - **问题 3:Host 部署不区分模式** → 修复:`01-项目架构.md` 模式 B 章节重写为"Host 双角色统一部署 = (1) Web 端前端/后端同源 + (2) 云同步权威服务端",端点扩展为 4 项(/、/api、/mcp、/swagger+/admin),新增中间件顺序硬约束说明 + - **验证**:`HostBootstrapAndAuthRedirectTests.cs` 6 个 ATDD 测试全部通过 + 原 `WebRootStaticFileTests.cs` 4 场景全部通过;`GetDiagnostics` 0 编译错误 + ## 六、最近一次重大重构(如有) - **术语统一与目录中文化**(2026-06): diff --git a/docs/manual/03-版本记录.md b/docs/manual/03-版本记录.md index 5f3b2f4..01f1501 100644 --- a/docs/manual/03-版本记录.md +++ b/docs/manual/03-版本记录.md @@ -10,6 +10,22 @@ - v1.2.0:Linux 支持与增强功能 - v1.3.0(开发中):MCP 服务与语音控制 +### v1.3.0 (2026-08-08,Host 双角色+路由+鉴权流修补) + +- **修复:根路径 404 / 前端缺失引导**:`Hua.Todo.Host/Program.cs` 新增 `MinimalBootstrapPageMiddleware`(位于 Swagger 之后、UseDefaultFiles/UseStaticFiles 之前)。当前端 `wwwroot/index.html` 缺失时,访问 `/` 或 SPA 路由(如 `/settings`)不再返回 404,而是返回**中文最小引导页**(含期望路径、WebRoot、环境版本、三种修复方式:`dotnet build Host` / 手动 npm build / CI skip build;同时列出 `/swagger`、`/api/auth/bootstrap` 等仍可用的入口),解决了"根路径 404 看起来像后端挂了"的误判问题。 +- **修复:没权限应该跳登录页**:`DynamicApiMiddleware` 新增 `WriteUnauthorizedResponse` / `WriteForbiddenResponse` / `IsBrowserHtmlRequest` 三个方法。判定规则:请求是 `GET`/`HEAD` 且 `Accept: text/html` 且 **非** `X-Requested-With: XMLHttpRequest` → 视为「用户在地址栏直访问接口页面」→ 返回 `302 Location: "/"`,回到 Host 前端 SPA,SPA 判断未登录 → 弹出登录/云同步设置 `CloudSyncSettingsDialog` 完成鉴权。**不破坏** axios/fetch:Accept 为 JSON 或非 GET/HEAD 时,继续返回原有的 401/403 JSON(`success=false + message`)。403 同样走 302 跳转。 +- **架构文档对齐(Host 不区分模式 = 双角色统一部署)**:更新 `01-项目架构.md` 模式 B 章节:Host 部署时**同时承担 (1) Web 端前端/后端同源部署和 (2) 云同步权威服务端**两种职责,不区分"模式";端点列表扩展为 4 项(`/`、`/api/*`、`/mcp`、`/swagger + /admin/*`);明确 401/403 在浏览器页面时 302 跳 `/` 由 SPA 接管登录;新增中间件顺序硬约束链接。 +- **验收测试补齐(ATDD)**:新增 `test/Hua.Todo.Host.Tests/CloudSync/HostBootstrapAndAuthRedirectTests.cs`,覆盖 6 场景:① 无 index.html 时 GET `/` + AcceptHtml → 200 + 引导页含「前端尚未构建」构建命令;② `/settings`(SPA 路由)同样 200 引导页;③ 未登录浏览器 GET `/api/security/policy`(PolicyRead 权限)+ AcceptHtml → 302 `/`;④ 未登录 AcceptJson 同一接口 → 401 JSON `success=false`;⑤ PUT `/api/security/policy`(非 GET)+ AcceptHtml → 仍 401 JSON 不跳转;⑥ 匿名 `POST /api/cloud-sync/probe` 不被 redirect 或 401 影响。6 个新测试 + 原 `WebRootStaticFileTests` 4 个场景 = 10 个全部通过。 + +### v1.3.0 (2026-06-17) + +- **架构对齐(v1.3.0 工单 05)**:新增「Hua.Todo.HttpApi.AspNetCore」中间层到项目清单和依赖图(Core → Application → HttpApi.AspNetCore → {Host,Maui,Avalonia});抽离 `AddCloudSyncServer` 到 `CloudSyncServiceCollectionExtensions.cs`;清理 `01-项目架构.md` §5 过时的扩展点路径;`00-工单总览.md` 明确模式 B 中间件顺序硬约束,`MapCloudSyncProxySettings` 已废弃(接口改由 DynamicApi 自动暴露)。 +- **Web 端根路径托管前端(v1.3.0 工单 06)**:模式 B 下 Host 同时承担「动态 API + 前端静态站点 + MCP」三大职责;`Program.cs` 新增 `UseDefaultFiles`/`UseStaticFiles`/`MapFallbackToFile("index.html")` 并对齐中间件顺序;`Host.csproj` 新增 `BuildHuaTodoWebWebRoot` Target(增量 npm build + 双份 wwwroot 拷贝 + Node 缺失 Warning);开发文档新增「模式 B 双开」与「BuildHuaTodoWebWebRoot 自动构建」指南;新增 `WebRootStaticFileTests.cs` 覆盖根路径 / SPA fallback / API 不覆盖 / Swagger 不覆盖 4 场景。 +- **CloudSync Swagger + 路由修复**:CloudSync 接口改查 `IsMetadataEnabled`,移除 `IsEnabled=false`,Swagger 中 `/auth/* /tasks/* /security/* /admin/* /cloud-sync/*` 可见;MCP 工具改用命名空间过滤 `Hua.Todo.Application.CloudSync.*`,避免云同步接口暴露为 MCP。 +- **CloudSync 端点 DynamicApi 化**:移除 16 个手动 `MapCloudSyncEndpoints()`,新增 5 个 `IDynamicApiService` 接口 + `DynamicApiRouteAttribute` + `RequirePermissionAttribute`,中间件支持自定义前缀与权限检查;错误响应统一 401/403。 +- **测试项目重构**:`src/test/` → `test/`;Host.Tests 161 个测试全部通过。 +- **Host 云同步端点补齐修复**:Program.cs 新增 `AddCloudSyncServer()` + `MapCloudSyncEndpoints()`(后已改为 DynamicApi) + `app.UseAuthentication()` 顺序;前端云同步默认账号 `admin`/`123456` + 默认地址 `http://localhost:5173`;`TaskList.vue` 新增登录/登出/同步按钮。 + ### v1.3.0 (2026-06-16) - **MCP 服务**:新增 MCP 协议端点(`/mcp`),DynamicMcpToolExtensions 自动扫描 IDynamicApiService 接口生成 MCP 工具;当前覆盖 ITaskService(9 个)+ IVoiceService(4 个)= 13 个 MCP 工具 diff --git a/docs/manual/06-开发环境与构建.md b/docs/manual/06-开发环境与构建.md index 8db26b0..330115c 100644 --- a/docs/manual/06-开发环境与构建.md +++ b/docs/manual/06-开发环境与构建.md @@ -27,19 +27,60 @@ npm install ### 1.3 启动开发环境 -**方式一:Windows 三件套(推荐)** +#### 方式一:模式 B「Web 端双开」(推荐,调试 Host API + 前端) + +> 模式 B = Hua.Todo.Host 同时承担「动态 API + 前端静态站点 + MCP」三大职责, +> 访问 `http://localhost:5173/` 即可看到完整 Todo 前端,HTTP 同源无需再配 Vite proxy。 + +开发调试时分两个终端(修改前端能 HMR 热更新,不会每次都触发 npm build): ```powershell -# 终端 1:启动 Host(API 服务端) -.\start-host.ps1 - -# 终端 2:启动前端开发服务器 -.\start-dev.ps1 +# 终端 1:启动 Host(监听 :5173 HTTP + :7055 HTTPS,未登录时 HTTP 即可) +# ⚠️ 若尚未构建过前端,Host 直接启动后 GET / 返回 404。首次需要先 build 一次: +# cd src/Hua.Todo.Web && npm ci && npm run build +# 后续 build 会被 MSBuild Target 自动触发,见 1.4 节。 +cd src/Hua.Todo.Host +dotnet run # 或 Visual Studio F5 调试 Hua.Todo.Host ``` -**方式二:MAUI 嵌入模式** +```powershell +# 终端 2:Vite 开发服务器(:5174,负责前端 HMR;通过 .env.development 把 API 打回 :5173 Host) +cd src/Hua.Todo.Web +npm run dev +# 浏览器访问 http://localhost:5174 → 代码保存后浏览器立即刷新 +``` -在 Visual Studio 中直接调试运行 `Hua.Todo.Maui` 项目,MAUI 会自动启动内嵌 WebServer 并加载前端。 +> 关闭 Vite 后直接访问 `http://localhost:5173/` 也能看到上次构建的前端静态页(非 HMR)。 + +#### 方式二:模式 A「MAUI 嵌入模式」 + +在 Visual Studio 中直接调试运行 `Hua.Todo.Maui` 项目,MAUI 会自动启动内嵌 WebServer(`localhost:5057`)并加载前端。 +云同步链路:前端同源调用 `:5057` → CloudSyncProxy → 用户配置的 Web 端地址(默认 `http://localhost:5173`)。 + +#### 方式三:纯 Host 发布模式(快速验证部署产物) + +```powershell +# 单次构建(前端 dist + Host 二进制 + wwwroot 复制) +dotnet build src/Hua.Todo.Host -c Release +dotnet run -c Release --project src/Hua.Todo.Host +# 浏览器访问 http://localhost:5173/ → 与部署后的体验一致 +``` + +### 1.4 BuildHuaTodoWebWebRoot 自动构建说明 + +`src/Hua.Todo.Host/Hua.Todo.Host.csproj` 内置 `BuildHuaTodoWebWebRoot` MSBuild Target(触发时机:`BeforeBuild`),每次 `dotnet build/publish Hua.Todo.Host` 时执行: + +| 行为 | 说明 | +|---|---| +| 增量构建 | 仅当 `src/Hua.Todo.Web/package.json` 或 `src/**/*.{ts,vue,css,html}` 变化时才 npm build,否则跳过 | +| Node 缺失保护 | 未安装 Node/npm 时仅输出 Warning,不阻塞 Host C# API 层编译(CI 可在外层单独 build 前端) | +| 跳过开关 | `dotnet build /p:SkipBuildHuaTodoWebWebRoot=true` 禁用整个 Target | +| 双份拷贝 | 产物同时写入 `src/Hua.Todo.Host/wwwroot/`(本地 `dotnet run`)和 `bin/Release/net10.0/wwwroot/`(`dotnet publish` 打包) | + +常见提示排查: + +- **`[BuildHuaTodoWebWebRoot] 未检测到 Node/npm`**:安装 Node.js 18+ 并重启终端;或 CI 先 `cd src/Hua.Todo.Web && npm ci && npm run build`。 +- **`[BuildHuaTodoWebWebRoot] npm run build 退出码 != 0`**:去 `src/Hua.Todo.Web` 手动 `npm run build` 看 TypeScript/Vite 报错,修复后再 build Host。 ## 2. 版本构建 (Release Build) @@ -68,6 +109,15 @@ npm install ## 3. 服务端部署 +### 3.0 构建流程(新增:前端 dist 自动进 wwwroot) + +v1.3.0 起 `dotnet publish src/Hua.Todo.Host` 会自动执行: +1. `npm install`(仅 node_modules 不存在时) +2. `npm run build` → `src/Hua.Todo.Web/dist` +3. `dist/**/*` → `publish_output/wwwroot/`(保留目录结构) + +因此部署前**不再需要**单独 build 前端,直接一条命令即可。CI 若在容器内无 Node,设置 `-p:SkipBuildHuaTodoWebWebRoot=true` 并在外层先 `npm ci && npm run build`。 + ### 3.1 Docker 部署(推荐) ```bash diff --git a/docs/project/研发工单-v1.2.0/00-工单总览.md b/docs/project/研发工单-v1.2.0/00-工单总览.md index 8ac68b5..5f5c227 100644 --- a/docs/project/研发工单-v1.2.0/00-工单总览.md +++ b/docs/project/研发工单-v1.2.0/00-工单总览.md @@ -51,3 +51,5 @@ - Linux:在基线发行版(建议 Ubuntu LTS)上可启动、可渲染前端、可调用本地 API;且有可安装/可运行的交付产物 - Search:可在主界面按 Todo 待办项标题实时过滤(含层级 Todo 的展示策略清晰) - 云同步(基础可用):可配置服务端地址;登录后可拉取该用户的 Todo 数据;服务端可下发"是否允许落盘";客户端在禁止落盘时不产生本地持久化 + + diff --git a/docs/project/研发工单-v1.3.0/00-工单总览.md b/docs/project/研发工单-v1.3.0/00-工单总览.md index 57e30c9..223133c 100644 --- a/docs/project/研发工单-v1.3.0/00-工单总览.md +++ b/docs/project/研发工单-v1.3.0/00-工单总览.md @@ -40,6 +40,16 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级: 当前版本暂无串行工单依赖。 +### 2.4 补充工单(架构对齐,2026-08-08 新增) + +> 按 [04-研发工单全流程规范.md](../../../.trae/rules/全局/04-研发工单全流程规范.md) §2.3「新增工单约束」:已有条目不可改写,本章节为本次**新增**。触发原因:本次 307 修复后重写 [01-项目架构.md](../../../.trae/rules/项目/01-项目架构.md) §3/§5,对照代码发现 2 处架构缺口(文档遗漏 HttpApi.AspNetCore 层、Web 端根路径无前端托管)。 + +| 工单编号 | 标题 | 负责人 | 状态 | 并行性 | +|---|---|---|---|---| +| 05 | 架构文档与代码实际路径对齐 | - | 待开始 | 与 03/06 并行 | +| 06 | Web 端根路径托管前端 | - | 待开始 | 与 03/05 并行(建议 05 缺口 D 先落,再推 06 Program.cs 集成) | +| 07 - 数据库初始化默认管理员 | 待实现 | 待验证 | Host 服务端 DB Migrate 后自动创建默认管理员账号;仅 Host 模式;配置驱动 + 幂等 + 强制首登改密;作为 06.1 Bootstrap 的自动前置项。详见 [10-数据库初始化默认管理员.md](10-数据库初始化默认管理员.md) | + --- ## 三、各子工单摘要 @@ -123,6 +133,33 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级: - 本地附件可通过系统关联程序打开 - 附件数量(20个)和大小(50MB)限制生效 +### 3.5 工单 05 - 架构文档与代码实际路径对齐 + +**目标**:修正 01-项目架构.md §1/§2/§5 共 5 处与实际代码结构不一致的描述,使规则文档成为代码的单一事实来源。 + +**核心需求**: +- **文档侧**:§1 项目清单补 `Hua.Todo.HttpApi.AspNetCore` 条目;§2 ASCII 依赖图加该中间层;§5 路径修正 Mcp 描述 +- **代码侧**:`CloudSyncServerService.cs` 抽离 `AddCloudSyncServer()` 到新文件 `CloudSyncServiceCollectionExtensions.cs`;新建 `CloudSyncEndpointExtensions.cs` 还原 `MapCloudSyncProxySettings()` +- **回归**:Host 编译 + 161 个既有测试全部通过 + +**不包含**:业务逻辑变更、公共 API 签名修改、v1.2.0 工单 10(DataSeeder) + +**验收标准**:详细 5 条 Given-When-Then 见 [05-架构文档与代码实际路径对齐.md](./05-架构文档与代码实际路径对齐.md) §五。 + +### 3.6 工单 06 - Web 端根路径托管前端 + +**目标**:补齐模式 B(Hua.Todo.Host Web 端)的前端静态页托管链路,使 `dotnet publish Hua.Todo.Host` 产出一个「页面 + API + Admin 占位」完整可部署的服务。 + +**核心需求**: +- Host `Program.cs`:`UseDefaultFiles` + `UseStaticFiles` + `MapFallbackToFile("index.html")`(支持 SPA 路由) +- Host `csproj`:新增 `BuildHuaTodoWebWebRoot` Target(`npm install`/`npm run build`/`copy dist → wwwroot`,Inputs/Outputs 支持增量构建) +- 文档:`docs/manual/08-构建与开发指南.md` 追加模式 B 开发双开步骤 + 生产发布步骤 +- 测试:`WebRootStaticFileTests.cs` 验证根路径 `/` 与 SPA fallback 不覆盖 `/api/*` + +**不包含**:Admin 管理后台前端实现(v1.2.0 工单 06.1)、嵌入式模式前端托管(`EmbeddedWebServerService` 各自管理) + +**验收标准**:详细 6 条 Given-When-Then 见 [06-Web端根路径托管前端.md](./06-Web端根路径托管前端.md) §四。 + --- ## 四、待验证表 @@ -149,6 +186,14 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级: | 04 | 附件数量/大小限制 | 待验证 | 20 个 / 50MB | | 04 | 待办项删除时附件级联清理 | 待验证 | - | | 04 | 跨平台编辑安全(移动端不覆盖描述/附件) | 待验证 | 桌面设值 → 移动端改标题 → 桌面验证不丢失 | +| 05 | 规则文档 §1/§2 补 HttpApi.AspNetCore 条目与依赖图 | 待验证 | Glob 到项目 + 规则描述一致 | +| 05 | §5 扩展点 AddCloudSyncServer / MapCloudSyncProxySettings / Mcp 3 个路径修复 | 待验证 | 规则路径 = Grep 实际路径;Host 编译 0 错误 | +| 05 | 既有 AddCloudSyncServer 回归测试 | 待验证 | `CloudSyncEndpointRegistrationTests` 5 个用例全部通过 | +| 06 | Host `/` 返回 Hua.Todo.Web index.html(200 + div#app) | 待验证 | `WebRootStaticFileTests.Root_Get_ReturnsIndexHtml` 通过 | +| 06 | SPA 路由 `/settings` fallback 到 index.html | 待验证 | 不 404;不覆盖 `/api/*` 动态 API | +| 06 | `dotnet publish Hua.Todo.Host` 产出包含前端静态资源 | 待验证 | publish/wwwroot/index.html 存在 + 资源齐全 | +| 06 | 增量构建前端未变时跳过 npm build | 待验证 | MSBuild Inputs/Outputs 检测 Skipping target 提示 | +| 06 | 模式 B 开发双开文档可执行性 | 待验证 | 从零 clone → 文档 step by step 跑通页面 + API | --- @@ -172,6 +217,9 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级: | 附件存储策略 | 附件存储在应用数据目录 `Attachments/` 子目录;外部链接不复制文件仅存 URL;单文件 50MB / 每待办项 20 个上限 | | 外部程序启动 | 通过 `IPlatformAttachmentOpener` 接口实现平台差异:Windows 用 `Process.Start`,Linux 用 `xdg-open` | | 工单 04 与 03 共享文件 | `TaskEntity.cs`、`TodoDbContext.cs`、`task.ts`、`TaskEditDialog.vue` 为共享文件,工单 03 先写入,04 后续追加 | +| **架构文档 ↔ 代码一致性强制策略** | 后续凡改 01-项目架构.md 任何一节,改后必须立即对 §1/§2/§5 所有提到的文件路径做一次 Glob/Grep 校验;不一致 → 先开对齐工单,不直接碰业务代码(本次 05 工单即为策略首次落地案例) | +| **Host 中间件顺序固定模板** | Swagger → UseDefaultFiles/UseStaticFiles → CORS → Authentication → Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile → Run(顺序错误直接判作编译/验收失败) | +| **MSBuild 前端构建 Target 命名与增量** | Host 前端构建 Target 固定名 `BuildHuaTodoWebWebRoot`;必须显式写 `Inputs="$(HuaTodoWebSrc);$(HuaTodoWebPkgJson)" Outputs="$(WebRoot)/index.html"` 支持增量;Node/npm 不存在时只 Warning 不阻塞 API 层 build | --- @@ -185,6 +233,9 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级: | LLM API(AI 拆分) | 待确认 | Host 端调用 | | 浏览器 MediaRecorder API | 已就绪 | 前端录音 | | 工单 02 LlmClientService | 待实现 | 会议拆分复用 | +| 工单 05 前置(缺口 D) | 待完成 | 建议先还原 `CloudSyncEndpointExtensions.cs`(MapCloudSyncProxySettings),再推 06 Program.cs 集成 | +| Node.js + npm(构建机器) | 外部依赖 | 构建 Hua.Todo.Web 必备;缺失时 Host csproj Target 仅 Warning | +| 01-项目架构.md §3 模式 B 端点划分表 | 已固化 | 本批次工单 05/06 的对齐基准 | --- @@ -199,9 +250,25 @@ Hua.Todo v1.3.0 版本聚焦于三个核心能力的升级: | 会议录音文件过大 | 上传超时/存储压力 | 前端限制最长 2 小时;压缩音频格式 | | 浏览器 MediaRecorder 兼容性 | 部分平台录音不可用 | 降级提示使用文字输入 | | STT 转写准确率不足 | 会议纪要质量差 | 转写后支持用户编辑修正 | +| **工单 05 风险:架构文档与代码路径反复漂移** | 后续智能体又按旧路径改错文件 | 05 验收完成后,01-项目架构.md §2 ASCII 图顶部加"最后一次代码对齐时间戳";后续改架构章节必须更新时间戳 + 走 Glob 校验 | +| **工单 05 风险:抽离 AddCloudSyncServer 时丢 using/命名空间** | Host 编译失败 | 改动范围严格控制在 CloudSync 两个文件;Program.cs 仅补 using;单步 build 验证后再提交 | +| **工单 06 风险:MapFallbackToFile 覆盖 /api 或 /swagger** | API/Swagger 全 404 | 中间件顺序严格按 §5 固定模板;集成测试同时验证 `/`、`/settings`(fallback)、`/api/cloud-sync/probe`(DynamicApi)、`/swagger`(Swagger UI)四个端点 | +| **工单 06 风险:CI 环境 npm 网络差/无 Node** | publish 超时或失败 | MSBuild Target 加 Node 缺失检测 → Warning + 跳过;生产部署可在外层 CI 先 build 前端再 publish Host | +| **工单 06 风险:增量构建 Inputs/Outputs 粗粒度** | 前端代码改了不重 build 或每次都重 build | Inputs 精确到 `**/*.{ts,vue,css,html}` + `package.json`;Outputs 精确到 `wwwroot/index.html`,首次过 CI 后人工改一行 .vue 验证是否触发 rebuild | --- +## 补充工单(追加于 2026-08-08) + +### 工单 10 - 数据库初始化默认管理员 + +- **目标**:Host 服务端 DB Migrate 完成后自动创建默认管理员账号,免去手动调用 Bootstrap API +- **范围**:仅 `Hua.Todo.Host`;嵌入式模式(MAUI/Avalonia)不涉及 +- **关联**:作为 06.1 服务端安全设计 Bootstrap 的自动前置项;与现有 `BootstrapAdmin` API 共存 +- **详见**:[10-数据库初始化默认管理员.md](10-数据库初始化默认管理员.md) +--- + **创建日期**:2026-06-15 -**修订日期**:2026-06-16 +**修订日期**:2026-08-08 **版本**:v1.3.0 +**修订说明(2026-08-08)**:新增架构对齐补充工单 05/06(01-项目架构.md §3 模式 B 端点划分表与代码实际不一致触发);所有新增条目均以"追加末尾"形式写入,未改动 2026-06-16 版本已有表格行和章节结构(符合 04-研发工单全流程规范 §2.1 已有文件不可触碰)。 diff --git a/docs/project/研发工单-v1.3.0/05-架构文档与代码实际路径对齐.md b/docs/project/研发工单-v1.3.0/05-架构文档与代码实际路径对齐.md new file mode 100644 index 0000000..6992eb2 --- /dev/null +++ b/docs/project/研发工单-v1.3.0/05-架构文档与代码实际路径对齐.md @@ -0,0 +1,105 @@ +# 研发工单 v1.3.0 - 05:架构文档与代码实际路径对齐 + +> 术语澄清:本文件中"研发工单"指智能体/开发者执行的**编码工作项**,与业务侧的 Todo 待办项无关。详见 [.trae/rules/全局/04-研发工单全流程规范.md](../../../.trae/rules/全局/04-研发工单全流程规范.md)。 + +--- + +## 一、背景与动机 + +在修正「云同步探测 307 → HTTPS 重定向规则」时,重写了 `.trae/rules/项目/01-项目架构.md` §3「两种运行模式」与 §5「关键扩展点」以固化正确链路。但对照代码后发现:**规则文档中的两处描述已与当前代码结构不一致**,会导致后续智能体/开发者按规则路径搜索文件时直接落空,进而引发与本次 307 同类的"链路理解错位 → 改错文件"问题。 + +本工单是纯**工程化对齐工单**(无用户可见的业务功能变更),目标是让 01-项目架构.md 成为"代码的单一事实来源":要么文档改到与代码一致,要么代码重构到与文档一致,**不允许中间的灰色状态**。 + +## 二、缺口清单(代码 vs 01-项目架构.md 规则) + +| # | 缺口 | 规则写的内容 | 代码实际情况 | 本工单处理方式 | +|---|---|---|---|---| +| A | **§1 项目清单漏列 HttpApi.AspNetCore 层** | Core / Application / Host / Maui / Avalonia / Web 共 6 行 | 存在 `src/Hua.Todo.HttpApi.AspNetCore/`(CloudSync/DynamicApi/Mcp 三层 ASP.NET Core 专属实现),Host 直接引用它 | **规则加表项**:补齐项目条目,注明"承载 ASP.NET Core 专属的 HTTP 层代码(Application 保持 netstandard 兼容)" | +| B | **§2 依赖方向图缺失 HttpApi.AspNetCore 中间层** | `Application ← { Host, Maui, Avalonia }` 直接三层 | 实际是 `Application ← HttpApi.AspNetCore ← Host`,MAUI/Avalonia 只按需引用 HttpApi.AspNetCore 的部分子模块 | **规则改 ASCII 图**:新增中间层 + 注释跨层引用的边界 | +| C | **§5 扩展点路径:AddCloudSyncServer 所在文件名错误** | `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs` | 定义在 `CloudSync/Services/CloudSyncServerService.cs` 内(非 *Extensions 命名,与 Mcp / DynamicApi 的命名习惯不一致) | **代码重命名/抽离**:新建 `CloudSyncServiceCollectionExtensions.cs`,把 `AddCloudSyncServer()` 移入;原 `CloudSyncServerService.cs` 仅保留服务实现类 | +| D | **§5 扩展点路径:MapCloudSyncProxySettings 所在文件缺失** | `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncEndpointExtensions.cs`(仅保留 `MapCloudSyncProxySettings`) | Grep 显示该方法**仅被 Host Program.cs 调用,定义文件不存在**(即时状态记忆 2026-06-17 条目说"仅保留 MapCloudSyncProxySettings",但文件未落地) | **代码补齐**:新建 `CloudSyncEndpointExtensions.cs`,写入 `MapCloudSyncProxySettings` 扩展方法(将目前分散的或在旧文件被删除的内容还原),并更新引用命名空间 | +| E | **§5 扩展点:McpServerMiddleware 实际不存在** | `DynamicMcpToolExtensions.cs + McpServerMiddleware` | 只有 `McpEndpointExtensions.cs`(`MapMcpServer`)+ `McpServiceCollectionExtensions.cs`(`AddMcpServerServices`),无 Middleware 类型 | **规则改路径**:改为 `McpEndpointExtensions.cs` + `McpServiceCollectionExtensions.cs`;不强求新增 Middleware 类型(EndpointRouting 扩展符合 ASP.NET Core 主流模式),只改描述 | + +## 三、目标 / 范围 + +### 3.1 做什么 + +- 对齐 01-项目架构.md §1/§2/§5 三章共 5 处缺口(A–E,见上表) +- 代码改动:C(文件抽离+重命名)、D(新建+还原) +- 文档改动:A、B、E(仅改规则文档,不改代码) + +### 3.2 不做什么 + +- 不改业务逻辑(不触碰 CloudSyncServerService 内的方法体、不改动 DynamicApiMiddleware 行为) +- 不改公共 API 契约(AddCloudSyncServer / MapCloudSyncProxySettings 的签名保持不变,仅迁移到新文件) +- 不做 v1.2.0 工单 10 范围内的工作(DataSeeder) + +## 四、前置条件 / 依赖 + +- 无;可与 v1.3.0 工单 06、03 并行执行 +- 共享文件:`.trae/rules/项目/01-项目架构.md`(只读,只做局部追加替换;不重排原有章节结构) +- 共享文件:`Hua.Todo.HttpApi.AspNetCore.csproj`(新增 2 个源文件,C# 编译级冲突风险极低) + +## 五、验收标准(Given-When-Then) + +| # | 场景 | GIVEN | WHEN | THEN | +|---|---|---|---|---| +| 1 | 缺口 A/B(规则补层) | - | 打开规则文档 §1/§2 | §1 出现 `Hua.Todo.HttpApi.AspNetCore` 表项,描述包含"ASP.NET Core 专属 HTTP 层";§2 ASCII 图新增中间层,箭头方向正确(单向 Application → HttpApi.AspNetCore → Host) | +| 2 | 缺口 C(DI 扩展抽离新文件) | `CloudSyncServerService.cs` 当前包含 `AddCloudSyncServer(this IServiceCollection)` | 抽离为 `CloudSyncServiceCollectionExtensions.cs` | 新文件存在且命名空间为 `Hua.Todo.HttpApi.AspNetCore.CloudSync`;`Program.cs` 编译通过无需改 using;旧文件仅保留服务实现(非 static / 非扩展方法)| +| 3 | 缺口 D(MapCloudSyncProxySettings 新建文件) | `MapCloudSyncProxySettings(this IEndpointRouteBuilder)` 在 Program.cs 被调用但无定义文件 | 创建 `CloudSyncEndpointExtensions.cs` 并补全方法体 | 新文件存在;`Program.cs` 加对应 using 后编译通过;该端点在 Host 启动后响应 `/api/cloud-sync/proxy-settings`(与既有集成测试一致) | +| 4 | 缺口 E(规则改 Middleware 描述) | §5 写 "McpServerMiddleware" | 将 §5 对应行改为 "McpEndpointExtensions.cs(MapMcpServer 端点) + McpServiceCollectionExtensions.cs(DI 注册)" | 规则路径描述与实际文件一一对应,Grep 两个路径都能找到 | +| 5 | 回归(全部编译通过) | - | `dotnet build src/Hua.Todo.Host/Hua.Todo.Host.csproj` + `dotnet test test/Hua.Todo.Host.Tests/` 全量 | 0 编译错误;既有测试(含 AddCloudSyncServer DI 注册测试)全部通过,不允许新增失败用例 | + +## 六、并行冲突规避 + +| Touch List | 共享文件策略 | 编译绿线策略 | +|---|---|---| +| `.trae/rules/项目/01-项目架构.md` | 追加/局部替换,**不改 §3 模式 A/B 的正文段落和 §6 版本统一/§7 测试架构**;本工单只动 §1 表、§2 ASCII 图 + 说明、§5 表 | 纯 Markdown,无编译;提交前 diff 确认未改动他人段落 | +| `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServerService.cs` | 只移除 `AddCloudSyncServer` 静态方法块,**不改动类的其他成员**;新文件独立写入,无双向合并 | `dotnet build Hua.Todo.HttpApi.AspNetCore.csproj` 必须 0 错误 | +| `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs` | 新建;其他并行工单不改同名文件 | 同上 | +| `Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncEndpointExtensions.cs` | 新建;其他并行工单不改同名文件 | 同上 | +| `Hua.Todo.Host/Program.cs` | 可能需要追加 `using Hua.Todo.HttpApi.AspNetCore.CloudSync;`(如果原 using 不完整);不碰其他语句 | `dotnet build Hua.Todo.Host.csproj` 0 错误 | + +## 七、风险与回滚 + +| 风险 | 影响 | 回滚策略 | +|---|---|---| +| C/D 移动方法体时遗漏了 using 或扩展方法的 `this` 参数 | Host 编译失败 | git 单文件 revert;不要手改多个地方 | +| §2 依赖方向图改动后,后续智能体仍按旧图改代码 | 跨工单改错 | 规则文档 §2 顶部加一句"本图为 2026-08-08 v1.3.0 工单 05 更新版"作为时间戳标记 | +| D 还原的 MapCloudSyncProxySettings 方法体与"即时状态记忆 2026-06-17"实际实现不一致 | 端点行为变化 | 先从 git 历史(CloudSyncEndpointExtensions.cs 被删前的版本)原样恢复,不做任何逻辑修改 | + +## 八、与其他工单的语音/UI 入口衔接 + +> 按 [05-多入口功能同步规范.md](../../../.trae/rules/项目/05-多入口功能同步规范.md) 第 3.2 条:纯基础设施调整无需检查双入口。 + +| 入口 | 已规划 | 方案 | +|---|---|---| +| UI | ❌(无需) | 纯工程化,用户无感知 | +| 语音 | ❌(无需) | 不涉及业务操作 | + +--- + +**创建日期**:2026-08-08 +**关联规则**:[01-项目架构.md](../../../.trae/rules/项目/01-项目架构.md) +**关联工单**:v1.2.0 工单 08 / 工单 10;v1.3.0 工单 01 / 06 + +--- + +## 九、执行期设计修正(2026-08-08,不违反 §2.1:本段落为本次执行前新增,未改动 §二 缺口表原有行) + +### 9.1 缺口 D 重判:`MapCloudSyncProxySettings` 实际上不应作为手动 Map 存在 + +**执行前代码核对结果**:`ICloudSyncProxySettingsService` 已继承 `IDynamicApiService`(见 `src/Hua.Todo.Application/Services/CloudSync/Services/ICloudSyncProxySettingsService.cs`),因此代理设置端点(GET/POST `/api/cloudSyncProxySettings`)**由 DynamicApiMiddleware 自动暴露**。即时状态记忆 2026-06-17 条目记录的「CloudSyncEndpointExtensions 仅保留 `MapCloudSyncProxySettings`」属于"CloudSync 端点 DynamicApi 化"过程中的中间状态快照:移除 16 个手动 Map 时,旧 MapCloudSyncProxySettings 本应连同删除却留下了文档引用,造成规则文档 §5 出现了「云同步代理设置端点 → CloudSyncEndpointExtensions.cs」这个错误路径。 + +**修正后缺口 D**:从「代码侧新建 CloudSyncEndpointExtensions.cs 还原方法」**改为「规则侧清理过时引用」**: +- 01-项目架构.md §5:把「云同步代理设置端点 → CloudSyncEndpointExtensions.cs」改为 `ICloudSyncProxySettingsService`(DynamicApi 自动暴露,仅在嵌入式 AddCloudSyncProxy 时注册) +- v1.3.0 00-工单总览 §5 关键决策「Host 中间件顺序固定模板」:末尾移除 `→ MapCloudSyncProxySettings`(Host 上代理设置端点本来就不注册) +- 代码侧:不新建 CloudSyncEndpointExtensions.cs,也不在 Program.cs 追加 `MapCloudSyncProxySettings()` 调用 + +**验收影响**: +- §五 验收 #3 对应取消;新增验收 #3b:01-项目架构.md §5 中不再出现 `CloudSyncEndpointExtensions.cs` 和 `MapCloudSyncProxySettings` 两个字符串;`ICloudSyncProxySettingsService` 行出现且描述正确 +- §五 验收 #5 回归编译仍然保留(仍然是 0 错误 + 既有测试全过) + +### 9.2 其余缺口 A/B/C/E 不变 + +A(§1 补条目)、B(§2 ASCII 补中间层)、C(抽离 AddCloudSyncServer 到 *Extensions 文件)、E(Mcp 描述改 EndpointExtensions + ServiceCollectionExtensions)与代码实际核对一致,按原计划执行。 diff --git a/docs/project/研发工单-v1.3.0/06-Web端根路径托管前端.md b/docs/project/研发工单-v1.3.0/06-Web端根路径托管前端.md new file mode 100644 index 0000000..618ab26 --- /dev/null +++ b/docs/project/研发工单-v1.3.0/06-Web端根路径托管前端.md @@ -0,0 +1,89 @@ +# 研发工单 v1.3.0 - 06:Web 端根路径托管前端 + +> 术语澄清:本文件中"研发工单"指智能体/开发者执行的**编码工作项**,与业务侧的 Todo 待办项无关。详见 [.trae/rules/全局/04-研发工单全流程规范.md](../../../.trae/rules/全局/04-研发工单全流程规范.md)。 + +--- + +## 一、背景与动机 + +根据 [01-项目架构.md](../../../.trae/rules/项目/01-项目架构.md) §3「模式 B:Web 端」端点划分表的定义: + +| URL 前缀 | 内容 | 来源 | +|---|---|---| +| `/`(根路径) | **Hua.Todo.Web 用户前端**(登录后 Todo 列表) | `src/Hua.Todo.Web` 的 `dist/` 构建产物,复制到 `Hua.Todo.Host/wwwroot/` | +| `/api/*` | 动态 API | DynamicApi | +| `/admin/*` | 管理后台前端 | `Hua.Todo.Host/wwwroot/admin/`(占位) | + +**当前代码不符合**: + +- Host `Program.cs` 没有 `UseStaticFiles()` / `UseDefaultFiles()`;访问 `http://localhost:5173/` 返回 **404** +- `Hua.Todo.Host.csproj` 没有任何构建 Target 把 `Hua.Todo.Web` 的 `dist/*` 产物拷到 `Hua.Todo.Host/wwwroot/` +- 开发联调文档缺失:开发时 Vite(:5174)+ Host(:5173)双开怎么跑、端口怎么代理、生产部署怎么构建单文件,都没有书面说明 + +结果:**用户按规则部署"Web 端(Hua.Todo.Host)",会拿到一个只有 API 没有前端页面的半成品服务**。本工单补全模式 B 的生产/开发闭环。 + +## 二、目标 / 范围 + +### 2.1 做什么 + +1. **Host 侧 ASP.NET Core 中间件补齐**:在 `Program.cs` DynamicApi 之前插入 `UseDefaultFiles()` + `UseStaticFiles()`,并保证 SPA 路由(前端内部路由,如 `/settings`)fallback 到 `index.html`(用 `MapFallbackToFile("index.html")`) +2. **Host csproj 构建 Target**:新增 `npm install`(若 `node_modules` 不存在)+ `npm run build`(Hua.Todo.Web)+ 复制 `dist/**` → `$(OutputPath)/wwwroot/` 的 MSBuild Target,只在构建 Host 时触发(确保 `dotnet publish Hua.Todo.Host` 产出含前端的完整包) +3. **开发环境联调说明**:在 `docs/manual/08-构建与开发指南.md`(或现有的开发指南对应章节)追加 "模式 B(Web 端)开发双开步骤" 与 "生产发布步骤" 小节 +4. **验收测试**:新增 1 个 Host 集成测试(`WebRootStaticFileTests`),验证 HTTP GET `/` 返回 200 且 body 含 ``/`<div id="app">` 特征 + +### 2.2 不做什么 + +- **不实现 `/admin/*` 管理后台前端**(只有 index.html 占位,v1.2.0 工单 06.1 规划) +- **不改嵌入式模式 MAUI/Avalonia 的前端托管逻辑**(那边是 `EmbeddedWebServerService` 内自己 UseWebRoot + 复制 wwwroot,与 Host 部署模式解耦) +- **不改 Hua.Todo.Web 构建逻辑本身**(只调用现成 `npm run build`;不修改 vite.config.ts build.outDir) + +## 三、前置条件 / 依赖 + +- v1.3.0 工单 05 缺口 D:`CloudSyncEndpointExtensions.cs` 的还原落地(因为本工单需要在 Program.cs 末尾追加 `MapFallbackToFile`,和 `MapCloudSyncProxySettings()` 同时存在于 `Program.cs` 尾部,改动顺序不敏感但建议先落 05 的 D) +- Node.js + npm 在构建机器可用(作为前置条件,Host csproj Target 不负责安装 Node) +- 共享文件:`Hua.Todo.Host/Program.cs`、`Hua.Todo.Host/Hua.Todo.Host.csproj`、`docs/manual/08-构建与开发指南.md` +- 可与 v1.3.0 工单 03、05 并行 + +## 四、验收标准(Given-When-Then) + +| # | 场景 | GIVEN | WHEN | THEN | +|---|---|---|---|---| +| 1 | 根路径返回前端 HTML(模式 B) | Host 已 `dotnet build`,且 `wwwroot/index.html` 有内容 | HTTP GET `http://localhost:5173/` | 返回 200;`Content-Type: text/html`;body 含 `<div id="app">`(来自 Hua.Todo.Web 模板/index.html) | +| 2 | SPA 路由 fallback | 同 1 | HTTP GET `http://localhost:5173/settings`(前端内部路由) | 返回 200;body 是 `index.html` 全文,**不是** 404 页 | +| 3 | API 路由不被 fallback 覆盖 | 同 1 | HTTP POST `/api/cloud-sync/probe` | DynamicApi 正常处理(200 / 503 都行,但不是 index.html) | +| 4 | MSBuild Target:`dotnet publish Hua.Todo.Host` | 干净克隆(删 node_modules 和 dist),Node 已装 | 执行 `dotnet publish src/Hua.Todo.Host -c Release -o ./publish-host` | `publish-host/wwwroot/` 下有 `index.html`、`assets/` 等 Hua.Todo.Web build 产物;且 publish-host 启动后 `GET /` 满足验收 1 | +| 5 | MSBuild Target 增量构建(不重复 npm run build) | Hua.Todo.Web src 文件未变动,已执行过一次 build | 再次执行 `dotnet build src/Hua.Todo.Host` | npm build 阶段跳过后直接 copy(Inputs/Outputs 正确);日志出现 "Skipping target BuildHuaTodoWeb"(或等价跳过提示) | +| 6 | 开发联调(文档可执行) | 新环境 clone 后 Node 已装、.NET 10 SDK 已装 | 按 docs/manual 新增章节的步骤执行(两条命令:1. Vite dev;2. Host run) | Vite :5174 页面打开、API 请求能到 Host :5173;云同步探测能返回 `isReachable` JSON(与 307 修复后的链路一致) | + +## 五、并行冲突规避 + +| Touch List | 共享文件策略 | 编译绿线策略 | +|---|---|---| +| `Hua.Todo.Host/Program.cs` | 只在 `app.UseDynamicApi();` **之前**插入 `UseDefaultFiles/UseStaticFiles`;在 `app.Run()` **之前**追加 `MapFallbackToFile("index.html")`(若已存在 `/api/cloud-sync/proxy-settings` endpoint 则不改动) | `dotnet build Hua.Todo.Host.csproj` 0 错误 | +| `Hua.Todo.Host/Hua.Todo.Host.csproj` | 追加 `<Target Name="BuildHuaTodoWebWebRoot" BeforeTargets="BeforeBuild" ...>` 节点;**不修改任何现有 PropertyGroup/ItemGroup/Target** | 同编译 + `dotnet publish` 产出验证 | +| `docs/manual/08-构建与开发指南.md` | 末尾追加两个新小节标题,不改原有章节顺序 | 纯 Markdown | +| `test/Hua.Todo.Host.Tests/...` | 新建 `WebRootStaticFileTests.cs`(独立文件),不修改既有测试类 | `dotnet test Hua.Todo.Host.Tests` 新增 + 既有全部通过 | + +## 六、风险与回滚 + +| 风险 | 影响 | 应对策略 | +|---|---|---| +| `npm install` 时长慢(首次)+ 网络差导致构建失败 | CI publish 超时 | MSBuild Target 加 `ContinueOnError="WarnAndContinue"` + 清晰错误日志:"Hua.Todo.Web build skipped,deploy wwwroot 是空的——请手动构建 Hua.Todo.Web 后重试" | +| `MapFallbackToFile` 顺序放错,覆盖掉 `/api/*` 或 `/swagger/*` | API / Swagger 404 | 在 Program.cs 写明确顺序:`UseSwagger → UseStaticFiles → UseAuthentication/Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile`;并在集成测试里同时验证 fallback 和 API 都正常 | +| 增量构建 Inputs/Outputs 写粗了导致漏 build(或反复 build) | 前端代码变更后不生效 / 构建变慢 | Inputs 精确到 `Hua.Todo.Web/package.json` + `Hua.Todo.Web/src/**/*.{ts,vue,css,html}`;Outputs 精确到 `wwwroot/index.html` | +| CI 环境 Node 不存在 | 构建报错 | Target 检测不到 `node`/`npm` 时仅 Warning,不阻塞 Host API 层 build(生产环境可由外部 CI 先 build 前端再打包) | + +## 七、与其他工单的语音/UI 入口衔接 + +> 按 [05-多入口功能同步规范.md](../../../.trae/rules/项目/05-多入口功能同步规范.md) 第 3.2 条:纯基础设施调整无需检查双入口。 + +| 入口 | 已规划 | 方案 | +|---|---|---| +| UI | ✅(本次就是 UI 托管入口补齐) | 模式 B `http://localhost:5173/` 直接返回 Hua.Todo.Web | +| 语音 | ❌(无需) | 不涉及业务操作 | + +--- + +**创建日期**:2026-08-08 +**关联规则**:[01-项目架构.md §3 模式 B](../../../.trae/rules/项目/01-项目架构.md) +**关联工单**:v1.3.0 工单 05;v1.2.0 工单 06.1(Admin 管理后台前端) diff --git a/src/Hua.Todo.Application/Services/DataSeeder/AdminBootstrapOptions.cs b/src/Hua.Todo.Application/Services/DataSeeder/AdminBootstrapOptions.cs new file mode 100644 index 0000000..2e86ae3 --- /dev/null +++ b/src/Hua.Todo.Application/Services/DataSeeder/AdminBootstrapOptions.cs @@ -0,0 +1,27 @@ +namespace Hua.Todo.Application.Services.DataSeeder; + +/// <summary> +/// 默认管理员初始化配置,对应 appsettings.json 的 "AdminBootstrap" 节。 +/// </summary> +/// <remarks> +/// 用于 <see cref="IDataSeeder"/> 在数据库首次初始化时创建默认管理员账号。 +/// 生产环境强烈建议在配置中覆盖 <see cref="Password"/>。 +/// </remarks> +public class AdminBootstrapOptions +{ + /// <summary> + /// 是否启用数据库初始化时自动创建管理员。默认 <c>true</c>。 + /// </summary> + public bool Enabled { get; set; } = true; + + /// <summary> + /// 默认管理员用户名。默认 <c>"admin"</c>。 + /// </summary> + public string UserName { get; set; } = "admin"; + + /// <summary> + /// 默认管理员密码。留空时使用内置默认值 <c>"Admin@123"</c>。 + /// 生产环境强烈建议在配置中覆盖。 + /// </summary> + public string Password { get; set; } = "Admin@123"; +} diff --git a/src/Hua.Todo.Application/Services/DataSeeder/DataSeeder.cs b/src/Hua.Todo.Application/Services/DataSeeder/DataSeeder.cs new file mode 100644 index 0000000..22f7318 --- /dev/null +++ b/src/Hua.Todo.Application/Services/DataSeeder/DataSeeder.cs @@ -0,0 +1,46 @@ +using Hua.Todo.Application.Repositories; +using Microsoft.AspNetCore.Identity; +using Hua.Todo.Core.Entities; + +namespace Hua.Todo.Application.Services.DataSeeder; + +/// <summary> +/// 数据库种子数据初始化器实现。 +/// </summary> +/// <remarks> +/// 当前为 RED 阶段桩:方法未实现,待验收测试就位后补全逻辑。 +/// 仅在 <c>Hua.Todo.Host</c> 服务端模式注册;嵌入式模式不调用 <c>AddCloudSyncServer()</c>, +/// 故此类型不会在 MAUI/Avalonia 解析。 +/// </remarks> +public class DataSeeder : IDataSeeder +{ + private readonly TodoDbContext _dbContext; + private readonly IPasswordHasher<UserEntity> _passwordHasher; + private readonly AdminBootstrapOptions _options; + + /// <summary> + /// 创建 <see cref="DataSeeder"/>。 + /// </summary> + /// <param name="dbContext">数据库上下文。</param> + /// <param name="passwordHasher">密码哈希器(由 <c>AddCloudSyncServer</c> 注册)。</param> + /// <param name="options">默认管理员初始化配置。</param> + public DataSeeder( + TodoDbContext dbContext, + IPasswordHasher<UserEntity> passwordHasher, + AdminBootstrapOptions options) + { + _dbContext = dbContext; + _passwordHasher = passwordHasher; + _options = options; + } + + /// <summary> + /// 执行种子数据初始化。若默认管理员已存在则跳过(幂等)。 + /// </summary> + /// <param name="cancellationToken">取消令牌。</param> + public Task SeedAsync(CancellationToken cancellationToken = default) + { + // RED 阶段:尚未实现,验收测试预期失败。 + throw new NotImplementedException("DataSeeder.SeedAsync 尚未实现"); + } +} diff --git a/src/Hua.Todo.Application/Services/DataSeeder/IDataSeeder.cs b/src/Hua.Todo.Application/Services/DataSeeder/IDataSeeder.cs new file mode 100644 index 0000000..4421cc8 --- /dev/null +++ b/src/Hua.Todo.Application/Services/DataSeeder/IDataSeeder.cs @@ -0,0 +1,18 @@ +namespace Hua.Todo.Application.Services.DataSeeder; + +/// <summary> +/// 数据库种子数据初始化器,在 EF Core Migrate 完成后执行,幂等写入默认数据。 +/// </summary> +/// <remarks> +/// 仅在 <c>Hua.Todo.Host</c> 服务端模式注册与调用;嵌入式模式(MAUI/Avalonia)使用 +/// <c>TodoUserIds.LocalUserId</c>,不暴露云同步端点,无需管理员账号。 +/// </remarks> +public interface IDataSeeder +{ + /// <summary> + /// 执行种子数据初始化。若默认管理员已存在则跳过(幂等)。 + /// </summary> + /// <param name="cancellationToken">取消令牌。</param> + /// <returns>表示异步操作的任务。</returns> + Task SeedAsync(CancellationToken cancellationToken = default); +} diff --git a/src/Hua.Todo.Application/Services/TaskService.cs b/src/Hua.Todo.Application/Services/TaskService.cs index f6ca307..150f53d 100644 --- a/src/Hua.Todo.Application/Services/TaskService.cs +++ b/src/Hua.Todo.Application/Services/TaskService.cs @@ -7,31 +7,42 @@ namespace Hua.Todo.Application.Services; /// <summary> /// 任务管理服务实现。 +/// 同时在 Web Host(多用户,<see cref="ICurrentUserAccessor"/> 提供登录用户 ID) +/// 和嵌入式(单用户,本地 <see cref="TodoUserIds.LocalUserId"/>)下运行。 /// </summary> public class TaskService : ITaskService { private readonly ITaskRepository _taskRepository; private readonly IAttachmentRepository? _attachmentRepository; + private readonly ICurrentUserAccessor? _currentUserAccessor; /// <summary> /// 初始化任务管理服务的新实例。 /// </summary> /// <param name="taskRepository">任务仓储接口。</param> /// <param name="attachmentRepository">附件仓储接口(可选,静态服务端可能未注册)。</param> - public TaskService(ITaskRepository taskRepository, IAttachmentRepository? attachmentRepository = null) + /// <param name="currentUserAccessor">当前用户访问器(Web Host 注入;嵌入式为 null 时回退 LocalUserId)。</param> + public TaskService( + ITaskRepository taskRepository, + IAttachmentRepository? attachmentRepository = null, + ICurrentUserAccessor? currentUserAccessor = null) { _taskRepository = taskRepository; _attachmentRepository = attachmentRepository; + _currentUserAccessor = currentUserAccessor; } + private Guid EffectiveUserId => _currentUserAccessor?.GetCurrentUserId() ?? TodoUserIds.LocalUserId; + /// <summary> /// 获取所有任务 /// </summary> /// <returns>所有任务的 DTO 列表</returns> public async Task<List<TaskDto>> GetAllTasksAsync() { + var userId = EffectiveUserId; var tasks = await _taskRepository.GetAllAsync(); - return tasks.Select(MapToDto).ToList(); + return tasks.Where(t => t.UserId == userId).Select(MapToDto).ToList(); } /// <summary> @@ -51,8 +62,9 @@ public class TaskService : ITaskService /// <returns>未完成任务的 DTO 列表</returns> public async Task<List<TaskDto>> GetActiveTasksAsync() { + var userId = EffectiveUserId; var allTasks = await _taskRepository.GetAllAsync(); - return allTasks.Where(t => !t.IsCompleted).Select(MapToDto).ToList(); + return allTasks.Where(t => t.UserId == userId && !t.IsCompleted).Select(MapToDto).ToList(); } /// <summary> @@ -61,8 +73,9 @@ public class TaskService : ITaskService /// <returns>已完成任务的 DTO 列表</returns> public async Task<List<TaskDto>> GetCompletedTasksAsync() { + var userId = EffectiveUserId; var allTasks = await _taskRepository.GetAllAsync(); - return allTasks.Where(t => t.IsCompleted).Select(MapToDto).ToList(); + return allTasks.Where(t => t.UserId == userId && t.IsCompleted).Select(MapToDto).ToList(); } /// <summary> @@ -72,11 +85,12 @@ public class TaskService : ITaskService /// <returns>新创建的任务 DTO</returns> public async Task<TaskDto> CreateTaskAsync(CreateTaskDto dto) { - var maxCode = await _taskRepository.GetMaxCodeAsync(TodoUserIds.LocalUserId); + var userId = EffectiveUserId; + var maxCode = await _taskRepository.GetMaxCodeAsync(userId); var task = new TaskEntity { Id = Guid.NewGuid(), - UserId = TodoUserIds.LocalUserId, + UserId = userId, Title = dto.Title, Priority = dto.Priority, Code = (maxCode + 1).ToString(), diff --git a/src/Hua.Todo.Host/Hua.Todo.Host.csproj b/src/Hua.Todo.Host/Hua.Todo.Host.csproj index a84ab2d..54dd350 100644 --- a/src/Hua.Todo.Host/Hua.Todo.Host.csproj +++ b/src/Hua.Todo.Host/Hua.Todo.Host.csproj @@ -4,6 +4,16 @@ <TargetFramework>net10.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> + <!-- + BuildHuaTodoWebWebRoot 相关路径: + HuaTodoWebProjectDir 相对于 Host.csproj(当前文件位于 src/Hua.Todo.Host/ → 上一级是 src/ → src/Hua.Todo.Web) + SPA 构建产物复制到 $(MSBuildProjectDirectory)/wwwroot/spa/(与 admin/ 平级) + WebRoot 默认是 $(MSBuildProjectDirectory)/wwwroot;构建 npm 输出目录由 vite.config.ts build.outDir 指定为 Hua.Todo.Web/dist + --> + <HuaTodoWebProjectDir>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/../Hua.Todo.Web'))</HuaTodoWebProjectDir> + <HuaTodoWebOutputDir>$(HuaTodoWebProjectDir)/dist</HuaTodoWebOutputDir> + <HuaTodoWebSrcDir>$(HuaTodoWebProjectDir)/src</HuaTodoWebSrcDir> + <HuaTodoWebPkgJson>$(HuaTodoWebProjectDir)/package.json</HuaTodoWebPkgJson> </PropertyGroup> <ItemGroup> @@ -24,4 +34,81 @@ <ProjectReference Include="..\Hua.Todo.HttpApi.AspNetCore\Hua.Todo.HttpApi.AspNetCore.csproj" /> </ItemGroup> + <!-- + Target: BuildHuaTodoWebWebRoot + BeforeTargets="BeforeBuild" → 在每次 dotnet build/publish Hua.Todo.Host 前执行 + Inputs/Outputs → 支持增量构建:package.json + src/**/*.{ts,vue,css,html} 不变时跳过 npm build + Node/npm 不存在 → 只 Warning,不阻塞 API 层 build(外部 CI 可先 build 前端) + + 步骤: + 1) Exec 检测 node -v / npm -v 是否可用 + 2) 若 node_modules 不存在:npm ci(优先,依赖锁定)或 npm install + 3) npm run build → Hua.Todo.Web/dist + 4) Copy $(HuaTodoWebOutputDir)/** → $(MSBuildProjectDirectory)/wwwroot/spa/ 和 $(OutputPath)/wwwroot/spa/ 保留目录结构 + (与 wwwroot/admin/ 平级;本地 dotnet run 用项目 wwwroot/spa,dotnet publish 用 OutputPath/wwwroot/spa) + --> + <Target Name="BuildHuaTodoWebWebRoot" + BeforeTargets="BeforeBuild" + Inputs="$(HuaTodoWebPkgJson);$(HuaTodoWebSrcDir)**/*.ts;$(HuaTodoWebSrcDir)**/*.vue;$(HuaTodoWebSrcDir)**/*.css;$(HuaTodoWebSrcDir)**/*.html" + Outputs="$(MSBuildProjectDirectory)/wwwroot/spa/index.html;$(OutputPath)/wwwroot/spa/index.html" + Condition="'$(SkipBuildHuaTodoWebWebRoot)' != 'true'"> + + <!-- 1. 检测 Node/npm 是否可用(CI 常缺) --> + <Exec Command="node -v" ConsoleToMsBuild="true" IgnoreExitCode="true" ContinueOnError="WarnAndContinue"> + <Output TaskParameter="ExitCode" PropertyName="NodeExitCode" /> + </Exec> + <Exec Command="npm -v" ConsoleToMsBuild="true" IgnoreExitCode="true" ContinueOnError="WarnAndContinue"> + <Output TaskParameter="ExitCode" PropertyName="NpmExitCode" /> + </Exec> + + <Warning Condition="'$(NodeExitCode)' != '0' Or '$(NpmExitCode)' != '0'" + Text="[BuildHuaTodoWebWebRoot] 未检测到 Node/npm,跳过前端构建。 + Host wwwroot/spa 将不包含 Hua.Todo.Web 静态页面,HTTP GET / 会返回 404。 + 请安装 Node.js(>= 18)或设置 SkipBuildHuaTodoWebWebRoot=true 抑制本警告。 + CI 可在外层先执行:cd src/Hua.Todo.Web && npm ci && npm run build。" /> + + <!-- 2. npm install(仅当 node_modules 不存在时) --> + <Exec Condition="'$(NodeExitCode)' == '0' And '$(NpmExitCode)' == '0' And !Exists('$(HuaTodoWebProjectDir)node_modules')" + Command="npm install" + WorkingDirectory="$(HuaTodoWebProjectDir)" + ConsoleToMsBuild="true" + ContinueOnError="WarnAndContinue"> + <Output TaskParameter="ExitCode" PropertyName="NpmInstallExitCode" /> + </Exec> + + <!-- 3. npm run build(vue-tsc -b && vite build) --> + <Exec Condition="'$(NodeExitCode)' == '0' And '$(NpmExitCode)' == '0'" + Command="npm run build" + WorkingDirectory="$(HuaTodoWebProjectDir)" + ConsoleToMsBuild="true" + ContinueOnError="WarnAndContinue"> + <Output TaskParameter="ExitCode" PropertyName="NpmBuildExitCode" /> + </Exec> + + <Warning Condition="'$(NodeExitCode)' == '0' And '$(NpmExitCode)' == '0' And '$(NpmBuildExitCode)' != '0'" + Text="[BuildHuaTodoWebWebRoot] npm run build 退出码 $(NpmBuildExitCode)。 + 请在 src/Hua.Todo.Web 目录下手动 npm run build 排查 TypeScript/Vite 错误。" /> + + <!-- 4. 展开 dist 目录为 ItemGroup(一次,两步 Copy 共用) --> + <ItemGroup Condition="'$(NodeExitCode)' == '0' And '$(NpmExitCode)' == '0' And Exists('$(HuaTodoWebOutputDir)/index.html')"> + <_WebDist Include="$(HuaTodoWebOutputDir)/**/*" /> + </ItemGroup> + + <!-- 4a. 拷贝到 Host 项目 wwwroot/spa(本地 dotnet run 用) --> + <Copy Condition="'@(_WebDist)' != ''" + SourceFiles="@(_WebDist)" + DestinationFiles="@(_WebDist->'$(MSBuildProjectDirectory)/wwwroot/spa/%(RecursiveDir)%(Filename)%(Extension)')" + SkipUnchangedFiles="true" /> + + <!-- 4b. 拷贝到 $(OutputPath)/wwwroot/spa(dotnet build/publish 打包输出用) --> + <Copy Condition="'@(_WebDist)' != ''" + SourceFiles="@(_WebDist)" + DestinationFiles="@(_WebDist->'$(OutputPath)/wwwroot/spa/%(RecursiveDir)%(Filename)%(Extension)')" + SkipUnchangedFiles="true" /> + + <Message Condition="Exists('$(MSBuildProjectDirectory)/wwwroot/spa/index.html')" + Importance="High" + Text="[BuildHuaTodoWebWebRoot] 前端静态页已复制到 Host wwwroot/spa/(增量构建命中:Inputs 未变化时本 Target 整体跳过)。" /> + </Target> + </Project> diff --git a/src/Hua.Todo.Host/Program.cs b/src/Hua.Todo.Host/Program.cs index c62f75a..8c044ca 100644 --- a/src/Hua.Todo.Host/Program.cs +++ b/src/Hua.Todo.Host/Program.cs @@ -1,3 +1,5 @@ +using System.Reflection; +using System.Text; using System.Text.Json; using Microsoft.EntityFrameworkCore; using Serilog; @@ -5,7 +7,7 @@ using Hua.Todo.Application.Common; using Hua.Todo.Application.Models; using Hua.Todo.Application.Repositories; using Hua.Todo.HttpApi; -using Hua.Todo.HttpApi.AspNetCore.CloudSync.Services; +using Hua.Todo.HttpApi.AspNetCore.CloudSync; using Hua.Todo.HttpApi.AspNetCore.DynamicApi; using Hua.Todo.HttpApi.AspNetCore.DynamicApi.Swagger; using Hua.Todo.HttpApi.AspNetCore.Mcp; @@ -16,6 +18,11 @@ Log.Logger = LoggingConfiguration.Create( Log.Information("[Startup] Serilog 初始化完成"); var builder = WebApplication.CreateBuilder(args); +// 显式指定开发环境监听端口:HTTP 5173(与前端云同步默认服务器地址对齐),HTTPS 7055 +// 开发环境默认端口 5000/5001 与前端配置不一致,导致探测失败 +builder.WebHost.UseUrls( + "http://0.0.0.0:5173", + "https://0.0.0.0:7055"); builder.Host.UseSerilog(); Log.Information("[Startup] WebApplication.CreateBuilder 完成"); @@ -29,7 +36,10 @@ Log.Information("[Startup] Swagger 注册完成"); builder.Services.AddApplicationServices("Data Source=Hua.Todo.db"); Log.Information("[Startup] AddApplicationServices 完成"); -// 注册云同步服务端能力(认证 / RBAC / 二次认证 / 任务同步 / 安全策略) +// Host 双角色统一部署(不区分「模式 A / 模式 B」): +// 角色 1 - Web 端:用户浏览器直接访问 Host → 同域下承载前端静态页、Dynamic API、Swagger、MCP +// 角色 2 - 云同步服务端:MAUI / Avalonia 嵌入式客户端通过 CloudSyncProxy 转发到 Host /api/* +// 因此必须同时注册:AddApplicationServices + AddCloudSyncServer + AddMcpServerServices builder.Services.AddCloudSyncServer(); Log.Information("[Startup] AddCloudSyncServer 完成"); @@ -60,13 +70,47 @@ using (var scope = app.Services.CreateScope()) Log.Information("[Startup] EF Core Migrate 完成"); } +// === Host 中间件顺序严格对齐 v1.3.0 00-工单总览 §5 硬约束: +// Swagger → MinimalBootstrapPage(仅当 wwwroot/index.html 不存在时生效) +// → UseDefaultFiles/UseStaticFiles → CORS → Authentication +// → Authorization → UseDynamicApi → MapMcpServer → MapFallbackToFile → Run +// 顺序错误会导致 /api/* 或 /swagger/* 被 MapFallbackToFile 覆盖成返回 index.html。 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } -app.UseHttpsRedirection(); +// 前端构建缺失兜底:若 wwwroot/spa/index.html 不存在,则在 MapFallbackToFile 之前的 404 路径上 +// 返回一个最小引导页(中文错误提示 + 构建命令 + 文档链接),不让用户看到空白 404。 +// 前端构建成功后此中间件不生效(短路判断:文件存在直接 _next)。 +app.UseMiddleware<MinimalBootstrapPageMiddleware>(); + +// 静态文件(分层,按优先级从高到低): +// 1) UseDefaultFiles + UseStaticFiles(wwwroot 根)→ 服务 admin/ 等非 SPA 静态文件 +// 2) UseDefaultFiles + UseStaticFiles(spa provider,映射到 /)→ 服务 SPA 的 assets/favicon 等 +// 放在第 2 层是因为 admin 等目录不希望在 SPA provider 下被覆盖 +// 3) MapFallbackToFile → SPA 路由(/settings 等)及 admin 路由(/admin/xxx) +// 注意:放在 UseDynamicApi / MapMcpServer 之后, +// 否则会先匹配返回 index.html,/api/* /mcp 永远走不到。 +app.UseDefaultFiles(); +app.UseStaticFiles(); + +// SPA 静态资源(wwwroot/spa/ 映射到根 /) +// 浏览器直访 /favicon.svg → 命中 wwwroot/spa/favicon.svg +// 未命中则 fallback 到下面的 MapFallbackToFile +var spaRoot = Path.Combine(app.Environment.WebRootPath, "spa"); +app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(spaRoot) }); +app.UseStaticFiles(new StaticFileOptions { FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(spaRoot) }); + +// 开发环境跳过 HTTPS 重定向: +// 1) 避免前端 axios 把 307 当错误直接 toast; +// 2) DynamicApi 的匿名端点(/probe、/login、/bootstrap)在未登录场景使用 HTTP 即可; +// 生产部署推荐由反向代理(Nginx/Caddy)统一处理 TLS 重定向,ASP.NET Core 不必二次重定向。 +if (!app.Environment.IsDevelopment()) +{ + app.UseHttpsRedirection(); +} app.UseCors("AllowAll"); app.UseAuthentication(); app.UseAuthorization(); @@ -75,4 +119,189 @@ app.UseDynamicApi(); // 映射 MCP Server 端点(Streamable HTTP,路径 /mcp) app.MapMcpServer(); +// Admin 后台 fallback:/admin 或 /admin/xxx 返回 wwwroot/admin/index.html +app.MapFallbackToFile("/admin/{**rest}", "admin/index.html"); + +// SPA 路由 fallback(放在最后,优先级最低,不覆盖 /api/* /mcp /swagger /admin) +// 注意:若 wwwroot/spa/index.html 不存在,MinimalBootstrapPageMiddleware 已先返回引导页,这里不会触发 404。 +app.MapFallbackToFile("spa/index.html"); + app.Run(); + +/// <summary> +/// 前端静态页缺失时的最小引导页中间件。 +/// +/// 触发条件:Host 启动时,WebRoot 下找不到 index.html(BuildHuaTodoWebWebRoot MSBuild Target 未执行、 +/// Node 缺失、手动跳过构建等)。 +/// 作用:拦截所有"非 /api/*、非 /swagger、非 mcp"的 GET 请求,如果最终命中到 404(因为 +/// UseStaticFiles / MapFallbackToFile 都找不到文件),则返回一个最小的引导 HTML 页面, +/// 让用户一眼看出是"前端没构建"而不是"部署挂了 / 404"。 +/// </summary> +internal sealed class MinimalBootstrapPageMiddleware +{ + private readonly RequestDelegate _next; + private readonly IWebHostEnvironment _env; + private readonly ILogger<MinimalBootstrapPageMiddleware> _logger; + private readonly string _indexHtmlPath; + private readonly Lazy<bool> _indexHtmlExists; + + public MinimalBootstrapPageMiddleware( + RequestDelegate next, + IWebHostEnvironment env, + ILogger<MinimalBootstrapPageMiddleware> logger) + { + _next = next; + _env = env; + _logger = logger; + _indexHtmlPath = Path.Combine(env.WebRootPath, "spa", "index.html"); + // 启动时只判断一次文件是否存在,避免每次请求都 IO + _indexHtmlExists = new Lazy<bool>(() => + { + var exists = File.Exists(_indexHtmlPath); + if (!exists) + { + logger.LogWarning( + "[MinimalBootstrapPage] 未找到前端入口文件 {IndexHtmlPath},将启用最小引导页。" + + "请执行:cd src/Hua.Todo.Web && npm install && npm run build ;" + + "或重新 build Host:dotnet build src/Hua.Todo.Host(会自动触发 BuildHuaTodoWebWebRoot Target)。" + + "CI 可设置 -p:SkipBuildHuaTodoWebWebRoot=true 并在外层 build 前端后拷贝 wwwroot/spa。", + _indexHtmlPath); + } + return exists; + }); + } + + public async Task InvokeAsync(HttpContext context) + { + await _next(context); + + // 只处理:status 404 + 非 /api/* + 非 /swagger/* + 非 /mcp + 非 /admin/* + // + GET 或 HEAD + Accept 包含 text/html(浏览器直接访问页面,不是 XHR/Fetch/API) + if (_indexHtmlExists.Value) return; + + if (context.Response.StatusCode != StatusCodes.Status404NotFound) return; + + var method = context.Request.Method; + if (!HttpMethods.IsGet(method) && !HttpMethods.IsHead(method)) return; + + var path = context.Request.Path.Value ?? string.Empty; + if (path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("/swagger", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("/mcp", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("/admin/", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + var accept = context.Request.Headers["Accept"].ToString(); + if (string.IsNullOrEmpty(accept) || + !accept.Contains("text/html", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + // 开始写响应——先重置 status/content-type,再写 body + context.Response.StatusCode = StatusCodes.Status200OK; + context.Response.ContentType = "text/html; charset=utf-8"; + + var appVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0"; + var html = BuildBootstrapHtml(appVersion, _indexHtmlPath, _env.WebRootPath, _env.EnvironmentName); + await context.Response.WriteAsync(html, Encoding.UTF8); + } + + private static string BuildBootstrapHtml(string appVersion, string indexHtmlPath, string webRootPath, string envName) + { + var idx = HtmlEncode(indexHtmlPath); + var wr = HtmlEncode(webRootPath); + var env = HtmlEncode(envName); + var ver = HtmlEncode(appVersion); + const string S = +@"<!doctype html> +<html lang=""zh-CN""> +<head> + <meta charset=""utf-8"" /> + <meta name=""viewport"" content=""width=device-width, initial-scale=1"" /> + <title>Hua.Todo - 前端尚未构建 + + + +
+ 提示:前端尚未构建,仅展示引导页(不影响 API/Swagger/MCP/云同步) +

Hua.Todo Host 已启动,但前端入口文件缺失

+

+ 浏览器访问 / 或 /settings 等 SPA 路由时, + 需要 wwwroot/index.html 才能加载完整的 Todo 前端界面。 + 当前 Host 仍在正常工作中:Dynamic API、Swagger、MCP、云同步服务端均已可用,不受影响。 +

+ +
+

检查项

+
    +
  • 期望路径:__IDX__(当前不存在)
  • +
  • WebRoot:__WR__
  • +
  • 环境:__ENV__ · 版本:__VER__
  • +
+
+ +
+

如何修复(任选一种)

+

方式一:重新 build Host(推荐,会自动触发 BuildHuaTodoWebWebRoot MSBuild Target)

+
cd d:/Proj/6.Hua.Todo
+dotnet build src/Hua.Todo.Host -c Release
+ +

方式二:手动构建前端再 copy

+
cd src/Hua.Todo.Web
+npm install
+npm run build
+# dist/**/* 自动被 Target 拷贝到:
+#   1) src/Hua.Todo.Host/wwwroot/     (本地 dotnet run 用)
+#   2) bin/Release/net10.0/wwwroot/   (dotnet publish 用)
+ +

方式三:CI 无 Node 环境 → 外层 build 前端再 publish

+
cd src/Hua.Todo.Web && npm ci && npm run build
+cd ../..
+dotnet publish src/Hua.Todo.Host -c Release `
+  -p:SkipBuildHuaTodoWebWebRoot=true
+  -o ./artifacts/host
+
+ +
+

在等待前端构建期间,你仍可以使用以下入口

+
    +
  • Swagger UI - 接口调试 /swagger/index.html
  • +
  • POST /api/auth/bootstrap(首次初始化管理员账号)
  • +
  • POST /api/auth/login(Web 端登录)
  • +
  • GET /api/cloud-sync-proxy-settings(云同步代理设置)
  • +
  • GET /mcp(MCP Server 端点,Streamable HTTP)
  • +
+
+ +
+ Hua.Todo · Host 双角色统一部署 = Web 端前端/后端 + 云同步服务端 + MCP · 版本 __VER__ +
+
+ +"; + return S.Replace("__IDX__", idx).Replace("__WR__", wr).Replace("__ENV__", env).Replace("__VER__", ver); + } + + private static string HtmlEncode(string? value) => + string.IsNullOrEmpty(value) ? string.Empty : System.Net.WebUtility.HtmlEncode(value); +} diff --git a/src/Hua.Todo.Host/wwwroot/admin/index.html b/src/Hua.Todo.Host/wwwroot/admin/index.html index a247b09..bda6035 100644 --- a/src/Hua.Todo.Host/wwwroot/admin/index.html +++ b/src/Hua.Todo.Host/wwwroot/admin/index.html @@ -189,11 +189,16 @@ const creatingUser = ref(false); const newUser = reactive({ userName: '', password: '', role: 'user' }); - // Axios 拦截器:注入 Token + // Axios 拦截器:注入 Token(与 SPA 共用 sessionStorage['Hua.Todo_cloud_sync_session']) axios.interceptors.request.use(config => { - const token = localStorage.getItem('admin_token'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; + const raw = sessionStorage.getItem('Hua.Todo_cloud_sync_session'); + if (raw) { + try { + const session = JSON.parse(raw); + if (session.accessToken) { + config.headers.Authorization = `Bearer ${session.accessToken}`; + } + } catch (e) {} } return config; }); @@ -211,13 +216,16 @@ ); const checkAuth = () => { - const token = localStorage.getItem('admin_token'); - const user = localStorage.getItem('admin_user'); - if (token && user) { - isLoggedIn.value = true; - currentUser.value = JSON.parse(user); - return true; - } + const raw = sessionStorage.getItem('Hua.Todo_cloud_sync_session'); + if (!raw) return false; + try { + const session = JSON.parse(raw); + if (session.accessToken && session.role === 'admin') { + isLoggedIn.value = true; + currentUser.value = { userName: session.userName || '管理员', role: session.role }; + return true; + } + } catch (e) {} return false; }; @@ -227,18 +235,27 @@ } loggingIn.value = true; try { - const res = await axios.post('/auth/login', loginForm); - const data = res.data; + const res = await axios.post('/api/auth/login', loginForm); + // DynamicApi 响应:{ success, data: LoginResponse, message } + const payload = res.data.data; - if (data.role !== 'admin') { + if (!payload || payload.role !== 'admin') { throw new Error('只有管理员可以访问此后台'); } - localStorage.setItem('admin_token', data.accessToken); - localStorage.setItem('admin_user', JSON.stringify({ userName: loginForm.userName, role: data.role })); + // 与 SPA 共用 sessionStorage['Hua.Todo_cloud_sync_session'] 格式 + const session = { + accessToken: payload.accessToken, + expiresAtUtc: payload.expiresAtUtc, + userId: payload.userId, + role: payload.role, + permissions: payload.permissions || [], + userName: loginForm.userName + }; + sessionStorage.setItem('Hua.Todo_cloud_sync_session', JSON.stringify(session)); isLoggedIn.value = true; - currentUser.value = { userName: loginForm.userName, role: data.role }; + currentUser.value = { userName: loginForm.userName, role: payload.role }; loginForm.password = ''; ElementPlus.ElMessage.success('登录成功'); @@ -251,8 +268,7 @@ }; const handleLogout = () => { - localStorage.removeItem('admin_token'); - localStorage.removeItem('admin_user'); + sessionStorage.removeItem('Hua.Todo_cloud_sync_session'); isLoggedIn.value = false; currentUser.value = { userName: '', role: '' }; }; @@ -261,8 +277,8 @@ if (!isLoggedIn.value) return; loadingUsers.value = true; try { - const res = await axios.get('/admin/users'); - users.value = res.data; + const res = await axios.get('/api/admin/users'); + users.value = res.data.data || []; } catch (e) {} finally { loadingUsers.value = false; } @@ -272,8 +288,8 @@ if (!isLoggedIn.value) return; loadingSessions.value = true; try { - const res = await axios.get('/admin/sessions'); - sessions.value = res.data; + const res = await axios.get('/api/admin/sessions'); + sessions.value = res.data.data || []; } catch (e) {} finally { loadingSessions.value = false; } @@ -283,8 +299,8 @@ if (!isLoggedIn.value) return; loadingLogs.value = true; try { - const res = await axios.get('/admin/audit-logs?count=100'); - logs.value = res.data; + const res = await axios.get('/api/admin/audit-logs?count=100'); + logs.value = res.data.data || []; } catch (e) {} finally { loadingLogs.value = false; } @@ -303,7 +319,7 @@ } creatingUser.value = true; try { - await axios.post('/admin/users', newUser); + await axios.post('/api/admin/users', newUser); ElementPlus.ElMessage.success('创建用户成功'); showCreateUserDialog.value = false; newUser.userName = ''; @@ -323,7 +339,7 @@ inputType: 'password' }); if (newPass) { - await axios.post(`/admin/users/${user.id}/reset-password`, { newPassword: newPass }); + await axios.post(`/api/admin/users/${user.id}/reset-password`, { newPassword: newPass }); ElementPlus.ElMessage.success('重置成功'); } } catch (e) {} @@ -337,7 +353,7 @@ cancelButtonText: '取消', type: 'warning' }); - await axios.delete(`/admin/users/${user.id}`); + await axios.delete(`/api/admin/users/${user.id}`); ElementPlus.ElMessage.success('删除成功'); fetchUsers(); } catch (e) {} @@ -345,7 +361,7 @@ const handleRevokeSession = async (session) => { try { - await axios.delete(`/admin/sessions/${session.id}`); + await axios.delete(`/api/admin/sessions/${session.id}`); ElementPlus.ElMessage.success('已强制下线'); fetchSessions(); } catch (e) {} diff --git a/src/Hua.Todo.Host/wwwroot/spa/assets/index-7aro5nrm.css b/src/Hua.Todo.Host/wwwroot/spa/assets/index-7aro5nrm.css new file mode 100644 index 0000000..c7455f6 --- /dev/null +++ b/src/Hua.Todo.Host/wwwroot/spa/assets/index-7aro5nrm.css @@ -0,0 +1 @@ +:root{--text:#6b6375;--text-h:#08060d;--bg:#fff;--border:#e5e4e7;--code-bg:#f4f3ec;--accent:#aa3bff;--accent-bg:#aa3bff1a;--accent-border:#aa3bff80;--social-bg:#f4f3ec80;--shadow:#0000001a 0 10px 15px -3px, #0000000d 0 4px 6px -2px;--sans:system-ui, "Segoe UI", Roboto, sans-serif;--heading:system-ui, "Segoe UI", Roboto, sans-serif;--mono:ui-monospace, Consolas, monospace;font:18px/145% var(--sans);letter-spacing:.18px;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;color:var(--text);background:var(--bg);font-synthesis:none;text-rendering:optimizelegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}@media (width<=1024px){:root{font-size:16px}}@media (prefers-color-scheme:dark){:root{--text:#9ca3af;--text-h:#f3f4f6;--bg:#16171d;--border:#2e303a;--code-bg:#1f2028;--accent:#c084fc;--accent-bg:#c084fc26;--accent-border:#c084fc80;--social-bg:#2f303a80;--shadow:#0006 0 10px 15px -3px, #00000040 0 4px 6px -2px}#social .button-icon{filter:invert()brightness(2)}}body{margin:0}h1,h2{font-family:var(--heading);color:var(--text-h);font-weight:500}h1{letter-spacing:-1.68px;margin:32px 0;font-size:56px}@media (width<=1024px){h1{margin:20px 0;font-size:36px}}h2{letter-spacing:-.24px;margin:0 0 8px;font-size:24px;line-height:118%}@media (width<=1024px){h2{font-size:20px}}p{margin:0}code,.counter{font-family:var(--mono);color:var(--text-h);border-radius:4px;display:inline-flex}code{background:var(--code-bg);padding:4px 8px;font-size:15px;line-height:135%}.counter{color:var(--accent);background:var(--accent-bg);border:2px solid #0000;border-radius:5px;margin-bottom:24px;padding:5px 10px;font-size:16px;transition:border-color .3s}.counter:hover{border-color:var(--accent-border)}.counter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.hero{position:relative}.hero .base,.hero .framework,.hero .vite{margin:0 auto;inset-inline:0}.hero .base{z-index:0;width:170px;position:relative}.hero .framework,.hero .vite{position:absolute}.hero .framework{z-index:1;height:28px;top:34px;transform:perspective(2000px)rotate(300deg)rotateX(44deg)rotateY(39deg)scale(1.4)}.hero .vite{z-index:0;width:auto;height:26px;top:107px;transform:perspective(2000px)rotate(300deg)rotateX(40deg)rotateY(39deg)scale(.8)}#app{box-sizing:border-box;flex-direction:column;width:100%;max-width:none;min-height:100svh;margin:0;display:flex}#center{flex-direction:column;flex-grow:1;place-content:center;place-items:center;gap:25px;display:flex}@media (width<=1024px){#center{gap:18px;padding:32px 20px 24px}}#next-steps{border-top:1px solid var(--border);text-align:left;display:flex}#next-steps>div{flex:1 1 0;padding:32px}@media (width<=1024px){#next-steps>div{padding:24px 20px}}#next-steps .icon{width:22px;height:22px;margin-bottom:16px}@media (width<=1024px){#next-steps{text-align:center;flex-direction:column}}#docs{border-right:1px solid var(--border)}@media (width<=1024px){#docs{border-right:none;border-bottom:1px solid var(--border)}}#next-steps ul{gap:8px;margin:32px 0 0;padding:0;list-style:none;display:flex}#next-steps ul .logo{height:18px}#next-steps ul a{color:var(--text-h);background:var(--social-bg);border-radius:6px;align-items:center;gap:8px;padding:6px 12px;font-size:16px;text-decoration:none;transition:box-shadow .3s;display:flex}#next-steps ul a:hover{box-shadow:var(--shadow)}#next-steps ul a .button-icon{width:18px;height:18px}@media (width<=1024px){#next-steps ul{flex-wrap:wrap;justify-content:center;margin-top:20px}#next-steps ul li{flex:calc(50% - 8px)}#next-steps ul a{box-sizing:border-box;justify-content:center;width:100%}}#spacer{border-top:1px solid var(--border);height:88px}@media (width<=1024px){#spacer{height:48px}}.ticks{width:100%;position:relative}.ticks:before,.ticks:after{content:"";border:5px solid #0000;position:absolute;top:-4.5px}.ticks:before{border-left-color:var(--border);left:0}.ticks:after{border-right-color:var(--border);right:0}.attachment-section[data-v-5b5fc0e2]{margin-top:4px}.attachment-header[data-v-5b5fc0e2]{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.attachment-title[data-v-5b5fc0e2]{color:#334155;font-size:13px;font-weight:600}.attachment-actions[data-v-5b5fc0e2]{gap:6px;display:flex}.btn-attach[data-v-5b5fc0e2],.btn-link[data-v-5b5fc0e2]{color:#374151;cursor:pointer;background:#f9fafb;border:1px solid #d1d5db;border-radius:8px;align-items:center;gap:4px;padding:4px 10px;font-size:12px;font-weight:500;transition:all .2s;display:flex}.btn-attach[data-v-5b5fc0e2]:hover,.btn-link[data-v-5b5fc0e2]:hover{color:#2563eb;background:#eff6ff;border-color:#93c5fd}.attachment-empty[data-v-5b5fc0e2]{color:#9ca3af;padding:8px 0;font-size:12px}.attachment-list[data-v-5b5fc0e2]{flex-direction:column;gap:4px;max-height:160px;display:flex;overflow-y:auto}.attachment-item[data-v-5b5fc0e2]{background:#f8fafc;border:1px solid #e5e7eb;border-radius:8px;align-items:center;gap:8px;padding:6px 8px;transition:border-color .2s;display:flex}.attachment-item[data-v-5b5fc0e2]:hover{border-color:#93c5fd}.attachment-item.is-missing[data-v-5b5fc0e2]{opacity:.5;border-color:#fecaca}.file-icon[data-v-5b5fc0e2]{flex-shrink:0;font-size:16px}.file-info[data-v-5b5fc0e2]{flex-direction:column;flex:1;min-width:0;display:flex}.file-name[data-v-5b5fc0e2]{color:#1e293b;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.file-size[data-v-5b5fc0e2]{color:#9ca3af;font-size:10px}.missing-tag[data-v-5b5fc0e2]{color:#ef4444;font-size:10px}.file-actions[data-v-5b5fc0e2]{flex-shrink:0;gap:2px;display:flex}.btn-file[data-v-5b5fc0e2]{cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:24px;height:24px;transition:all .15s;display:flex}.btn-open[data-v-5b5fc0e2]{color:#6b7280}.btn-open[data-v-5b5fc0e2]:hover{color:#2563eb;background:#dbeafe}.btn-delete[data-v-5b5fc0e2]{color:#9ca3af}.btn-delete[data-v-5b5fc0e2]:hover{color:#dc2626;background:#fee2e2}.link-overlay[data-v-5d086492]{z-index:1100;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.link-dialog[data-v-5d086492]{background:#fff;border-radius:14px;width:92%;max-width:420px;animation:.2s dialogSlideIn-5d086492;overflow:hidden;box-shadow:0 20px 60px #00000040}@keyframes dialogSlideIn-5d086492{0%{opacity:0;transform:translateY(-16px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}.link-header[data-v-5d086492]{border-bottom:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:14px 18px;display:flex}.link-header h4[data-v-5d086492]{color:#111827;margin:0;font-size:15px;font-weight:650}.btn-close[data-v-5d086492]{color:#6b7280;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;font-size:20px;display:flex}.btn-close[data-v-5d086492]:hover{color:#111827;background:#f3f4f6}.link-body[data-v-5d086492]{gap:14px;padding:16px 18px;display:grid}.form-group[data-v-5d086492]{flex-direction:column;gap:4px;display:flex}.form-label[data-v-5d086492]{color:#334155;font-size:13px;font-weight:600}.form-input[data-v-5d086492]{color:#1e293b;background:#f9fafb;border:1.5px solid #d1d5db;border-radius:8px;outline:none;padding:8px 10px;font-size:14px;transition:border-color .2s}.form-input[data-v-5d086492]:focus{background:#fff;border-color:#6366f1;box-shadow:0 0 0 3px #6366f11a}.form-input[data-v-5d086492]::placeholder{color:#9ca3af}.link-footer[data-v-5d086492]{background:#f8fafc;border-top:1px solid #e5e7eb;justify-content:flex-end;gap:8px;padding:12px 18px 16px;display:flex}.btn-cancel[data-v-5d086492]{color:#374151;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:10px;padding:8px 16px;font-size:13px;font-weight:500;transition:background .2s}.btn-cancel[data-v-5d086492]:hover{background:#f3f4f6}.btn-confirm[data-v-5d086492]{color:#fff;cursor:pointer;background:linear-gradient(135deg,#2563eb,#1d4ed8);border:none;border-radius:10px;padding:8px 16px;font-size:13px;font-weight:600;transition:all .2s;box-shadow:0 2px 8px #2563eb4d}.btn-confirm[data-v-5d086492]:hover:enabled{transform:translateY(-1px);box-shadow:0 4px 12px #2563eb66}.btn-confirm[data-v-5d086492]:disabled{opacity:.4;cursor:not-allowed}.dialog-overlay[data-v-7854be41]{z-index:1000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#00000080;justify-content:center;align-items:center;padding:20px 16px;display:flex;position:fixed;inset:0}.dialog-content[data-v-7854be41]{background:#fff;border:1px solid #6366f11f;border-radius:20px;width:min(100%,420px);animation:.22s dialogSlideIn-7854be41;overflow:hidden;box-shadow:0 20px 60px #0000004d}@keyframes dialogSlideIn-7854be41{0%{opacity:0;transform:translateY(-20px)scale(.95)}to{opacity:1;transform:translateY(0)scale(1)}}.dialog-header[data-v-7854be41]{border-bottom:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:12px 14px;display:flex}.dialog-title[data-v-7854be41]{color:#1e293b;margin:0;font-size:16px;font-weight:700;line-height:1.3}.btn-close[data-v-7854be41]{color:#64748b;cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;width:28px;height:28px;font-size:20px;line-height:1;transition:all .2s;display:flex}.btn-close[data-v-7854be41]:hover{color:#334155;background:#f1f5f9}.dialog-body[data-v-7854be41]{gap:14px;padding:14px;display:grid}.form-group[data-v-7854be41]{margin-bottom:0}.form-label[data-v-7854be41]{color:#334155;margin-bottom:6px;font-size:13px;font-weight:600;display:block}.form-input[data-v-7854be41]{color:#1e293b;box-sizing:border-box;background:0 0;border:none;border-bottom:2px solid #e2e8f0;border-radius:0;width:100%;min-height:40px;padding:8px;font-size:14px;transition:all .2s}.form-input[data-v-7854be41]:focus{box-shadow:none;background:0 0;border-bottom-color:#6366f1;outline:none}.form-select[data-v-7854be41]{cursor:pointer;color:#1e293b;box-sizing:border-box;appearance:none;background:#f8fafc url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2364748b'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E") right 10px center/18px no-repeat;border:2px solid #e2e8f0;border-radius:14px;width:100%;min-height:40px;padding:10px 40px 10px 14px;font-size:14px;transition:all .3s}.form-select[data-v-7854be41]:hover{background-color:#f8fafc;border-color:#cbd5e1}.form-select[data-v-7854be41]:focus{background:#fff;border-color:#6366f1;outline:none;box-shadow:0 0 0 4px #6366f11a}.form-select option[data-v-7854be41]{color:#1e293b;background:#fff}.form-select option[value="0"][data-v-7854be41]{color:#22c55e}.form-select option[value="1"][data-v-7854be41]{color:#f59e0b}.form-select option[value="2"][data-v-7854be41]{color:#ef4444}.dialog-footer[data-v-7854be41]{background:#f8fafc;border-top:1px solid #e5e7eb;justify-content:flex-end;gap:8px;padding:12px 16px 16px;display:flex}.btn-cancel[data-v-7854be41]{color:#374151;cursor:pointer;background:#e5e7eb;border:none;border-radius:14px;min-width:76px;padding:8px 18px;font-size:14px;font-weight:600;transition:background .2s}.btn-cancel[data-v-7854be41]:hover{background:#d1d5db}.btn-save[data-v-7854be41]{color:#fff;cursor:pointer;background:linear-gradient(135deg,#2563eb 0%,#1d4ed8 100%);border:none;border-radius:14px;min-width:76px;padding:8px 18px;font-size:14px;font-weight:600;transition:all .3s;box-shadow:0 4px 12px -2px #2563eb66}.btn-save[data-v-7854be41]:hover{transform:translateY(-2px);box-shadow:0 8px 16px -2px #2563eb80}.btn-save[data-v-7854be41]:active{transform:translateY(0)}.form-textarea[data-v-7854be41]{color:#1e293b;resize:vertical;box-sizing:border-box;background:#f9fafb;border:1.5px solid #d1d5db;border-radius:10px;outline:none;width:100%;min-height:80px;padding:10px;font-family:inherit;font-size:14px;line-height:1.5;transition:border-color .2s}.form-textarea[data-v-7854be41]:focus{background:#fff;border-color:#6366f1;box-shadow:0 0 0 3px #6366f11a}.form-textarea[data-v-7854be41]::placeholder{color:#9ca3af}.char-count[data-v-7854be41]{text-align:right;color:#9ca3af;margin-top:2px;font-size:11px;display:block}.hidden-file-input[data-v-7854be41]{display:none}.attach-error[data-v-7854be41]{color:#ef4444;margin-top:4px;font-size:12px}@media (width<=768px){.dialog-content[data-v-7854be41]{border-radius:20px;width:100%}.dialog-header[data-v-7854be41]{padding:12px 14px}.dialog-title[data-v-7854be41]{font-size:16px}.dialog-body[data-v-7854be41]{gap:12px;padding:14px}.dialog-footer[data-v-7854be41]{padding:10px 14px 14px}.btn-cancel[data-v-7854be41],.btn-save[data-v-7854be41]{padding:8px 16px;font-size:14px}}.audio-recorder[data-v-3824a835]{background:#fafafa;border:1px solid #ddd;border-radius:12px;padding:16px}.error-message[data-v-3824a835]{color:#d32f2f;background:#fde8e8;border-radius:8px;margin-bottom:12px;padding:8px 12px;font-size:.9em}.unsupported-message[data-v-3824a835]{color:#666;text-align:center;padding:12px;font-style:italic}.ready-state[data-v-3824a835]{justify-content:center;display:flex}.record-btn[data-v-3824a835]{color:#fff;cursor:pointer;background:#e53935;border:none;border-radius:24px;padding:10px 28px;font-size:1em;transition:background .2s}.record-btn[data-v-3824a835]:hover{background:#c62828}.recording-state[data-v-3824a835]{flex-direction:column;align-items:center;gap:12px;display:flex}.recording-header[data-v-3824a835]{align-items:center;gap:16px;display:flex}.recording-indicator[data-v-3824a835]{color:#e53935;font-weight:700;animation:1.2s infinite pulse-3824a835}@keyframes pulse-3824a835{0%,to{opacity:1}50%{opacity:.4}}.timer[data-v-3824a835]{font-variant-numeric:tabular-nums;color:#333;font-size:1.1em}.waveform[data-v-3824a835]{justify-content:center;align-items:center;gap:4px;width:100%;max-width:240px;height:48px;display:flex}.waveform-bar[data-v-3824a835]{background:#e53935;border-radius:4px;width:8px;animation:.6s ease-in-out infinite alternate wave-3824a835}@keyframes wave-3824a835{0%{height:8px}to{height:40px}}.stop-btn[data-v-3824a835]{color:#fff;cursor:pointer;background:#333;border:none;border-radius:24px;padding:8px 24px;font-size:.95em;transition:background .2s}.stop-btn[data-v-3824a835]:hover{background:#555}.stopped-state[data-v-3824a835]{flex-direction:column;align-items:center;gap:12px;display:flex}.stopped-info[data-v-3824a835]{color:#333;font-size:1em}.stopped-actions[data-v-3824a835]{gap:12px;display:flex}.submit-btn[data-v-3824a835]{color:#fff;cursor:pointer;background:#1976d2;border:none;border-radius:8px;padding:8px 20px;font-size:.95em;transition:background .2s}.submit-btn[data-v-3824a835]:hover:not(:disabled){background:#1565c0}.submit-btn[data-v-3824a835]:disabled{opacity:.6;cursor:not-allowed}.reset-btn[data-v-3824a835]{color:#333;cursor:pointer;background:#f5f5f5;border:1px solid #ccc;border-radius:8px;padding:8px 20px;font-size:.95em;transition:background .2s}.reset-btn[data-v-3824a835]:hover{background:#e0e0e0}.transcribing-state[data-v-3824a835]{justify-content:center;align-items:center;padding:16px;display:flex}.transcribing-text[data-v-3824a835]{color:#1976d2;font-size:.95em}.overlay[data-v-fd688f75]{z-index:1000;background:#00000080;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.dialog[data-v-fd688f75]{background:#fff;border-radius:12px;flex-direction:column;width:92%;max-width:640px;max-height:85vh;display:flex;overflow:hidden;box-shadow:0 20px 60px #0000004d}.dialog-header[data-v-fd688f75]{border-bottom:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.dialog-header h3[data-v-fd688f75]{color:#111827;text-overflow:ellipsis;white-space:nowrap;max-width:80%;margin:0;font-size:16px;font-weight:650;overflow:hidden}.close-button[data-v-fd688f75]{color:#6b7280;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:24px;transition:background .2s;display:flex}.close-button[data-v-fd688f75]:hover{color:#111827;background:#f3f4f6}.loading-state[data-v-fd688f75]{color:#6b7280;flex-direction:column;align-items:center;gap:16px;padding:48px 20px;display:flex}.spinner[data-v-fd688f75]{border:3px solid #e5e7eb;border-top-color:#3b82f6;border-radius:50%;width:36px;height:36px;animation:.8s linear infinite spin-fd688f75}@keyframes spin-fd688f75{to{transform:rotate(360deg)}}.loading-state p[data-v-fd688f75]{margin:0;font-size:14px}.error-state[data-v-fd688f75]{flex-direction:column;align-items:center;gap:16px;padding:48px 20px;display:flex}.error-text[data-v-fd688f75]{color:#ef4444;text-align:center;margin:0;font-size:14px}.suggestions-body[data-v-fd688f75]{flex:1;padding:16px 20px;overflow-y:auto}.hint[data-v-fd688f75]{color:#6b7280;margin:0 0 12px;font-size:13px;line-height:1.5}.suggestions-list[data-v-fd688f75]{flex-direction:column;gap:8px;max-height:380px;margin-bottom:12px;display:flex;overflow-y:auto}.suggestion-item[data-v-fd688f75]{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:12px;transition:border-color .2s}.suggestion-item[data-v-fd688f75]:hover{border-color:#93c5fd}.item-row[data-v-fd688f75]{align-items:center;gap:8px;display:flex}.checkbox-label[data-v-fd688f75]{cursor:pointer;flex:1;align-items:center;gap:8px;min-width:0;display:flex}.checkbox-label input[type=checkbox][data-v-fd688f75]{cursor:pointer;accent-color:#3b82f6;flex-shrink:0;width:18px;height:18px}.title-text[data-v-fd688f75]{color:#111827;text-overflow:ellipsis;white-space:nowrap;-webkit-user-select:none;user-select:none;font-size:14px;overflow:hidden}.title-input[data-v-fd688f75]{background:#fff;border:1px solid #3b82f6;border-radius:6px;outline:none;flex:1;min-width:0;padding:4px 8px;font-size:14px;box-shadow:0 0 0 2px #3b82f626}.priority-select[data-v-fd688f75]{cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:6px;flex-shrink:0;padding:4px 8px;font-size:13px}.priority-select[data-v-fd688f75]:focus{border-color:#3b82f6;outline:none}.remove-btn[data-v-fd688f75]{color:#9ca3af;cursor:pointer;background:0 0;border:none;border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;width:28px;height:28px;padding:4px;font-size:16px;transition:all .2s;display:flex}.remove-btn[data-v-fd688f75]:hover{color:#ef4444;background:#fef2f2}.reason[data-v-fd688f75]{color:#6b7280;margin-top:8px;padding-left:26px;font-size:12px;line-height:1.4}.manual-tag[data-v-fd688f75]{color:#8b5cf6;font-style:italic}.add-btn[data-v-fd688f75]{color:#6b7280;cursor:pointer;background:0 0;border:2px dashed #d1d5db;border-radius:10px;width:100%;padding:10px;font-size:14px;transition:all .2s}.add-btn[data-v-fd688f75]:hover{color:#3b82f6;background:#eff6ff;border-color:#3b82f6}.dialog-footer[data-v-fd688f75]{background:#f9fafb;border-top:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:14px 20px;display:flex}.selected-count[data-v-fd688f75]{color:#6b7280;font-size:13px}.footer-actions[data-v-fd688f75]{gap:10px;display:flex}.btn[data-v-fd688f75]{cursor:pointer;border:none;border-radius:10px;padding:10px 18px;font-size:13px;font-weight:600;transition:all .2s}.btn[data-v-fd688f75]:disabled{opacity:.5;cursor:not-allowed}.btn-primary[data-v-fd688f75]{color:#fff;background:#3b82f6}.btn-primary[data-v-fd688f75]:hover:enabled{background:#2563eb}.btn-secondary[data-v-fd688f75]{color:#374151;background:#fff;border:1px solid #d1d5db}.btn-secondary[data-v-fd688f75]:hover:enabled{background:#f3f4f6;border-color:#9ca3af}.task-item[data-v-4cce1be2]{-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;text-align:left;background:#fff;border:1px solid #e2e8f0e6;border-left-width:8px;border-radius:12px;flex-direction:column;margin-bottom:0;padding:10px 12px;transition:background .2s,box-shadow .2s,transform .2s,border-color .2s;display:flex;position:relative}.task-item.priority-low[data-v-4cce1be2]{background:#d1fae5a6;border-color:#a7f3d0bf #a7f3d0bf #a7f3d0bf #22c55ee6}.task-item.priority-medium[data-v-4cce1be2]{background:#fef3c7b3;border-color:#fde68acc #fde68acc #fde68acc #f59e0be6}.task-item.priority-high[data-v-4cce1be2]{background:#fee2e2b3;border-color:#fecacad9 #fecacad9 #fecacad9 #ef4444e6}.task-item.priority-low[data-v-4cce1be2]:hover{background:#d1fae5d9}.task-item.priority-medium[data-v-4cce1be2]:hover{background:#fef3c7d9}.task-item.priority-high[data-v-4cce1be2]:hover{background:#fee2e2d9}.task-item[data-v-4cce1be2]:hover{box-shadow:0 10px 20px -12px #0f172a40}.task-item.completed[data-v-4cce1be2]{opacity:.6}.task-item.completed .task-title[data-v-4cce1be2]{text-decoration:line-through}.meeting-badge[data-v-4cce1be2]{opacity:.4;pointer-events:none;font-size:14px;position:absolute;top:8px;right:8px}.meeting-actions[data-v-4cce1be2]{border-top:1px solid #0000000f;justify-content:flex-end;gap:4px;margin-top:6px;padding-top:6px;display:flex}.btn-meeting[data-v-4cce1be2]{cursor:pointer;background:#f9fafb;border:1px solid #d1d5db;border-radius:8px;justify-content:center;align-items:center;width:30px;height:30px;padding:0;font-size:14px;transition:all .2s;display:flex}.btn-meeting[data-v-4cce1be2]:hover:enabled{background:#eff6ff;border-color:#93c5fd}.btn-meeting[data-v-4cce1be2]:disabled{opacity:.35;cursor:not-allowed}.notes-editor[data-v-4cce1be2]{background:#ffffffb3;border:1px solid #0000000f;border-radius:10px;margin-top:8px;padding:10px}.notes-textarea[data-v-4cce1be2]{color:#1e293b;resize:vertical;box-sizing:border-box;background:#fff;border:1px solid #d1d5db;border-radius:8px;outline:none;width:100%;min-height:80px;padding:8px;font-family:inherit;font-size:13px;line-height:1.5}.notes-textarea[data-v-4cce1be2]:focus{border-color:#6366f1;box-shadow:0 0 0 2px #6366f11a}.notes-actions[data-v-4cce1be2]{justify-content:space-between;align-items:center;margin-top:6px;display:flex}.audio-info[data-v-4cce1be2]{color:#6b7280;font-size:11px}.btn-save-notes[data-v-4cce1be2]{color:#fff;cursor:pointer;background:linear-gradient(135deg,#2563eb,#1d4ed8);border:none;border-radius:8px;padding:4px 14px;font-size:12px;font-weight:600;transition:all .2s;box-shadow:0 2px 6px #2563eb4d}.btn-save-notes[data-v-4cce1be2]:hover:enabled{transform:translateY(-1px)}.btn-save-notes[data-v-4cce1be2]:disabled{opacity:.5;cursor:not-allowed}.task-desc-preview[data-v-4cce1be2]{color:#6b7280;text-overflow:ellipsis;white-space:nowrap;max-width:100%;margin-top:2px;font-size:11px;line-height:1.3;overflow:hidden}.task-attach-badge[data-v-4cce1be2]{color:#6366f1;flex-shrink:0;font-size:10px;font-weight:500}.task-content[data-v-4cce1be2]{cursor:pointer;-webkit-user-select:none;user-select:none;flex:1;align-items:center;gap:6px;display:flex}.task-content[data-v-4cce1be2]:hover{background:0 0}.task-expand[data-v-4cce1be2]{cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;width:18px;height:18px;padding:0;display:flex}.expand-icon[data-v-4cce1be2]{color:#6b7280;font-size:12px;transition:transform .2s}.task-checkbox[data-v-4cce1be2]{cursor:pointer;appearance:none;background:#ffffffeb;border:1.5px solid #64748b99;border-radius:4px;place-content:center;width:18px;height:18px;transition:background-color .2s,border-color .2s,box-shadow .2s;display:grid}.task-checkbox[data-v-4cce1be2]:after{content:"";opacity:0;border-bottom:2px solid #fff;border-right:2px solid #fff;width:6px;height:10px;transition:opacity .2s;transform:translateY(-1px)rotate(45deg)}.task-checkbox[data-v-4cce1be2]:checked{background:#6366f1;border-color:#6366f1}.task-checkbox[data-v-4cce1be2]:checked:after{opacity:1}.task-checkbox[data-v-4cce1be2]:focus-visible{outline-offset:2px;outline:2px solid #6366f18c}@media (prefers-color-scheme:dark){.task-checkbox[data-v-4cce1be2]{background:#0f172a59;border-color:#94a3b8bf}}.task-info[data-v-4cce1be2]{flex-direction:column;flex:1;align-items:flex-start;gap:4px;min-width:0;display:flex}.task-title[data-v-4cce1be2]{color:#374151;white-space:nowrap;text-overflow:ellipsis;width:100%;font-size:15px;overflow:hidden}.task-meta[data-v-4cce1be2]{align-items:center;gap:8px;width:100%;min-width:0;display:flex}.task-created[data-v-4cce1be2]{color:#475569d9;white-space:nowrap;text-overflow:ellipsis;flex:1;font-size:12px;line-height:1.2;overflow:hidden}.task-id[data-v-4cce1be2]{color:#475569e6;font-variant-numeric:tabular-nums;background:#94a3b824;border-radius:999px;flex-shrink:0;padding:2px 8px;font-size:12px;font-weight:600;line-height:1.2}.task-id--compact[data-v-4cce1be2]{padding:1px 6px;font-size:11px}.task-priority[data-v-4cce1be2]{color:#475569e6;background:#94a3b82e;border-radius:999px;flex-shrink:0;padding:2px 8px;font-size:12px;font-weight:600;line-height:1.2}.task-priority--compact[data-v-4cce1be2]{padding:1px 6px;font-size:11px}.task-item.priority-low .task-priority[data-v-4cce1be2]{color:#15803df2;background:#22c55e24}.task-item.priority-medium .task-priority[data-v-4cce1be2]{color:#b45309f2;background:#f59e0b29}.task-item.priority-high .task-priority[data-v-4cce1be2]{color:#b91c1cf2;background:#ef444424}.task-actions[data-v-4cce1be2]{flex-shrink:0;align-items:center;gap:6px;display:flex}.btn-edit-icon[data-v-4cce1be2]{color:#2563eb;cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex}.btn-edit-icon[data-v-4cce1be2]:hover{color:#1d4ed8;background:#dbeafe}.btn-edit-icon svg[data-v-4cce1be2]{flex-shrink:0}.btn-add-subtask[data-v-4cce1be2]{color:#1e40af;cursor:pointer;background:#dbeafe;border:none;border-radius:3px;align-items:center;gap:4px;padding:4px 10px;font-size:11px;transition:background .2s;display:flex}.btn-add-subtask svg[data-v-4cce1be2]{flex-shrink:0}.btn-add-subtask-icon[data-v-4cce1be2]{color:#6b7280;cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex}.btn-add-subtask-icon[data-v-4cce1be2]:hover{color:#1e40af;background:#dbeafe}.btn-add-subtask-icon svg[data-v-4cce1be2]{flex-shrink:0}.btn-add-subtask[data-v-4cce1be2]:hover{background:#bfdbfe}.btn-delete-icon[data-v-4cce1be2]{color:#dc2626;cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex}.btn-delete-icon[data-v-4cce1be2]:hover{color:#991b1b;background:#fee2e2}.btn-delete-icon svg[data-v-4cce1be2]{flex-shrink:0}.add-subtask-form[data-v-4cce1be2]{background:#ffffff8c;border:1px solid #0000000d;border-radius:12px;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;margin-top:10px;padding:8px 12px;animation:.3s ease-out slideDown-4cce1be2;display:flex}@keyframes slideDown-4cce1be2{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.subtask-input[data-v-4cce1be2]{color:#1e293b;background:0 0;border:none;border-bottom:1.5px solid #00000014;border-radius:0;width:100%;padding:8px;font-size:14px;transition:all .3s}.subtask-input[data-v-4cce1be2]::placeholder{color:#94a3b8}.subtask-input[data-v-4cce1be2]:focus{box-shadow:none;background:0 0;border-bottom-color:#6366f1;outline:none}.subtask-select[data-v-4cce1be2]{cursor:pointer;color:#1e293b;background:#fff9;border:1.5px solid #00000014;border-radius:8px;min-width:60px;padding:4px 8px;font-size:13px;transition:all .3s}.subtask-select[data-v-4cce1be2]:focus{background:#fff;border-color:#6366f1;outline:none;box-shadow:0 0 0 4px #6366f11a}.subtask-select option[data-v-4cce1be2]{color:#1e293b;background:#fff}.subtask-select option[value="0"][data-v-4cce1be2]{color:#22c55e}.subtask-select option[value="1"][data-v-4cce1be2]{color:#f59e0b}.subtask-select option[value="2"][data-v-4cce1be2]{color:#ef4444}.btn-create-subtask[data-v-4cce1be2]{color:#fff;cursor:pointer;background:linear-gradient(135deg,#2563eb 0%,#1d4ed8 100%);border:none;border-radius:8px;justify-content:center;align-items:center;padding:4px 12px;font-size:14px;font-weight:600;transition:all .3s;display:flex;box-shadow:0 4px 12px -2px #2563eb4d}.btn-create-subtask[data-v-4cce1be2]:hover{transform:translateY(-2px);box-shadow:0 8px 16px -2px #2563eb66}.btn-create-subtask[data-v-4cce1be2]:active{transform:translateY(0)}.btn-cancel[data-v-4cce1be2]{color:#dc2626;cursor:pointer;background:0 0;border:none;border-radius:4px;flex-shrink:0;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex}.btn-cancel[data-v-4cce1be2]:hover{color:#475569;background:#e2e8f0}.add-subtask-form[data-v-4cce1be2]>*{box-sizing:border-box}@media (width<=600px){.add-subtask-form[data-v-4cce1be2]{border-radius:16px;flex-wrap:wrap}.subtask-input[data-v-4cce1be2]{flex:100%;margin-bottom:4px}}.subtasks[data-v-4cce1be2]{background:0 0;border:none;border-radius:0;flex-direction:column;gap:8px;margin-top:10px;margin-left:14px;padding:0;display:flex}.subtasks[data-v-4cce1be2] .task-item{box-shadow:none;border-left-width:5px;padding:10px 12px}.subtasks[data-v-4cce1be2] .task-item:hover{box-shadow:none}.subtasks[data-v-4cce1be2] .task-item{border-top-width:0;border-bottom-width:0;border-right-width:0}.subtasks[data-v-4cce1be2] .task-item.priority-low,.subtasks[data-v-4cce1be2] .task-item.priority-medium,.subtasks[data-v-4cce1be2] .task-item.priority-high{border-top-color:#0000;border-bottom-color:#0000;border-right-color:#0000}.task-item[data-v-4cce1be2] ::selection{background:0 0}.task-list[data-v-0b9b025b]{background:0 0;flex-direction:column;width:100%;max-width:none;height:100dvh;min-height:100vh;margin:0;padding:0;display:flex}.page-header[data-v-0b9b025b]{--header-chip-height:24px;--header-chip-font-size:10px;--header-chip-pad-x:8px;background:#fff;border-radius:16px;justify-content:space-between;align-items:center;gap:8px;margin-bottom:12px;padding:4px 6px;display:flex;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.header-controls[data-v-0b9b025b]{flex:1;min-width:0}.task-form[data-v-0b9b025b]{background:#fff;border:1px solid #6366f11a;border-radius:20px;gap:8px;margin-bottom:8px;padding:8px;display:flex;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.task-input[data-v-0b9b025b]{color:#1e293b;background:0 0;border:none;border-bottom:2px solid #e2e8f0;border-radius:0;flex:1;padding:8px;font-size:14px;transition:all .3s}.task-input[data-v-0b9b025b]:focus{box-shadow:none;background:0 0;border-bottom-color:#6366f1;outline:none}.task-select[data-v-0b9b025b]{cursor:pointer;color:#1e293b;background:#f8fafc;border:2px solid #e2e8f0;border-radius:14px;min-width:150px;padding:10px 14px;font-size:14px;transition:all .3s}.task-select[data-v-0b9b025b]:focus{background:#fff;border-color:#6366f1;outline:none;box-shadow:0 0 0 4px #6366f11a}.task-select option[data-v-0b9b025b]{color:#1e293b;background:#fff}.task-select option[value="0"][data-v-0b9b025b]{color:#22c55e}.task-select option[value="1"][data-v-0b9b025b]{color:#f59e0b}.task-select option[value="2"][data-v-0b9b025b]{color:#ef4444}.btn-add[data-v-0b9b025b]{color:#fff;cursor:pointer;background:linear-gradient(135deg,#2563eb 0%,#1d4ed8 100%);border:none;border-radius:14px;justify-content:center;align-items:center;padding:8px 16px;font-size:14px;font-weight:600;transition:all .3s;display:flex;box-shadow:0 4px 12px -2px #2563eb66}.btn-add[data-v-0b9b025b]:hover{transform:translateY(-2px);box-shadow:0 8px 16px -2px #2563eb80}.btn-add[data-v-0b9b025b]:active{transform:translateY(0)}.checkbox-wrapper[data-v-0b9b025b]{cursor:pointer;-webkit-user-select:none;user-select:none;height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);background:linear-gradient(135deg,#f8fafc 0%,#f1f5f9 100%);border:1px solid #e2e8f0;border-radius:8px;align-items:center;gap:4px;transition:all .3s;display:flex}.checkbox-wrapper[data-v-0b9b025b]:hover{background:#fff;border-color:#6366f1}.checkbox-input[data-v-0b9b025b]{cursor:pointer;appearance:none;background:#ffffffeb;border:1.5px solid #64748b99;border-radius:4px;place-content:center;width:14px;height:14px;transition:background-color .2s,border-color .2s,box-shadow .2s;display:grid}.checkbox-input[data-v-0b9b025b]:after{content:"";opacity:0;border-bottom:2px solid #fff;border-right:2px solid #fff;width:5px;height:8px;transition:opacity .2s;transform:translateY(-1px)rotate(45deg)}.checkbox-input[data-v-0b9b025b]:checked{background:#6366f1;border-color:#6366f1}.checkbox-input[data-v-0b9b025b]:checked:after{opacity:1}.checkbox-input[data-v-0b9b025b]:focus-visible{outline-offset:2px;outline:2px solid #6366f18c}@media (prefers-color-scheme:dark){.checkbox-input[data-v-0b9b025b]{background:#0f172a59;border-color:#94a3b8bf}}.checkbox-label[data-v-0b9b025b]{font-size:var(--header-chip-font-size);color:#475569;cursor:pointer;font-weight:400;transition:all .3s}.checkbox-wrapper:hover .checkbox-label[data-v-0b9b025b]{color:#6366f1}.task-info[data-v-0b9b025b]{flex-shrink:0;align-items:center;gap:8px;display:flex}.info-item[data-v-0b9b025b]{cursor:default;-webkit-user-select:none;user-select:none;height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size);color:#475569;background:linear-gradient(135deg,#f8fafc 0%,#f1f5f9 100%);border:1px solid #e2e8f0;border-radius:8px;align-items:center;gap:4px;font-weight:500;transition:all .3s;display:flex}.info-memory[data-v-0b9b025b]{color:#92400e;background:linear-gradient(135deg,#fef3c7 0%,#fde68a 100%);border-color:#f59e0b;font-weight:600}.info-login-btn[data-v-0b9b025b]{cursor:pointer;color:#1d4ed8;background:linear-gradient(135deg,#dbeafe 0%,#bfdbfe 100%);border-color:#93c5fd;font-weight:600;transition:all .2s}.info-login-btn[data-v-0b9b025b]:hover{background:linear-gradient(135deg,#bfdbfe 0%,#93c5fd 100%);border-color:#60a5fa}.info-user[data-v-0b9b025b]{color:#6d28d9;font-variant-numeric:tabular-nums;background:linear-gradient(135deg,#ede9fe 0%,#ddd6fe 100%);border-color:#c4b5fd;font-weight:600}.info-sync-btn[data-v-0b9b025b]{cursor:pointer;background:linear-gradient(135deg,#d1fae5 0%,#a7f3d0 100%);border-color:#6ee7b7;justify-content:center;min-width:24px;padding:0 6px;font-weight:600;transition:all .2s}.info-sync-btn[data-v-0b9b025b]:hover:enabled{background:linear-gradient(135deg,#a7f3d0 0%,#6ee7b7 100%);border-color:#34d399}.info-sync-btn.syncing[data-v-0b9b025b]{animation:1s linear infinite syncSpin-0b9b025b}@keyframes syncSpin-0b9b025b{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.info-settings-btn[data-v-0b9b025b]{cursor:pointer;color:#6b7280;background:linear-gradient(135deg,#f3f4f6 0%,#e5e7eb 100%);border-color:#d1d5db;justify-content:center;min-width:20px;padding:0 6px;font-weight:600;transition:all .2s}.info-settings-btn[data-v-0b9b025b]:hover{color:#374151;background:linear-gradient(135deg,#e5e7eb 0%,#d1d5db 100%)}.info-logout-btn[data-v-0b9b025b]{cursor:pointer;color:#dc2626;background:linear-gradient(135deg,#fee2e2 0%,#fecaca 100%);border-color:#fca5a5;font-weight:600;transition:all .2s}.info-logout-btn[data-v-0b9b025b]:hover{background:linear-gradient(135deg,#fecaca 0%,#fca5a5 100%);border-color:#f87171}.info-item svg[data-v-0b9b025b]{flex-shrink:0}.sort-group[data-v-0b9b025b]{flex-wrap:wrap;align-items:center;gap:4px;display:flex}.sort-select[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 calc(var(--header-chip-pad-x) + 14px) 0 var(--header-chip-pad-x);cursor:pointer;font-size:var(--header-chip-font-size);color:#475569;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;transition:all .3s}.sort-select[data-v-0b9b025b]:focus{background:#fff;border-color:#6366f1;outline:none}.sort-select[data-v-0b9b025b]:hover{border-color:#6366f1}.sort-select.sort-order-btn[data-v-0b9b025b]{height:var(--header-chip-height);width:var(--header-chip-height);box-sizing:border-box;-webkit-user-select:none;user-select:none;border-radius:8px;justify-content:center;align-items:center;min-width:0;padding:0;font-size:0;line-height:0;display:inline-flex}.sort-order-icon[data-v-0b9b025b]{flex-shrink:0;width:14px;height:14px;display:block}.sort-order-btn[data-v-0b9b025b]:active{transform:translateY(.5px)}.hotkey-settings-btn[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);color:#fff;cursor:pointer;font-size:var(--header-chip-font-size);background:linear-gradient(135deg,#6366f1 0%,#8b5cf6 100%);border:none;border-radius:8px;align-items:center;gap:4px;font-weight:500;transition:all .3s;display:flex;box-shadow:0 2px 8px -2px #6366f166}.hotkey-settings-btn[data-v-0b9b025b]:hover{transform:translateY(-1px);box-shadow:0 4px 12px -2px #6366f180}.hotkey-settings-btn[data-v-0b9b025b]:active{transform:translateY(0)}.voice-btn[data-v-0b9b025b]{background:linear-gradient(135deg,#8b5cf6 0%,#6366f1 100%)!important}.voice-pulse[data-v-0b9b025b]{font-size:12px;animation:1.2s ease-in-out infinite voicePulse-0b9b025b}@keyframes voicePulse-0b9b025b{0%,to{opacity:1}50%{opacity:.4}}.voice-indicator[data-v-0b9b025b]{background:#8b5cf614;border:1px solid #8b5cf626;border-radius:8px;flex-wrap:wrap;align-items:center;gap:6px;margin-top:4px;padding:4px 8px;display:flex}.voice-dot[data-v-0b9b025b]{background:#ef4444;border-radius:50%;flex-shrink:0;width:8px;height:8px;animation:.8s ease-in-out infinite voicePulse-0b9b025b}.voice-text[data-v-0b9b025b]{color:#475569;flex:1;font-size:12px}.voice-error[data-v-0b9b025b]{color:#ef4444;font-size:12px}.voice-candidates[data-v-0b9b025b]{border-top:1px solid #8b5cf61a;flex-wrap:wrap;align-items:center;gap:4px;width:100%;margin-top:4px;padding-top:4px;display:flex}.candidates-hint[data-v-0b9b025b]{color:#6b7280;margin-right:4px;font-size:11px}.candidate-btn[data-v-0b9b025b]{color:#7c3aed;cursor:pointer;background:#fff;border:1px solid #c4b5fd;border-radius:12px;padding:2px 10px;font-size:12px;transition:all .15s}.candidate-btn[data-v-0b9b025b]:hover{color:#fff;background:#7c3aed;border-color:#7c3aed}.hotkey-settings-btn svg[data-v-0b9b025b]{flex-shrink:0;width:14px;height:14px}.hotkey-settings-btn span[data-v-0b9b025b]{font-size:inherit}.empty[data-v-0b9b025b]{text-align:center;color:#94a3b8;background:#fff;border:2px dashed #e2e8f0;border-radius:20px;padding:40px 20px;font-size:16px;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.tasks-shell[data-v-0b9b025b]{background:#ffffffd9;border:1px solid #e2e8f0;border-radius:18px;flex:1;min-height:0;padding:10px;display:flex}.tasks-scroll[data-v-0b9b025b]{scrollbar-gutter:stable;flex-direction:column;flex:1;gap:10px;min-height:0;display:flex;overflow-y:auto}@media (width<=768px){.task-list[data-v-0b9b025b]{max-width:100%}.page-header[data-v-0b9b025b]{border-radius:14px;flex-flow:wrap;align-items:center;gap:6px;margin-bottom:8px;padding:4px 6px}.task-form[data-v-0b9b025b]{border-radius:16px;gap:6px;margin-bottom:8px;padding:8px}.task-input[data-v-0b9b025b]{border-radius:12px;padding:8px 10px;font-size:14px}.task-select[data-v-0b9b025b]{border-radius:12px;min-width:84px;padding:8px 10px;font-size:14px}.btn-add[data-v-0b9b025b]{border-radius:12px;padding:8px 14px;font-size:14px}.task-filters[data-v-0b9b025b]{border-radius:16px;flex-direction:row;align-items:center;gap:6px;margin-bottom:8px;padding:8px 10px}.checkbox-wrapper[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x)}.checkbox-input[data-v-0b9b025b]{width:14px;height:14px}.checkbox-label[data-v-0b9b025b]{font-size:var(--header-chip-font-size)}.sort-group[data-v-0b9b025b]{justify-content:flex-start;width:auto}.sort-select[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 calc(var(--header-chip-pad-x) + 14px) 0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size);flex:none}.hotkey-settings-btn[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size);border-radius:8px}.hotkey-settings-btn svg[data-v-0b9b025b]{width:14px;height:14px}.task-info[data-v-0b9b025b]{justify-content:flex-start;width:auto}.info-item[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size)}.empty[data-v-0b9b025b]{border-radius:16px;padding:30px 16px;font-size:16px}.tasks[data-v-0b9b025b]{gap:6px}}@media (width<=480px){.task-list[data-v-0b9b025b]{padding:0}.page-header[data-v-0b9b025b]{border-radius:14px;margin-bottom:6px;padding:4px 6px}.page-title[data-v-0b9b025b]{font-size:20px}.title-icon[data-v-0b9b025b]{width:22px;height:22px;padding:3px}.task-form[data-v-0b9b025b]{border-radius:14px;gap:6px;margin-bottom:6px;padding:6px}.task-input[data-v-0b9b025b]{border-radius:10px;padding:6px 8px;font-size:14px}.task-select[data-v-0b9b025b]{border-radius:10px;min-width:72px;padding:6px 8px;font-size:14px}.btn-add[data-v-0b9b025b]{border-radius:10px;padding:6px 12px;font-size:14px}.btn-add svg[data-v-0b9b025b]{width:14px;height:14px}.task-filters[data-v-0b9b025b]{border-radius:14px;flex-direction:row;align-items:center;gap:6px;margin-bottom:6px;padding:6px 8px}.checkbox-wrapper[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x)}.checkbox-input[data-v-0b9b025b]{width:16px;height:16px}.checkbox-label[data-v-0b9b025b]{font-size:var(--header-chip-font-size)}.sort-group[data-v-0b9b025b]{justify-content:flex-start;width:auto}.sort-select[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 calc(var(--header-chip-pad-x) + 14px) 0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size);flex:none}.hotkey-settings-btn[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size);border-radius:8px}.hotkey-settings-btn svg[data-v-0b9b025b]{width:12px;height:12px}.hotkey-settings-btn span[data-v-0b9b025b]{display:none}.task-info[data-v-0b9b025b]{justify-content:flex-start;width:auto}.info-item[data-v-0b9b025b]{height:var(--header-chip-height);padding:0 var(--header-chip-pad-x);font-size:var(--header-chip-font-size)}.info-item svg[data-v-0b9b025b]{width:10px;height:10px}.empty[data-v-0b9b025b]{border-radius:14px;padding:25px 12px;font-size:16px}.tasks[data-v-0b9b025b]{gap:6px}}.hotkey-settings-overlay[data-v-ae88d5d6]{z-index:1000;background:#00000080;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.hotkey-settings-dialog[data-v-ae88d5d6]{background:#fff;border-radius:12px;width:90%;max-width:450px;overflow:hidden;box-shadow:0 20px 60px #0000004d}.dialog-header[data-v-ae88d5d6]{border-bottom:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:20px 24px;display:flex}.dialog-header h2[data-v-ae88d5d6]{color:#111827;margin:0;font-size:20px;font-weight:600}.close-button[data-v-ae88d5d6]{color:#6b7280;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:28px;transition:background .2s;display:flex}.close-button[data-v-ae88d5d6]:hover{color:#111827;background:#f3f4f6}.dialog-body[data-v-ae88d5d6]{padding:24px}.form-group[data-v-ae88d5d6]{margin-bottom:20px}.form-group label[data-v-ae88d5d6]{color:#374151;margin-bottom:8px;font-size:14px;font-weight:500;display:block}.form-group select[data-v-ae88d5d6]{cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:6px;width:100%;padding:10px 12px;font-size:14px;transition:border-color .2s}.form-group select[data-v-ae88d5d6]:focus{border-color:#3b82f6;outline:none;box-shadow:0 0 0 3px #3b82f61a}.checkbox-label[data-v-ae88d5d6]{cursor:pointer;-webkit-user-select:none;user-select:none;align-items:center;display:flex}.checkbox-label input[type=checkbox][data-v-ae88d5d6]{cursor:pointer;accent-color:#3b82f6;width:18px;height:18px;margin-right:10px}.checkbox-label span[data-v-ae88d5d6]{font-weight:400}.preview[data-v-ae88d5d6]{background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px;margin-top:24px;padding:16px}.preview strong[data-v-ae88d5d6]{color:#6b7280;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;font-size:13px;display:block}.hotkey-preview[data-v-ae88d5d6]{color:#3b82f6;font-family:Consolas,Monaco,monospace;font-size:16px;font-weight:600}.dialog-footer[data-v-ae88d5d6]{background:#f9fafb;border-top:1px solid #e5e7eb;gap:12px;padding:20px 24px;display:flex}.btn[data-v-ae88d5d6]{cursor:pointer;border:none;border-radius:8px;flex:1;padding:12px 20px;font-size:14px;font-weight:500;transition:all .2s}.btn-primary[data-v-ae88d5d6]{color:#fff;background:#3b82f6}.btn-primary[data-v-ae88d5d6]:hover{background:#2563eb}.btn-secondary[data-v-ae88d5d6]{color:#374151;background:#fff;border:1px solid #d1d5db}.btn-secondary[data-v-ae88d5d6]:hover{background:#f3f4f6;border-color:#9ca3af}.overlay[data-v-ab8b3536]{z-index:1000;background:#00000080;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.dialog[data-v-ab8b3536]{background:#fff;border-radius:12px;width:92%;max-width:720px;overflow:hidden;box-shadow:0 20px 60px #0000004d}.dialog-header[data-v-ab8b3536]{border-bottom:1px solid #e5e7eb;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.dialog-header h2[data-v-ab8b3536]{color:#111827;margin:0;font-size:18px;font-weight:650}.close-button[data-v-ab8b3536]{color:#6b7280;cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;width:32px;height:32px;padding:0;font-size:28px;transition:background .2s;display:flex}.close-button[data-v-ab8b3536]:hover{color:#111827;background:#f3f4f6}.dialog-body[data-v-ab8b3536]{flex-direction:column;gap:16px;padding:18px 20px;display:flex}.section[data-v-ab8b3536]{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:14px}.section-title[data-v-ab8b3536]{letter-spacing:.06em;color:#6b7280;text-transform:uppercase;margin-bottom:10px;font-size:12px;font-weight:700}.checkbox-row[data-v-ab8b3536]{-webkit-user-select:none;user-select:none;align-items:center;gap:10px;display:flex}.checkbox-row input[type=checkbox][data-v-ab8b3536]{cursor:pointer;accent-color:#3b82f6;width:18px;height:18px}.hint[data-v-ab8b3536]{color:#6b7280;margin-top:8px;font-size:12px;line-height:1.5}.mono[data-v-ab8b3536]{font-family:Consolas,Monaco,monospace;font-size:12px}.policy-info[data-v-ab8b3536]{background:#f9fafb;border:1px solid #f3f4f6;border-radius:8px;flex-direction:column;gap:6px;margin-top:10px;padding:10px;display:flex}.policy-item[data-v-ab8b3536]{align-items:center;gap:8px;font-size:13px;display:flex}.policy-item .label[data-v-ab8b3536]{color:#6b7280;width:70px}.policy-item .success[data-v-ab8b3536]{color:#059669;font-weight:550}.policy-item .warn[data-v-ab8b3536]{color:#d97706;font-weight:550}.sync-status[data-v-ab8b3536]{background:#f0f9ff;border:1px solid #e0f2fe;border-radius:8px;flex-direction:column;gap:6px;margin-top:10px;padding:10px;display:flex}.sync-status-item[data-v-ab8b3536]{align-items:center;gap:8px;font-size:13px;display:flex}.sync-status-item .label[data-v-ab8b3536]{color:#64748b;flex-shrink:0;width:70px}.sync-status .success[data-v-ab8b3536]{color:#059669;font-weight:550}.sync-status .offline[data-v-ab8b3536]{color:#94a3b8;font-weight:550}.form-row[data-v-ab8b3536]{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.text-input[data-v-ab8b3536]{background:#fff;border:1px solid #d1d5db;border-radius:8px;flex:1;min-width:240px;padding:10px 12px;font-size:14px;transition:border-color .2s,box-shadow .2s}.text-input[data-v-ab8b3536]:focus{border-color:#3b82f6;outline:none;box-shadow:0 0 0 3px #3b82f61a}.probe[data-v-ab8b3536]{background:#f9fafb;border:1px solid #e5e7eb;border-radius:10px;margin-top:10px;padding:10px 12px}.probe.success[data-v-ab8b3536]{background:#22c55e14;border-color:#22c55e40}.probe.warn[data-v-ab8b3536]{background:#f59e0b14;border-color:#f59e0b40}.probe.error[data-v-ab8b3536]{background:#ef444414;border-color:#ef444440}.probe-title[data-v-ab8b3536]{color:#111827;font-size:13px;font-weight:650}.probe-desc[data-v-ab8b3536]{color:#374151;margin-top:4px;font-size:12px;line-height:1.4}.button-row[data-v-ab8b3536]{flex-wrap:wrap;gap:10px;margin-top:10px;display:flex}.dialog-footer[data-v-ab8b3536]{background:#f9fafb;border-top:1px solid #e5e7eb;justify-content:flex-end;gap:12px;padding:14px 20px;display:flex}.error-text[data-v-ab8b3536]{color:#ef4444;margin-top:8px;font-size:13px}.btn[data-v-ab8b3536]{cursor:pointer;border:none;border-radius:10px;padding:10px 14px;font-size:13px;font-weight:600;transition:all .2s}.btn[data-v-ab8b3536]:disabled{opacity:.6;cursor:not-allowed}.btn-primary[data-v-ab8b3536]{color:#fff;background:#3b82f6}.btn-primary[data-v-ab8b3536]:hover:enabled{background:#2563eb}.btn-secondary[data-v-ab8b3536]{color:#374151;background:#fff;border:1px solid #d1d5db}.btn-secondary[data-v-ab8b3536]:hover:enabled{background:#f3f4f6;border-color:#9ca3af}.btn-outline[data-v-ab8b3536]{color:#3b82f6;background:#fff;border:1px solid #3b82f6}.btn-outline[data-v-ab8b3536]:hover:enabled{color:#2563eb;background:#eff6ff;border-color:#2563eb}*{box-sizing:border-box;margin:0;padding:0}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.app{background:linear-gradient(#f8fafc 0%,#f1f5f9 100%);width:100%;min-height:100vh}h1{text-align:center;color:#1f2937;margin-bottom:32px;font-size:32px;font-weight:700}.toast{z-index:9999;background:#fff;border-left:4px solid #ef4444;border-radius:12px;align-items:center;gap:12px;min-width:300px;max-width:90%;padding:12px 20px;display:flex;position:fixed;top:20px;left:50%;transform:translate(-50%);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d}.toast.success{border-left-color:#22c55e}.toast.error{border-left-color:#ef4444}.toast-content{flex:1;align-items:center;gap:8px;display:flex}.toast-message{color:#1f2937;white-space:pre-line;font-size:14px;font-weight:500}.toast-close{color:#9ca3af;cursor:pointer;background:0 0;border:none;padding:0 4px;font-size:20px}.toast-close:hover{color:#4b5563}.toast-enter-active,.toast-leave-active{transition:all .3s cubic-bezier(.4,0,.2,1)}.toast-enter-from,.toast-leave-to{opacity:0;transform:translate(-50%,-20px)} diff --git a/src/Hua.Todo.Host/wwwroot/spa/assets/index-Ct35jSwq.js b/src/Hua.Todo.Host/wwwroot/spa/assets/index-Ct35jSwq.js new file mode 100644 index 0000000..3d87c49 --- /dev/null +++ b/src/Hua.Todo.Host/wwwroot/spa/assets/index-Ct35jSwq.js @@ -0,0 +1,12 @@ +var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function n(e){let t=Object.create(null);for(let n of e.split(`,`))t[n]=1;return e=>e in t}var r={},i=[],a=()=>{},o=()=>!1,s=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),c=e=>e.startsWith(`onUpdate:`),l=Object.assign,u=(e,t)=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)},d=Object.prototype.hasOwnProperty,f=(e,t)=>d.call(e,t),p=Array.isArray,m=e=>C(e)===`[object Map]`,h=e=>C(e)===`[object Set]`,g=e=>C(e)===`[object Date]`,_=e=>typeof e==`function`,v=e=>typeof e==`string`,y=e=>typeof e==`symbol`,b=e=>typeof e==`object`&&!!e,x=e=>(b(e)||_(e))&&_(e.then)&&_(e.catch),S=Object.prototype.toString,C=e=>S.call(e),w=e=>C(e).slice(8,-1),T=e=>C(e)===`[object Object]`,E=e=>v(e)&&e!==`NaN`&&e[0]!==`-`&&``+parseInt(e,10)===e,ee=n(`,key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted`),D=e=>{let t=Object.create(null);return(n=>t[n]||(t[n]=e(n)))},te=/-\w/g,O=D(e=>e.replace(te,e=>e.slice(1).toUpperCase())),ne=/\B([A-Z])/g,re=D(e=>e.replace(ne,`-$1`).toLowerCase()),ie=D(e=>e.charAt(0).toUpperCase()+e.slice(1)),ae=D(e=>e?`on${ie(e)}`:``),k=(e,t)=>!Object.is(e,t),oe=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},ce=e=>{let t=parseFloat(e);return isNaN(t)?e:t},le=e=>{let t=v(e)?Number(e):NaN;return isNaN(t)?e:t},ue,de=()=>ue||=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{};function fe(e){if(p(e)){let t={};for(let n=0;n{if(e){let n=e.split(me);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function _e(e){let t=``;if(v(e))t=e;else if(p(e))for(let n=0;nSe(e,t))}var we=e=>!!(e&&e.__v_isRef===!0),A=e=>v(e)?e:e==null?``:p(e)||b(e)&&(e.toString===S||!_(e.toString))?we(e)?A(e.value):JSON.stringify(e,Te,2):String(e),Te=(e,t)=>we(t)?Te(e,t.value):m(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((e,[t,n],r)=>(e[Ee(t,r)+` =>`]=n,e),{})}:h(t)?{[`Set(${t.size})`]:[...t.values()].map(e=>Ee(e))}:y(t)?Ee(t):b(t)&&!p(t)&&!T(t)?String(t):t,Ee=(e,t=``)=>y(e)?`Symbol(${e.description??t})`:e,De,Oe=class{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.__v_skip=!0,this.parent=De,!e&&De&&(this.index=(De.scopes||=[]).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,t;if(this.scopes)for(e=0,t=this.scopes.length;e0&&--this._on===0&&(De=this.prevScope,this.prevScope=void 0)}stop(e){if(this._active){this._active=!1;let t,n;for(t=0,n=this.effects.length;t0)return;if(Pe){let e=Pe;for(Pe=void 0;e;){let t=e.next;e.next=void 0,e.flags&=-9,e=t}}let e;for(;Ne;){let t=Ne;for(Ne=void 0;t;){let n=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(t){e||=t}t=n}}if(e)throw e}function Re(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function ze(e){let t,n=e.depsTail,r=n;for(;r;){let e=r.prevDep;r.version===-1?(r===n&&(n=e),He(r),Ue(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=e}e.deps=t,e.depsTail=n}function Be(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Ve(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Ve(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Ye)||(e.globalVersion=Ye,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Be(e))))return;e.flags|=2;let t=e.dep,n=j,r=We;j=e,We=!0;try{Re(e);let n=e.fn(e._value);(t.version===0||k(n,e._value))&&(e.flags|=128,e._value=n,t.version++)}catch(e){throw t.version++,e}finally{j=n,We=r,ze(e),e.flags&=-3}}function He(e,t=!1){let{dep:n,prevSub:r,nextSub:i}=e;if(r&&(r.nextSub=i,e.prevSub=void 0),i&&(i.prevSub=r,e.nextSub=void 0),n.subs===e&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let e=n.computed.deps;e;e=e.nextDep)He(e,!0)}!t&&!--n.sc&&n.map&&n.map.delete(n.key)}function Ue(e){let{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}var We=!0,Ge=[];function Ke(){Ge.push(We),We=!1}function qe(){let e=Ge.pop();We=e===void 0?!0:e}function Je(e){let{cleanup:t}=e;if(e.cleanup=void 0,t){let e=j;j=void 0;try{t()}finally{j=e}}}var Ye=0,Xe=class{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}},Ze=class{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!j||!We||j===this.computed)return;let t=this.activeLink;if(t===void 0||t.sub!==j)t=this.activeLink=new Xe(j,this),j.deps?(t.prevDep=j.depsTail,j.depsTail.nextDep=t,j.depsTail=t):j.deps=j.depsTail=t,Qe(t);else if(t.version===-1&&(t.version=this.version,t.nextDep)){let e=t.nextDep;e.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=e),t.prevDep=j.depsTail,t.nextDep=void 0,j.depsTail.nextDep=t,j.depsTail=t,j.deps===t&&(j.deps=e)}return t}trigger(e){this.version++,Ye++,this.notify(e)}notify(e){Ie();try{for(let e=this.subs;e;e=e.prevSub)e.sub.notify()&&e.sub.dep.notify()}finally{Le()}}};function Qe(e){if(e.dep.sc++,e.sub.flags&4){let t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let e=t.deps;e;e=e.nextDep)Qe(e)}let n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}var $e=new WeakMap,et=Symbol(``),tt=Symbol(``),nt=Symbol(``);function M(e,t,n){if(We&&j){let t=$e.get(e);t||$e.set(e,t=new Map);let r=t.get(n);r||(t.set(n,r=new Ze),r.map=t,r.key=n),r.track()}}function rt(e,t,n,r,i,a){let o=$e.get(e);if(!o){Ye++;return}let s=e=>{e&&e.trigger()};if(Ie(),t===`clear`)o.forEach(s);else{let i=p(e),a=i&&E(n);if(i&&n===`length`){let e=Number(r);o.forEach((t,n)=>{(n===`length`||n===nt||!y(n)&&n>=e)&&s(t)})}else switch((n!==void 0||o.has(void 0))&&s(o.get(n)),a&&s(o.get(nt)),t){case`add`:i?a&&s(o.get(`length`)):(s(o.get(et)),m(e)&&s(o.get(tt)));break;case`delete`:i||(s(o.get(et)),m(e)&&s(o.get(tt)));break;case`set`:m(e)&&s(o.get(et));break}}Le()}function it(e){let t=N(e);return t===e?t:(M(t,`iterate`,nt),Wt(e)?t:t.map(qt))}function at(e){return M(e=N(e),`iterate`,nt),e}function ot(e,t){return Ut(e)?Ht(e)?Jt(qt(t)):Jt(t):qt(t)}var st={__proto__:null,[Symbol.iterator](){return ct(this,Symbol.iterator,e=>ot(this,e))},concat(...e){return it(this).concat(...e.map(e=>p(e)?it(e):e))},entries(){return ct(this,`entries`,e=>(e[1]=ot(this,e[1]),e))},every(e,t){return ut(this,`every`,e,t,void 0,arguments)},filter(e,t){return ut(this,`filter`,e,t,e=>e.map(e=>ot(this,e)),arguments)},find(e,t){return ut(this,`find`,e,t,e=>ot(this,e),arguments)},findIndex(e,t){return ut(this,`findIndex`,e,t,void 0,arguments)},findLast(e,t){return ut(this,`findLast`,e,t,e=>ot(this,e),arguments)},findLastIndex(e,t){return ut(this,`findLastIndex`,e,t,void 0,arguments)},forEach(e,t){return ut(this,`forEach`,e,t,void 0,arguments)},includes(...e){return ft(this,`includes`,e)},indexOf(...e){return ft(this,`indexOf`,e)},join(e){return it(this).join(e)},lastIndexOf(...e){return ft(this,`lastIndexOf`,e)},map(e,t){return ut(this,`map`,e,t,void 0,arguments)},pop(){return pt(this,`pop`)},push(...e){return pt(this,`push`,e)},reduce(e,...t){return dt(this,`reduce`,e,t)},reduceRight(e,...t){return dt(this,`reduceRight`,e,t)},shift(){return pt(this,`shift`)},some(e,t){return ut(this,`some`,e,t,void 0,arguments)},splice(...e){return pt(this,`splice`,e)},toReversed(){return it(this).toReversed()},toSorted(e){return it(this).toSorted(e)},toSpliced(...e){return it(this).toSpliced(...e)},unshift(...e){return pt(this,`unshift`,e)},values(){return ct(this,`values`,e=>ot(this,e))}};function ct(e,t,n){let r=at(e),i=r[t]();return r!==e&&!Wt(e)&&(i._next=i.next,i.next=()=>{let e=i._next();return e.done||(e.value=n(e.value)),e}),i}var lt=Array.prototype;function ut(e,t,n,r,i,a){let o=at(e),s=o!==e&&!Wt(e),c=o[t];if(c!==lt[t]){let t=c.apply(e,a);return s?qt(t):t}let l=n;o!==e&&(s?l=function(t,r){return n.call(this,ot(e,t),r,e)}:n.length>2&&(l=function(t,r){return n.call(this,t,r,e)}));let u=c.call(o,l,r);return s&&i?i(u):u}function dt(e,t,n,r){let i=at(e),a=i!==e&&!Wt(e),o=n,s=!1;i!==e&&(a?(s=r.length===0,o=function(t,r,i){return s&&(s=!1,t=ot(e,t)),n.call(this,t,ot(e,r),i,e)}):n.length>3&&(o=function(t,r,i){return n.call(this,t,r,i,e)}));let c=i[t](o,...r);return s?ot(e,c):c}function ft(e,t,n){let r=N(e);M(r,`iterate`,nt);let i=r[t](...n);return(i===-1||i===!1)&&Gt(n[0])?(n[0]=N(n[0]),r[t](...n)):i}function pt(e,t,n=[]){Ke(),Ie();let r=N(e)[t].apply(e,n);return Le(),qe(),r}var mt=n(`__proto__,__v_isRef,__isVue`),ht=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!==`arguments`&&e!==`caller`).map(e=>Symbol[e]).filter(y));function gt(e){y(e)||(e=String(e));let t=N(this);return M(t,`has`,e),t.hasOwnProperty(e)}var _t=class{constructor(e=!1,t=!1){this._isReadonly=e,this._isShallow=t}get(e,t,n){if(t===`__v_skip`)return e.__v_skip;let r=this._isReadonly,i=this._isShallow;if(t===`__v_isReactive`)return!r;if(t===`__v_isReadonly`)return r;if(t===`__v_isShallow`)return i;if(t===`__v_raw`)return n===(r?i?Ft:Pt:i?Nt:Mt).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;let a=p(e);if(!r){let e;if(a&&(e=st[t]))return e;if(t===`hasOwnProperty`)return gt}let o=Reflect.get(e,t,P(e)?e:n);if((y(t)?ht.has(t):mt(t))||(r||M(e,`get`,t),i))return o;if(P(o)){let e=a&&E(t)?o:o.value;return r&&b(e)?Bt(e):e}return b(o)?r?Bt(o):Rt(o):o}},vt=class extends _t{constructor(e=!1){super(!1,e)}set(e,t,n,r){let i=e[t],a=p(e)&&E(t);if(!this._isShallow){let e=Ut(i);if(!Wt(n)&&!Ut(n)&&(i=N(i),n=N(n)),!a&&P(i)&&!P(n))return e||(i.value=n),!0}let o=a?Number(t)e,wt=e=>Reflect.getPrototypeOf(e);function Tt(e,t,n){return function(...r){let i=this.__v_raw,a=N(i),o=m(a),s=e===`entries`||e===Symbol.iterator&&o,c=e===`keys`&&o,u=i[e](...r),d=n?Ct:t?Jt:qt;return!t&&M(a,`iterate`,c?tt:et),l(Object.create(u),{next(){let{value:e,done:t}=u.next();return t?{value:e,done:t}:{value:s?[d(e[0]),d(e[1])]:d(e),done:t}}})}}function Et(e){return function(...t){return e===`delete`?!1:e===`clear`?void 0:this}}function Dt(e,t){let n={get(n){let r=this.__v_raw,i=N(r),a=N(n);e||(k(n,a)&&M(i,`get`,n),M(i,`get`,a));let{has:o}=wt(i),s=t?Ct:e?Jt:qt;if(o.call(i,n))return s(r.get(n));if(o.call(i,a))return s(r.get(a));r!==i&&r.get(n)},get size(){let t=this.__v_raw;return!e&&M(N(t),`iterate`,et),t.size},has(t){let n=this.__v_raw,r=N(n),i=N(t);return e||(k(t,i)&&M(r,`has`,t),M(r,`has`,i)),t===i?n.has(t):n.has(t)||n.has(i)},forEach(n,r){let i=this,a=i.__v_raw,o=N(a),s=t?Ct:e?Jt:qt;return!e&&M(o,`iterate`,et),a.forEach((e,t)=>n.call(r,s(e),s(t),i))}};return l(n,e?{add:Et(`add`),set:Et(`set`),delete:Et(`delete`),clear:Et(`clear`)}:{add(e){let n=N(this),r=wt(n),i=N(e),a=!t&&!Wt(e)&&!Ut(e)?i:e;return r.has.call(n,a)||k(e,a)&&r.has.call(n,e)||k(i,a)&&r.has.call(n,i)||(n.add(a),rt(n,`add`,a,a)),this},set(e,n){!t&&!Wt(n)&&!Ut(n)&&(n=N(n));let r=N(this),{has:i,get:a}=wt(r),o=i.call(r,e);o||=(e=N(e),i.call(r,e));let s=a.call(r,e);return r.set(e,n),o?k(n,s)&&rt(r,`set`,e,n,s):rt(r,`add`,e,n),this},delete(e){let t=N(this),{has:n,get:r}=wt(t),i=n.call(t,e);i||=(e=N(e),n.call(t,e));let a=r?r.call(t,e):void 0,o=t.delete(e);return i&&rt(t,`delete`,e,void 0,a),o},clear(){let e=N(this),t=e.size!==0,n=e.clear();return t&&rt(e,`clear`,void 0,void 0,void 0),n}}),[`keys`,`values`,`entries`,Symbol.iterator].forEach(r=>{n[r]=Tt(r,e,t)}),n}function Ot(e,t){let n=Dt(e,t);return(t,r,i)=>r===`__v_isReactive`?!e:r===`__v_isReadonly`?e:r===`__v_raw`?t:Reflect.get(f(n,r)&&r in t?n:t,r,i)}var kt={get:Ot(!1,!1)},At={get:Ot(!1,!0)},jt={get:Ot(!0,!1)},Mt=new WeakMap,Nt=new WeakMap,Pt=new WeakMap,Ft=new WeakMap;function It(e){switch(e){case`Object`:case`Array`:return 1;case`Map`:case`Set`:case`WeakMap`:case`WeakSet`:return 2;default:return 0}}function Lt(e){return e.__v_skip||!Object.isExtensible(e)?0:It(w(e))}function Rt(e){return Ut(e)?e:Vt(e,!1,bt,kt,Mt)}function zt(e){return Vt(e,!1,St,At,Nt)}function Bt(e){return Vt(e,!0,xt,jt,Pt)}function Vt(e,t,n,r,i){if(!b(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;let a=Lt(e);if(a===0)return e;let o=i.get(e);if(o)return o;let s=new Proxy(e,a===2?r:n);return i.set(e,s),s}function Ht(e){return Ut(e)?Ht(e.__v_raw):!!(e&&e.__v_isReactive)}function Ut(e){return!!(e&&e.__v_isReadonly)}function Wt(e){return!!(e&&e.__v_isShallow)}function Gt(e){return e?!!e.__v_raw:!1}function N(e){let t=e&&e.__v_raw;return t?N(t):e}function Kt(e){return!f(e,`__v_skip`)&&Object.isExtensible(e)&&se(e,`__v_skip`,!0),e}var qt=e=>b(e)?Rt(e):e,Jt=e=>b(e)?Bt(e):e;function P(e){return e?e.__v_isRef===!0:!1}function F(e){return Yt(e,!1)}function Yt(e,t){return P(e)?e:new Xt(e,t)}var Xt=class{constructor(e,t){this.dep=new Ze,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=t?e:N(e),this._value=t?e:qt(e),this.__v_isShallow=t}get value(){return this.dep.track(),this._value}set value(e){let t=this._rawValue,n=this.__v_isShallow||Wt(e)||Ut(e);e=n?e:N(e),k(e,t)&&(this._rawValue=e,this._value=n?e:qt(e),this.dep.trigger())}};function Zt(e){return P(e)?e.value:e}var Qt={get:(e,t,n)=>t===`__v_raw`?e:Zt(Reflect.get(e,t,n)),set:(e,t,n,r)=>{let i=e[t];return P(i)&&!P(n)?(i.value=n,!0):Reflect.set(e,t,n,r)}};function $t(e){return Ht(e)?e:new Proxy(e,Qt)}var en=class{constructor(e,t,n){this.fn=e,this.setter=t,this._value=void 0,this.dep=new Ze(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Ye-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!t,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&j!==this)return Fe(this,!0),!0}get value(){let e=this.dep.track();return Ve(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}};function tn(e,t,n=!1){let r,i;return _(e)?r=e:(r=e.get,i=e.set),new en(r,i,n)}var nn={},rn=new WeakMap,an=void 0;function on(e,t=!1,n=an){if(n){let t=rn.get(n);t||rn.set(n,t=[]),t.push(e)}}function sn(e,t,n=r){let{immediate:i,deep:o,once:s,scheduler:c,augmentJob:l,call:d}=n,f=e=>o?e:Wt(e)||o===!1||o===0?cn(e,1):cn(e),m,h,g,v,y=!1,b=!1;if(P(e)?(h=()=>e.value,y=Wt(e)):Ht(e)?(h=()=>f(e),y=!0):p(e)?(b=!0,y=e.some(e=>Ht(e)||Wt(e)),h=()=>e.map(e=>{if(P(e))return e.value;if(Ht(e))return f(e);if(_(e))return d?d(e,2):e()})):h=_(e)?t?d?()=>d(e,2):e:()=>{if(g){Ke();try{g()}finally{qe()}}let t=an;an=m;try{return d?d(e,3,[v]):e(v)}finally{an=t}}:a,t&&o){let e=h,t=o===!0?1/0:o;h=()=>cn(e(),t)}let x=ke(),S=()=>{m.stop(),x&&x.active&&u(x.effects,m)};if(s&&t){let e=t;t=(...t)=>{e(...t),S()}}let C=b?Array(e.length).fill(nn):nn,w=e=>{if(!(!(m.flags&1)||!m.dirty&&!e))if(t){let e=m.run();if(o||y||(b?e.some((e,t)=>k(e,C[t])):k(e,C))){g&&g();let n=an;an=m;try{let n=[e,C===nn?void 0:b&&C[0]===nn?[]:C,v];C=e,d?d(t,3,n):t(...n)}finally{an=n}}}else m.run()};return l&&l(w),m=new je(h),m.scheduler=c?()=>c(w,!1):w,v=e=>on(e,!1,m),g=m.onStop=()=>{let e=rn.get(m);if(e){if(d)d(e,4);else for(let t of e)t();rn.delete(m)}},t?i?w(!0):C=m.run():c?c(w.bind(null,!0),!0):m.run(),S.pause=m.pause.bind(m),S.resume=m.resume.bind(m),S.stop=S,S}function cn(e,t=1/0,n){if(t<=0||!b(e)||e.__v_skip||(n||=new Map,(n.get(e)||0)>=t))return e;if(n.set(e,t),t--,P(e))cn(e.value,t,n);else if(p(e))for(let r=0;r{cn(e,t,n)});else if(T(e)){for(let r in e)cn(e[r],t,n);for(let r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&cn(e[r],t,n)}return e}function ln(e,t,n,r){try{return r?e(...r):e()}catch(e){dn(e,t,n)}}function un(e,t,n,r){if(_(e)){let i=ln(e,t,n,r);return i&&x(i)&&i.catch(e=>{dn(e,t,n)}),i}if(p(e)){let i=[];for(let a=0;a>>1,i=I[r],a=En(i);a=En(n)?I.push(e):I.splice(bn(t),0,e),e.flags|=1,Sn()}}function Sn(){vn||=_n.then(Dn)}function Cn(e){p(e)?mn.push(...e):hn&&e.id===-1?hn.splice(gn+1,0,e):e.flags&1||(mn.push(e),e.flags|=1),Sn()}function wn(e,t,n=pn+1){for(;nEn(e)-En(t));if(mn.length=0,hn){hn.push(...e);return}for(hn=e,gn=0;gne.id==null?e.flags&2?-1:1/0:e.id;function Dn(e){try{for(pn=0;pn{r._d&&Qi(-1);let i=An(t),a;try{a=e(...n)}finally{An(i),r._d&&Qi(1)}return a};return r._n=!0,r._c=!0,r._d=!0,r}function L(e,t){if(On===null)return e;let n=Na(On),i=e.dirs||=[];for(let e=0;e1)return n&&_(t)?t.call(r&&r.proxy):t}}var Fn=Symbol.for(`v-scx`),In=()=>Pn(Fn);function Ln(e,t,n){return Rn(e,t,n)}function Rn(e,t,n=r){let{immediate:i,deep:o,flush:s,once:c}=n,u=l({},n),d=t&&i||!t&&s!==`post`,f;if(wa){if(s===`sync`){let e=In();f=e.__watcherHandles||=[]}else if(!d){let e=()=>{};return e.stop=a,e.resume=a,e.pause=a,e}}let p=K;u.call=(e,t,n)=>un(e,p,t,n);let m=!1;s===`post`?u.scheduler=e=>{Ni(e,p&&p.suspense)}:s!==`sync`&&(m=!0,u.scheduler=(e,t)=>{t?e():xn(e)}),u.augmentJob=e=>{t&&(e.flags|=4),m&&(e.flags|=2,p&&(e.id=p.uid,e.i=p))};let h=sn(e,t,u);return wa&&(f?f.push(h):d&&h()),h}function zn(e,t,n){let r=this.proxy,i=v(e)?e.includes(`.`)?Bn(r,e):()=>r[e]:e.bind(r,r),a;_(t)?a=t:(a=t.handler,n=t);let o=xa(this),s=Rn(i,a.bind(r),n);return o(),s}function Bn(e,t){let n=t.split(`.`);return()=>{let t=e;for(let e=0;ee.__isTeleport,Un=Symbol(`_leaveCb`),Wn=Symbol(`_enterCb`);function Gn(){let e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return yr(()=>{e.isMounted=!0}),Sr(()=>{e.isUnmounting=!0}),e}var Kn=[Function,Array],qn={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Kn,onEnter:Kn,onAfterEnter:Kn,onEnterCancelled:Kn,onBeforeLeave:Kn,onLeave:Kn,onAfterLeave:Kn,onLeaveCancelled:Kn,onBeforeAppear:Kn,onAppear:Kn,onAfterAppear:Kn,onAppearCancelled:Kn},Jn=e=>{let t=e.subTree;return t.component?Jn(t.component):t},Yn={name:`BaseTransition`,props:qn,setup(e,{slots:t}){let n=va(),r=Gn();return()=>{let i=t.default&&rr(t.default(),!0);if(!i||!i.length)return;let a=Xn(i),o=N(e),{mode:s}=o;if(r.isLeaving)return er(a);let c=tr(a);if(!c)return er(a);let l=$n(c,o,r,n,e=>l=e);c.type!==B&&nr(c,l);let u=n.subTree&&tr(n.subTree);if(u&&u.type!==B&&!na(u,c)&&Jn(n).type!==B){let e=$n(u,o,r,n);if(nr(u,e),s===`out-in`&&c.type!==B)return r.isLeaving=!0,e.afterLeave=()=>{r.isLeaving=!1,n.job.flags&8||n.update(),delete e.afterLeave,u=void 0},er(a);s===`in-out`&&c.type!==B?e.delayLeave=(e,t,n)=>{let i=Qn(r,u);i[String(u.key)]=u,e[Un]=()=>{t(),e[Un]=void 0,delete l.delayedLeave,u=void 0},l.delayedLeave=()=>{n(),delete l.delayedLeave,u=void 0}}:u=void 0}else u&&=void 0;return a}}};function Xn(e){let t=e[0];if(e.length>1){for(let n of e)if(n.type!==B){t=n;break}}return t}var Zn=Yn;function Qn(e,t){let{leavingVNodes:n}=e,r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function $n(e,t,n,r,i){let{appear:a,mode:o,persisted:s=!1,onBeforeEnter:c,onEnter:l,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:f,onLeave:m,onAfterLeave:h,onLeaveCancelled:g,onBeforeAppear:_,onAppear:v,onAfterAppear:y,onAppearCancelled:b}=t,x=String(e.key),S=Qn(n,e),C=(e,t)=>{e&&un(e,r,9,t)},w=(e,t)=>{let n=t[1];C(e,t),p(e)?e.every(e=>e.length<=1)&&n():e.length<=1&&n()},T={mode:o,persisted:s,beforeEnter(t){let r=c;if(!n.isMounted)if(a)r=_||c;else return;t[Un]&&t[Un](!0);let i=S[x];i&&na(e,i)&&i.el[Un]&&i.el[Un](),C(r,[t])},enter(t){if(S[x]===e)return;let r=l,i=u,o=d;if(!n.isMounted)if(a)r=v||l,i=y||u,o=b||d;else return;let s=!1;t[Wn]=e=>{s||(s=!0,C(e?o:i,[t]),T.delayedLeave&&T.delayedLeave(),t[Wn]=void 0)};let c=t[Wn].bind(null,!1);r?w(r,[t,c]):c()},leave(t,r){let i=String(e.key);if(t[Wn]&&t[Wn](!0),n.isUnmounting)return r();C(f,[t]);let a=!1;t[Un]=n=>{a||(a=!0,r(),C(n?g:h,[t]),t[Un]=void 0,S[i]===e&&delete S[i])};let o=t[Un].bind(null,!1);S[i]=e,m?w(m,[t,o]):o()},clone(e){let a=$n(e,t,n,r,i);return i&&i(a),a}};return T}function er(e){if(dr(e))return e=sa(e),e.children=null,e}function tr(e){if(!dr(e))return Hn(e.type)&&e.children?Xn(e.children):e;if(e.component)return e.component.subTree;let{shapeFlag:t,children:n}=e;if(n){if(t&16)return n[0];if(t&32&&_(n.default))return n.default()}}function nr(e,t){e.shapeFlag&6&&e.component?(e.transition=t,nr(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function rr(e,t=!1,n){let r=[],i=0;for(let a=0;a1)for(let e=0;ecr(e,t&&(p(t)?t[r]:t),n,i,a));return}if(ur(i)&&!a){i.shapeFlag&512&&i.type.__asyncResolved&&i.component.subTree.component&&cr(e,t,n,i.component.subTree);return}let s=i.shapeFlag&4?Na(i.component):i.el,c=a?null:s,{i:l,r:d}=e,m=t&&t.r,h=l.refs===r?l.refs={}:l.refs,g=l.setupState,y=N(g),b=g===r?o:e=>or(h,e)?!1:f(y,e),x=(e,t)=>!(t&&or(h,t));if(m!=null&&m!==d){if(lr(t),v(m))h[m]=null,b(m)&&(g[m]=null);else if(P(m)){let e=t;x(m,e.k)&&(m.value=null),e.k&&(h[e.k]=null)}}if(_(d))ln(d,l,12,[c,h]);else{let t=v(d),r=P(d);if(t||r){let i=()=>{if(e.f){let n=t?b(d)?g[d]:h[d]:x(d)||!e.k?d.value:h[e.k];if(a)p(n)&&u(n,s);else if(p(n))n.includes(s)||n.push(s);else if(t)h[d]=[s],b(d)&&(g[d]=h[d]);else{let t=[s];x(d,e.k)&&(d.value=t),e.k&&(h[e.k]=t)}}else t?(h[d]=c,b(d)&&(g[d]=c)):r&&(x(d,e.k)&&(d.value=c),e.k&&(h[e.k]=c))};if(c){let t=()=>{i(),sr.delete(e)};t.id=-1,sr.set(e,t),Ni(t,n)}else lr(e),i()}}}function lr(e){let t=sr.get(e);t&&(t.flags|=8,sr.delete(e))}de().requestIdleCallback,de().cancelIdleCallback;var ur=e=>!!e.type.__asyncLoader,dr=e=>e.type.__isKeepAlive;function fr(e,t){mr(e,`a`,t)}function pr(e,t){mr(e,`da`,t)}function mr(e,t,n=K){let r=e.__wdc||=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()};if(gr(t,r,n),n){let e=n.parent;for(;e&&e.parent;)dr(e.parent.vnode)&&hr(r,t,n,e),e=e.parent}}function hr(e,t,n,r){let i=gr(t,e,r,!0);Cr(()=>{u(r[t],i)},n)}function gr(e,t,n=K,r=!1){if(n){let i=n[e]||(n[e]=[]),a=t.__weh||=(...r)=>{Ke();let i=xa(n),a=un(t,n,e,r);return i(),qe(),a};return r?i.unshift(a):i.push(a),a}}var _r=e=>(t,n=K)=>{(!wa||e===`sp`)&&gr(e,(...e)=>t(...e),n)},vr=_r(`bm`),yr=_r(`m`),br=_r(`bu`),xr=_r(`u`),Sr=_r(`bum`),Cr=_r(`um`),wr=_r(`sp`),Tr=_r(`rtg`),Er=_r(`rtc`);function Dr(e,t=K){gr(`ec`,e,t)}var Or=`components`;function kr(e,t){return jr(Or,e,!0,t)||e}var Ar=Symbol.for(`v-ndc`);function jr(e,t,n=!0,r=!1){let i=On||K;if(i){let n=i.type;if(e===Or){let e=Pa(n,!1);if(e&&(e===t||e===O(t)||e===ie(O(t))))return n}let a=Mr(i[e]||n[e],t)||Mr(i.appContext[e],t);return!a&&r?n:a}}function Mr(e,t){return e&&(e[t]||e[O(t)]||e[ie(O(t))])}function Nr(e,t,n,r){let i,a=n&&n[r],o=p(e);if(o||v(e)){let n=o&&Ht(e),r=!1,s=!1;n&&(r=!Wt(e),s=Ut(e),e=at(e)),i=Array(e.length);for(let n=0,o=e.length;nt(e,n,void 0,a&&a[n]));else{let n=Object.keys(e);i=Array(n.length);for(let r=0,o=n.length;re?Ca(e)?Na(e):Pr(e.parent):null,Fr=l(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Pr(e.parent),$root:e=>Pr(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Wr(e),$forceUpdate:e=>e.f||=()=>{xn(e.update)},$nextTick:e=>e.n||=yn.bind(e.proxy),$watch:e=>zn.bind(e)}),Ir=(e,t)=>e!==r&&!e.__isScriptSetup&&f(e,t),Lr={get({_:e},t){if(t===`__v_skip`)return!0;let{ctx:n,setupState:i,data:a,props:o,accessCache:s,type:c,appContext:l}=e;if(t[0]!==`$`){let e=s[t];if(e!==void 0)switch(e){case 1:return i[t];case 2:return a[t];case 4:return n[t];case 3:return o[t]}else if(Ir(i,t))return s[t]=1,i[t];else if(a!==r&&f(a,t))return s[t]=2,a[t];else if(f(o,t))return s[t]=3,o[t];else if(n!==r&&f(n,t))return s[t]=4,n[t];else zr&&(s[t]=0)}let u=Fr[t],d,p;if(u)return t===`$attrs`&&M(e.attrs,`get`,``),u(e);if((d=c.__cssModules)&&(d=d[t]))return d;if(n!==r&&f(n,t))return s[t]=4,n[t];if(p=l.config.globalProperties,f(p,t))return p[t]},set({_:e},t,n){let{data:i,setupState:a,ctx:o}=e;return Ir(a,t)?(a[t]=n,!0):i!==r&&f(i,t)?(i[t]=n,!0):f(e.props,t)||t[0]===`$`&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:i,appContext:a,props:o,type:s}},c){let l;return!!(n[c]||e!==r&&c[0]!==`$`&&f(e,c)||Ir(t,c)||f(o,c)||f(i,c)||f(Fr,c)||f(a.config.globalProperties,c)||(l=s.__cssModules)&&l[c])},defineProperty(e,t,n){return n.get==null?f(n,`value`)&&this.set(e,t,n.value,null):e._.accessCache[t]=0,Reflect.defineProperty(e,t,n)}};function Rr(e){return p(e)?e.reduce((e,t)=>(e[t]=null,e),{}):e}var zr=!0;function Br(e){let t=Wr(e),n=e.proxy,r=e.ctx;zr=!1,t.beforeCreate&&Hr(t.beforeCreate,e,`bc`);let{data:i,computed:o,methods:s,watch:c,provide:l,inject:u,created:d,beforeMount:f,mounted:m,beforeUpdate:h,updated:g,activated:v,deactivated:y,beforeDestroy:x,beforeUnmount:S,destroyed:C,unmounted:w,render:T,renderTracked:E,renderTriggered:ee,errorCaptured:D,serverPrefetch:te,expose:O,inheritAttrs:ne,components:re,directives:ie,filters:ae}=t;if(u&&Vr(u,r,null),s)for(let e in s){let t=s[e];_(t)&&(r[e]=t.bind(n))}if(i){let t=i.call(n,n);b(t)&&(e.data=Rt(t))}if(zr=!0,o)for(let e in o){let t=o[e],i=Ia({get:_(t)?t.bind(n,n):_(t.get)?t.get.bind(n,n):a,set:!_(t)&&_(t.set)?t.set.bind(n):a});Object.defineProperty(r,e,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e})}if(c)for(let e in c)Ur(c[e],r,n,e);if(l){let e=_(l)?l.call(n):l;Reflect.ownKeys(e).forEach(t=>{Nn(t,e[t])})}d&&Hr(d,e,`c`);function k(e,t){p(t)?t.forEach(t=>e(t.bind(n))):t&&e(t.bind(n))}if(k(vr,f),k(yr,m),k(br,h),k(xr,g),k(fr,v),k(pr,y),k(Dr,D),k(Er,E),k(Tr,ee),k(Sr,S),k(Cr,w),k(wr,te),p(O))if(O.length){let t=e.exposed||={};O.forEach(e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t,enumerable:!0})})}else e.exposed||={};T&&e.render===a&&(e.render=T),ne!=null&&(e.inheritAttrs=ne),re&&(e.components=re),ie&&(e.directives=ie),te&&ar(e)}function Vr(e,t,n=a){p(e)&&(e=Yr(e));for(let n in e){let r=e[n],i;i=b(r)?`default`in r?Pn(r.from||n,r.default,!0):Pn(r.from||n):Pn(r),P(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e}):t[n]=i}}function Hr(e,t,n){un(p(e)?e.map(e=>e.bind(t.proxy)):e.bind(t.proxy),t,n)}function Ur(e,t,n,r){let i=r.includes(`.`)?Bn(n,r):()=>n[r];if(v(e)){let n=t[e];_(n)&&Ln(i,n)}else if(_(e))Ln(i,e.bind(n));else if(b(e))if(p(e))e.forEach(e=>Ur(e,t,n,r));else{let r=_(e.handler)?e.handler.bind(n):t[e.handler];_(r)&&Ln(i,r,e)}}function Wr(e){let t=e.type,{mixins:n,extends:r}=t,{mixins:i,optionsCache:a,config:{optionMergeStrategies:o}}=e.appContext,s=a.get(t),c;return s?c=s:!i.length&&!n&&!r?c=t:(c={},i.length&&i.forEach(e=>Gr(c,e,o,!0)),Gr(c,t,o)),b(t)&&a.set(t,c),c}function Gr(e,t,n,r=!1){let{mixins:i,extends:a}=t;a&&Gr(e,a,n,!0),i&&i.forEach(t=>Gr(e,t,n,!0));for(let i in t)if(!(r&&i===`expose`)){let r=Kr[i]||n&&n[i];e[i]=r?r(e[i],t[i]):t[i]}return e}var Kr={data:qr,props:Zr,emits:Zr,methods:Xr,computed:Xr,beforeCreate:R,created:R,beforeMount:R,mounted:R,beforeUpdate:R,updated:R,beforeDestroy:R,beforeUnmount:R,destroyed:R,unmounted:R,activated:R,deactivated:R,errorCaptured:R,serverPrefetch:R,components:Xr,directives:Xr,watch:Qr,provide:qr,inject:Jr};function qr(e,t){return t?e?function(){return l(_(e)?e.call(this,this):e,_(t)?t.call(this,this):t)}:t:e}function Jr(e,t){return Xr(Yr(e),Yr(t))}function Yr(e){if(p(e)){let t={};for(let n=0;nt===`modelValue`||t===`model-value`?e.modelModifiers:e[`${t}Modifiers`]||e[`${O(t)}Modifiers`]||e[`${re(t)}Modifiers`];function ii(e,t,...n){if(e.isUnmounted)return;let i=e.vnode.props||r,a=n,o=t.startsWith(`update:`),s=o&&ri(i,t.slice(7));s&&(s.trim&&(a=n.map(e=>v(e)?e.trim():e)),s.number&&(a=n.map(ce)));let c,l=i[c=ae(t)]||i[c=ae(O(t))];!l&&o&&(l=i[c=ae(re(t))]),l&&un(l,e,6,a);let u=i[c+`Once`];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[c])return;e.emitted[c]=!0,un(u,e,6,a)}}var ai=new WeakMap;function oi(e,t,n=!1){let r=n?ai:t.emitsCache,i=r.get(e);if(i!==void 0)return i;let a=e.emits,o={},s=!1;if(!_(e)){let r=e=>{let n=oi(e,t,!0);n&&(s=!0,l(o,n))};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}return!a&&!s?(b(e)&&r.set(e,null),null):(p(a)?a.forEach(e=>o[e]=null):l(o,a),b(e)&&r.set(e,o),o)}function si(e,t){return!e||!s(t)?!1:(t=t.slice(2).replace(/Once$/,``),f(e,t[0].toLowerCase()+t.slice(1))||f(e,re(t))||f(e,t))}function ci(e){let{type:t,vnode:n,proxy:r,withProxy:i,propsOptions:[a],slots:o,attrs:s,emit:l,render:u,renderCache:d,props:f,data:p,setupState:m,ctx:h,inheritAttrs:g}=e,_=An(e),v,y;try{if(n.shapeFlag&4){let e=i||r,t=e;v=ua(u.call(t,e,d,f,m,p,h)),y=s}else{let e=t;v=ua(e.length>1?e(f,{attrs:s,slots:o,emit:l}):e(f,null)),y=t.props?s:li(s)}}catch(t){Ji.length=0,dn(t,e,1),v=W(B)}let b=v;if(y&&g!==!1){let e=Object.keys(y),{shapeFlag:t}=b;e.length&&t&7&&(a&&e.some(c)&&(y=ui(y,a)),b=sa(b,y,!1,!0))}return n.dirs&&(b=sa(b,null,!1,!0),b.dirs=b.dirs?b.dirs.concat(n.dirs):n.dirs),n.transition&&nr(b,n.transition),v=b,An(_),v}var li=e=>{let t;for(let n in e)(n===`class`||n===`style`||s(n))&&((t||={})[n]=e[n]);return t},ui=(e,t)=>{let n={};for(let r in e)(!c(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function di(e,t,n){let{props:r,children:i,component:a}=e,{props:o,children:s,patchFlag:c}=t,l=a.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?fi(r,o,l):!!o;if(c&8){let e=t.dynamicProps;for(let t=0;tObject.create(hi),_i=e=>Object.getPrototypeOf(e)===hi;function vi(e,t,n,r=!1){let i={},a=gi();e.propsDefaults=Object.create(null),bi(e,t,i,a);for(let t in e.propsOptions[0])t in i||(i[t]=void 0);n?e.props=r?i:zt(i):e.type.props?e.props=i:e.props=a,e.attrs=a}function yi(e,t,n,r){let{props:i,attrs:a,vnode:{patchFlag:o}}=e,s=N(i),[c]=e.propsOptions,l=!1;if((r||o>0)&&!(o&16)){if(o&8){let n=e.vnode.dynamicProps;for(let r=0;r{d=!0;let[n,r]=Ci(e,t,!0);l(c,n),r&&u.push(...r)};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}if(!s&&!d)return b(e)&&a.set(e,i),i;if(p(s))for(let e=0;ee===`_`||e===`_ctx`||e===`$stable`,Ei=e=>p(e)?e.map(ua):[ua(e)],Di=(e,t,n)=>{if(t._n)return t;let r=jn((...e)=>Ei(t(...e)),n);return r._c=!1,r},Oi=(e,t,n)=>{let r=e._ctx;for(let n in e){if(Ti(n))continue;let i=e[n];if(_(i))t[n]=Di(n,i,r);else if(i!=null){let e=Ei(i);t[n]=()=>e}}},ki=(e,t)=>{let n=Ei(t);e.slots.default=()=>n},Ai=(e,t,n)=>{for(let r in t)(n||!Ti(r))&&(e[r]=t[r])},ji=(e,t,n)=>{let r=e.slots=gi();if(e.vnode.shapeFlag&32){let e=t._;e?(Ai(r,t,n),n&&se(r,`_`,e,!0)):Oi(t,r)}else t&&ki(e,t)},Mi=(e,t,n)=>{let{vnode:i,slots:a}=e,o=!0,s=r;if(i.shapeFlag&32){let e=t._;e?n&&e===1?o=!1:Ai(a,t,n):(o=!t.$stable,Oi(t,a)),s=t}else t&&(ki(e,t),s={default:1});if(o)for(let e in a)!Ti(e)&&s[e]==null&&delete a[e]},Ni=Gi;function Pi(e){return Fi(e)}function Fi(e,t){let n=de();n.__VUE__=!0;let{insert:o,remove:s,patchProp:c,createElement:l,createText:u,createComment:d,setText:f,setElementText:p,parentNode:m,nextSibling:h,setScopeId:g=a,insertStaticContent:_}=e,v=(e,t,n,r=null,i=null,a=null,o=void 0,s=null,c=!!t.dynamicChildren)=>{if(e===t)return;e&&!na(e,t)&&(r=ye(e),me(e,i,a,!0),e=null),t.patchFlag===-2&&(c=!1,t.dynamicChildren=null);let{type:l,ref:u,shapeFlag:d}=t;switch(l){case Ki:y(e,t,n,r);break;case B:b(e,t,n,r);break;case qi:e??x(t,n,r,o);break;case z:re(e,t,n,r,i,a,o,s,c);break;default:d&1?w(e,t,n,r,i,a,o,s,c):d&6?ie(e,t,n,r,i,a,o,s,c):(d&64||d&128)&&l.process(e,t,n,r,i,a,o,s,c,Se)}u!=null&&i?cr(u,e&&e.ref,a,t||e,!t):u==null&&e&&e.ref!=null&&cr(e.ref,null,a,e,!0)},y=(e,t,n,r)=>{if(e==null)o(t.el=u(t.children),n,r);else{let n=t.el=e.el;t.children!==e.children&&f(n,t.children)}},b=(e,t,n,r)=>{e==null?o(t.el=d(t.children||``),n,r):t.el=e.el},x=(e,t,n,r)=>{[e.el,e.anchor]=_(e.children,t,n,r,e.el,e.anchor)},S=({el:e,anchor:t},n,r)=>{let i;for(;e&&e!==t;)i=h(e),o(e,n,r),e=i;o(t,n,r)},C=({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=h(e),s(e),e=n;s(t)},w=(e,t,n,r,i,a,o,s,c)=>{if(t.type===`svg`?o=`svg`:t.type===`math`&&(o=`mathml`),e==null)T(t,n,r,i,a,o,s,c);else{let n=e.el&&e.el._isVueCE?e.el:null;try{n&&n._beginPatch(),te(e,t,i,a,o,s,c)}finally{n&&n._endPatch()}}},T=(e,t,n,r,i,a,s,u)=>{let d,f,{props:m,shapeFlag:h,transition:g,dirs:_}=e;if(d=e.el=l(e.type,a,m&&m.is,m),h&8?p(d,e.children):h&16&&D(e.children,d,null,r,i,Ii(e,a),s,u),_&&Mn(e,null,r,`created`),E(d,e,e.scopeId,s,r),m){for(let e in m)e!==`value`&&!ee(e)&&c(d,e,null,m[e],a,r);`value`in m&&c(d,`value`,null,m.value,a),(f=m.onVnodeBeforeMount)&&ma(f,r,e)}_&&Mn(e,null,r,`beforeMount`);let v=Ri(i,g);v&&g.beforeEnter(d),o(d,t,n),((f=m&&m.onVnodeMounted)||v||_)&&Ni(()=>{f&&ma(f,r,e),v&&g.enter(d),_&&Mn(e,null,r,`mounted`)},i)},E=(e,t,n,r,i)=>{if(n&&g(e,n),r)for(let t=0;t{for(let l=c;l{let l=t.el=e.el,{patchFlag:u,dynamicChildren:d,dirs:f}=t;u|=e.patchFlag&16;let m=e.props||r,h=t.props||r,g;if(n&&Li(n,!1),(g=h.onVnodeBeforeUpdate)&&ma(g,n,t,e),f&&Mn(t,e,n,`beforeUpdate`),n&&Li(n,!0),(m.innerHTML&&h.innerHTML==null||m.textContent&&h.textContent==null)&&p(l,``),d?O(e.dynamicChildren,d,l,n,i,Ii(t,a),o):s||le(e,t,l,null,n,i,Ii(t,a),o,!1),u>0){if(u&16)ne(l,m,h,n,a);else if(u&2&&m.class!==h.class&&c(l,`class`,null,h.class,a),u&4&&c(l,`style`,m.style,h.style,a),u&8){let e=t.dynamicProps;for(let t=0;t{g&&ma(g,n,t,e),f&&Mn(t,e,n,`updated`)},i)},O=(e,t,n,r,i,a,o)=>{for(let s=0;s{if(t!==n){if(t!==r)for(let r in t)!ee(r)&&!(r in n)&&c(e,r,t[r],null,a,i);for(let r in n){if(ee(r))continue;let o=n[r],s=t[r];o!==s&&r!==`value`&&c(e,r,s,o,a,i)}`value`in n&&c(e,`value`,t.value,n.value,a)}},re=(e,t,n,r,i,a,s,c,l)=>{let d=t.el=e?e.el:u(``),f=t.anchor=e?e.anchor:u(``),{patchFlag:p,dynamicChildren:m,slotScopeIds:h}=t;h&&(c=c?c.concat(h):h),e==null?(o(d,n,r),o(f,n,r),D(t.children||[],n,f,i,a,s,c,l)):p>0&&p&64&&m&&e.dynamicChildren&&e.dynamicChildren.length===m.length?(O(e.dynamicChildren,m,n,i,a,s,c),(t.key!=null||i&&t===i.subTree)&&zi(e,t,!0)):le(e,t,n,f,i,a,s,c,l)},ie=(e,t,n,r,i,a,o,s,c)=>{t.slotScopeIds=s,e==null?t.shapeFlag&512?i.ctx.activate(t,n,r,o,c):ae(t,n,r,i,a,o,c):k(e,t,c)},ae=(e,t,n,r,i,a,o)=>{let s=e.component=_a(e,r,i);if(dr(e)&&(s.ctx.renderer=Se),Ta(s,!1,o),s.asyncDep){if(i&&i.registerDep(s,se,o),!e.el){let r=s.subTree=W(B);b(null,r,t,n),e.placeholder=r.el}}else se(s,e,t,n,i,a,o)},k=(e,t,n)=>{let r=t.component=e.component;if(di(e,t,n))if(r.asyncDep&&!r.asyncResolved){ce(r,t,n);return}else r.next=t,r.update();else t.el=e.el,r.vnode=t},se=(e,t,n,r,i,a,o)=>{let s=()=>{if(e.isMounted){let{next:t,bu:n,u:r,parent:s,vnode:c}=e;{let n=Vi(e);if(n){t&&(t.el=c.el,ce(e,t,o)),n.asyncDep.then(()=>{Ni(()=>{e.isUnmounted||l()},i)});return}}let u=t,d;Li(e,!1),t?(t.el=c.el,ce(e,t,o)):t=c,n&&oe(n),(d=t.props&&t.props.onVnodeBeforeUpdate)&&ma(d,s,t,c),Li(e,!0);let f=ci(e),p=e.subTree;e.subTree=f,v(p,f,m(p.el),ye(p),e,i,a),t.el=f.el,u===null&&mi(e,f.el),r&&Ni(r,i),(d=t.props&&t.props.onVnodeUpdated)&&Ni(()=>ma(d,s,t,c),i)}else{let o,{el:s,props:c}=t,{bm:l,m:u,parent:d,root:f,type:p}=e,m=ur(t);if(Li(e,!1),l&&oe(l),!m&&(o=c&&c.onVnodeBeforeMount)&&ma(o,d,t),Li(e,!0),s&&we){let t=()=>{e.subTree=ci(e),we(s,e.subTree,e,i,null)};m&&p.__asyncHydrate?p.__asyncHydrate(s,e,t):t()}else{f.ce&&f.ce._hasShadowRoot()&&f.ce._injectChildStyle(p,e.parent?e.parent.type:void 0);let o=e.subTree=ci(e);v(null,o,n,r,e,i,a),t.el=o.el}if(u&&Ni(u,i),!m&&(o=c&&c.onVnodeMounted)){let e=t;Ni(()=>ma(o,d,e),i)}(t.shapeFlag&256||d&&ur(d.vnode)&&d.vnode.shapeFlag&256)&&e.a&&Ni(e.a,i),e.isMounted=!0,t=n=r=null}};e.scope.on();let c=e.effect=new je(s);e.scope.off();let l=e.update=c.run.bind(c),u=e.job=c.runIfDirty.bind(c);u.i=e,u.id=e.uid,c.scheduler=()=>xn(u),Li(e,!0),l()},ce=(e,t,n)=>{t.component=e;let r=e.vnode.props;e.vnode=t,e.next=null,yi(e,t.props,r,n),Mi(e,t.children,n),Ke(),wn(e),qe()},le=(e,t,n,r,i,a,o,s,c=!1)=>{let l=e&&e.children,u=e?e.shapeFlag:0,d=t.children,{patchFlag:f,shapeFlag:m}=t;if(f>0){if(f&128){fe(l,d,n,r,i,a,o,s,c);return}else if(f&256){ue(l,d,n,r,i,a,o,s,c);return}}m&8?(u&16&&ve(l,i,a),d!==l&&p(n,d)):u&16?m&16?fe(l,d,n,r,i,a,o,s,c):ve(l,i,a,!0):(u&8&&p(n,``),m&16&&D(d,n,r,i,a,o,s,c))},ue=(e,t,n,r,a,o,s,c,l)=>{e||=i,t||=i;let u=e.length,d=t.length,f=Math.min(u,d),p;for(p=0;pd?ve(e,a,o,!0,!1,f):D(t,n,r,a,o,s,c,l,f)},fe=(e,t,n,r,a,o,s,c,l)=>{let u=0,d=t.length,f=e.length-1,p=d-1;for(;u<=f&&u<=p;){let r=e[u],i=t[u]=l?da(t[u]):ua(t[u]);if(na(r,i))v(r,i,n,null,a,o,s,c,l);else break;u++}for(;u<=f&&u<=p;){let r=e[f],i=t[p]=l?da(t[p]):ua(t[p]);if(na(r,i))v(r,i,n,null,a,o,s,c,l);else break;f--,p--}if(u>f){if(u<=p){let e=p+1,i=ep)for(;u<=f;)me(e[u],a,o,!0),u++;else{let m=u,h=u,g=new Map;for(u=h;u<=p;u++){let e=t[u]=l?da(t[u]):ua(t[u]);e.key!=null&&g.set(e.key,u)}let _,y=0,b=p-h+1,x=!1,S=0,C=Array(b);for(u=0;u=b){me(r,a,o,!0);continue}let i;if(r.key!=null)i=g.get(r.key);else for(_=h;_<=p;_++)if(C[_-h]===0&&na(r,t[_])){i=_;break}i===void 0?me(r,a,o,!0):(C[i-h]=u+1,i>=S?S=i:x=!0,v(r,t[i],n,null,a,o,s,c,l),y++)}let w=x?Bi(C):i;for(_=w.length-1,u=b-1;u>=0;u--){let e=h+u,i=t[e],f=t[e+1],p=e+1{let{el:a,type:c,transition:l,children:u,shapeFlag:d}=e;if(d&6){pe(e.component.subTree,t,n,r);return}if(d&128){e.suspense.move(t,n,r);return}if(d&64){c.move(e,t,n,Se);return}if(c===z){o(a,t,n);for(let e=0;el.enter(a),i);else{let{leave:r,delayLeave:i,afterLeave:c}=l,u=()=>{e.ctx.isUnmounted?s(a):o(a,t,n)},d=()=>{a._isLeaving&&a[Un](!0),r(a,()=>{u(),c&&c()})};i?i(a,u,d):d()}else o(a,t,n)},me=(e,t,n,r=!1,i=!1)=>{let{type:a,props:o,ref:s,children:c,dynamicChildren:l,shapeFlag:u,patchFlag:d,dirs:f,cacheIndex:p}=e;if(d===-2&&(i=!1),s!=null&&(Ke(),cr(s,null,n,e,!0),qe()),p!=null&&(t.renderCache[p]=void 0),u&256){t.ctx.deactivate(e);return}let m=u&1&&f,h=!ur(e),g;if(h&&(g=o&&o.onVnodeBeforeUnmount)&&ma(g,t,e),u&6)_e(e.component,n,r);else{if(u&128){e.suspense.unmount(n,r);return}m&&Mn(e,null,t,`beforeUnmount`),u&64?e.type.remove(e,t,n,Se,r):l&&!l.hasOnce&&(a!==z||d>0&&d&64)?ve(l,t,n,!1,!0):(a===z&&d&384||!i&&u&16)&&ve(c,t,n),r&&he(e)}(h&&(g=o&&o.onVnodeUnmounted)||m)&&Ni(()=>{g&&ma(g,t,e),m&&Mn(e,null,t,`unmounted`)},n)},he=e=>{let{type:t,el:n,anchor:r,transition:i}=e;if(t===z){ge(n,r);return}if(t===qi){C(e);return}let a=()=>{s(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(e.shapeFlag&1&&i&&!i.persisted){let{leave:t,delayLeave:r}=i,o=()=>t(n,a);r?r(e.el,a,o):o()}else a()},ge=(e,t)=>{let n;for(;e!==t;)n=h(e),s(e),e=n;s(t)},_e=(e,t,n)=>{let{bum:r,scope:i,job:a,subTree:o,um:s,m:c,a:l}=e;Hi(c),Hi(l),r&&oe(r),i.stop(),a&&(a.flags|=8,me(o,e,t,n)),s&&Ni(s,t),Ni(()=>{e.isUnmounted=!0},t)},ve=(e,t,n,r=!1,i=!1,a=0)=>{for(let o=a;o{if(e.shapeFlag&6)return ye(e.component.subTree);if(e.shapeFlag&128)return e.suspense.next();let t=h(e.anchor||e.el),n=t&&t[Vn];return n?h(n):t},be=!1,xe=(e,t,n)=>{let r;e==null?t._vnode&&(me(t._vnode,null,null,!0),r=t._vnode.component):v(t._vnode||null,e,t,null,null,null,n),t._vnode=e,be||=(be=!0,wn(r),Tn(),!1)},Se={p:v,um:me,m:pe,r:he,mt:ae,mc:D,pc:le,pbc:O,n:ye,o:e},Ce,we;return t&&([Ce,we]=t(Se)),{render:xe,hydrate:Ce,createApp:ti(xe,Ce)}}function Ii({type:e,props:t},n){return n===`svg`&&e===`foreignObject`||n===`mathml`&&e===`annotation-xml`&&t&&t.encoding&&t.encoding.includes(`html`)?void 0:n}function Li({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Ri(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function zi(e,t,n=!1){let r=e.children,i=t.children;if(p(r)&&p(i))for(let e=0;e>1,e[n[s]]0&&(t[r]=n[a-1]),n[a]=r)}}for(a=n.length,o=n[a-1];a-- >0;)n[a]=o,o=t[o];return n}function Vi(e){let t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Vi(t)}function Hi(e){if(e)for(let t=0;te.__isSuspense;function Gi(e,t){t&&t.pendingBranch?p(e)?t.effects.push(...e):t.effects.push(e):Cn(e)}var z=Symbol.for(`v-fgt`),Ki=Symbol.for(`v-txt`),B=Symbol.for(`v-cmt`),qi=Symbol.for(`v-stc`),Ji=[],Yi=null;function V(e=!1){Ji.push(Yi=e?null:[])}function Xi(){Ji.pop(),Yi=Ji[Ji.length-1]||null}var Zi=1;function Qi(e,t=!1){Zi+=e,e<0&&Yi&&t&&(Yi.hasOnce=!0)}function $i(e){return e.dynamicChildren=Zi>0?Yi||i:null,Xi(),Zi>0&&Yi&&Yi.push(e),e}function H(e,t,n,r,i,a){return $i(U(e,t,n,r,i,a,!0))}function ea(e,t,n,r,i){return $i(W(e,t,n,r,i,!0))}function ta(e){return e?e.__v_isVNode===!0:!1}function na(e,t){return e.type===t.type&&e.key===t.key}var ra=({key:e})=>e??null,ia=({ref:e,ref_key:t,ref_for:n})=>(typeof e==`number`&&(e=``+e),e==null?null:v(e)||P(e)||_(e)?{i:On,r:e,k:t,f:!!n}:e);function U(e,t=null,n=null,r=0,i=null,a=e===z?0:1,o=!1,s=!1){let c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ra(t),ref:t&&ia(t),scopeId:kn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:a,patchFlag:r,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:On};return s?(fa(c,n),a&128&&e.normalize(c)):n&&(c.shapeFlag|=v(n)?8:16),Zi>0&&!o&&Yi&&(c.patchFlag>0||a&6)&&c.patchFlag!==32&&Yi.push(c),c}var W=aa;function aa(e,t=null,n=null,r=0,i=null,a=!1){if((!e||e===Ar)&&(e=B),ta(e)){let r=sa(e,t,!0);return n&&fa(r,n),Zi>0&&!a&&Yi&&(r.shapeFlag&6?Yi[Yi.indexOf(e)]=r:Yi.push(r)),r.patchFlag=-2,r}if(Fa(e)&&(e=e.__vccOpts),t){t=oa(t);let{class:e,style:n}=t;e&&!v(e)&&(t.class=_e(e)),b(n)&&(Gt(n)&&!p(n)&&(n=l({},n)),t.style=fe(n))}let o=v(e)?1:Wi(e)?128:Hn(e)?64:b(e)?4:_(e)?2:0;return U(e,t,n,r,i,o,a,!0)}function oa(e){return e?Gt(e)||_i(e)?l({},e):e:null}function sa(e,t,n=!1,r=!1){let{props:i,ref:a,patchFlag:o,children:s,transition:c}=e,l=t?pa(i||{},t):i,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&ra(l),ref:t&&t.ref?n&&a?p(a)?a.concat(ia(t)):[a,ia(t)]:ia(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:s,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==z?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:c,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&sa(e.ssContent),ssFallback:e.ssFallback&&sa(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return c&&r&&nr(u,c.clone(u)),u}function ca(e=` `,t=0){return W(Ki,null,e,t)}function la(e,t){let n=W(qi,null,e);return n.staticCount=t,n}function G(e=``,t=!1){return t?(V(),ea(B,null,e)):W(B,null,e)}function ua(e){return e==null||typeof e==`boolean`?W(B):p(e)?W(z,null,e.slice()):ta(e)?da(e):W(Ki,null,String(e))}function da(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:sa(e)}function fa(e,t){let n=0,{shapeFlag:r}=e;if(t==null)t=null;else if(p(t))n=16;else if(typeof t==`object`)if(r&65){let n=t.default;n&&(n._c&&(n._d=!1),fa(e,n()),n._c&&(n._d=!0));return}else{n=32;let r=t._;!r&&!_i(t)?t._ctx=On:r===3&&On&&(On.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else _(t)?(t={default:t,_ctx:On},n=32):(t=String(t),r&64?(n=16,t=[ca(t)]):n=8);e.children=t,e.shapeFlag|=n}function pa(...e){let t={};for(let n=0;nK||On,ya,ba;{let e=de(),t=(t,n)=>{let r;return(r=e[t])||(r=e[t]=[]),r.push(n),e=>{r.length>1?r.forEach(t=>t(e)):r[0](e)}};ya=t(`__VUE_INSTANCE_SETTERS__`,e=>K=e),ba=t(`__VUE_SSR_SETTERS__`,e=>wa=e)}var xa=e=>{let t=K;return ya(e),e.scope.on(),()=>{e.scope.off(),ya(t)}},Sa=()=>{K&&K.scope.off(),ya(null)};function Ca(e){return e.vnode.shapeFlag&4}var wa=!1;function Ta(e,t=!1,n=!1){t&&ba(t);let{props:r,children:i}=e.vnode,a=Ca(e);vi(e,r,a,t),ji(e,i,n||t);let o=a?Ea(e,t):void 0;return t&&ba(!1),o}function Ea(e,t){let n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,Lr);let{setup:r}=n;if(r){Ke();let n=e.setupContext=r.length>1?Ma(e):null,i=xa(e),a=ln(r,e,0,[e.props,n]),o=x(a);if(qe(),i(),(o||e.sp)&&!ur(e)&&ar(e),o){if(a.then(Sa,Sa),t)return a.then(n=>{Da(e,n,t)}).catch(t=>{dn(t,e,0)});e.asyncDep=a}else Da(e,a,t)}else Aa(e,t)}function Da(e,t,n){_(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:b(t)&&(e.setupState=$t(t)),Aa(e,n)}var Oa,ka;function Aa(e,t,n){let r=e.type;if(!e.render){if(!t&&Oa&&!r.render){let t=r.template||Wr(e).template;if(t){let{isCustomElement:n,compilerOptions:i}=e.appContext.config,{delimiters:a,compilerOptions:o}=r;r.render=Oa(t,l(l({isCustomElement:n,delimiters:a},i),o))}}e.render=r.render||a,ka&&ka(e)}{let t=xa(e);Ke();try{Br(e)}finally{qe(),t()}}}var ja={get(e,t){return M(e,`get`,``),e[t]}};function Ma(e){return{attrs:new Proxy(e.attrs,ja),slots:e.slots,emit:e.emit,expose:t=>{e.exposed=t||{}}}}function Na(e){return e.exposed?e.exposeProxy||=new Proxy($t(Kt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Fr)return Fr[n](e)},has(e,t){return t in e||t in Fr}}):e.proxy}function Pa(e,t=!0){return _(e)?e.displayName||e.name:e.name||t&&e.__name}function Fa(e){return _(e)&&`__vccOpts`in e}var Ia=(e,t)=>tn(e,t,wa);function La(e,t,n){try{Qi(-1);let r=arguments.length;return r===2?b(t)&&!p(t)?ta(t)?W(e,null,[t]):W(e,t):W(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&ta(n)&&(n=[n]),W(e,t,n))}finally{Qi(1)}}var Ra=`3.5.30`,za=void 0,Ba=typeof window<`u`&&window.trustedTypes;if(Ba)try{za=Ba.createPolicy(`vue`,{createHTML:e=>e})}catch{}var Va=za?e=>za.createHTML(e):e=>e,Ha=`http://www.w3.org/2000/svg`,Ua=`http://www.w3.org/1998/Math/MathML`,Wa=typeof document<`u`?document:null,Ga=Wa&&Wa.createElement(`template`),Ka={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{let t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{let i=t===`svg`?Wa.createElementNS(Ha,e):t===`mathml`?Wa.createElementNS(Ua,e):n?Wa.createElement(e,{is:n}):Wa.createElement(e);return e===`select`&&r&&r.multiple!=null&&i.setAttribute(`multiple`,r.multiple),i},createText:e=>Wa.createTextNode(e),createComment:e=>Wa.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Wa.querySelector(e),setScopeId(e,t){e.setAttribute(t,``)},insertStaticContent(e,t,n,r,i,a){let o=n?n.previousSibling:t.lastChild;if(i&&(i===a||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{Ga.innerHTML=Va(r===`svg`?`${e}`:r===`mathml`?`${e}`:e);let i=Ga.content;if(r===`svg`||r===`mathml`){let e=i.firstChild;for(;e.firstChild;)i.appendChild(e.firstChild);i.removeChild(e)}t.insertBefore(i,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},qa=`transition`,Ja=`animation`,Ya=Symbol(`_vtc`),Xa={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},Za=l({},qn,Xa),Qa=(e=>(e.displayName=`Transition`,e.props=Za,e))((e,{slots:t})=>La(Zn,to(e),t)),$a=(e,t=[])=>{p(e)?e.forEach(e=>e(...t)):e&&e(...t)},eo=e=>e?p(e)?e.some(e=>e.length>1):e.length>1:!1;function to(e){let t={};for(let n in e)n in Xa||(t[n]=e[n]);if(e.css===!1)return t;let{name:n=`v`,type:r,duration:i,enterFromClass:a=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:u=o,appearToClass:d=s,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=e,h=no(i),g=h&&h[0],_=h&&h[1],{onBeforeEnter:v,onEnter:y,onEnterCancelled:b,onLeave:x,onLeaveCancelled:S,onBeforeAppear:C=v,onAppear:w=y,onAppearCancelled:T=b}=t,E=(e,t,n,r)=>{e._enterCancelled=r,ao(e,t?d:s),ao(e,t?u:o),n&&n()},ee=(e,t)=>{e._isLeaving=!1,ao(e,f),ao(e,m),ao(e,p),t&&t()},D=e=>(t,n)=>{let i=e?w:y,o=()=>E(t,e,n);$a(i,[t,o]),oo(()=>{ao(t,e?c:a),io(t,e?d:s),eo(i)||co(t,r,g,o)})};return l(t,{onBeforeEnter(e){$a(v,[e]),io(e,a),io(e,o)},onBeforeAppear(e){$a(C,[e]),io(e,c),io(e,u)},onEnter:D(!1),onAppear:D(!0),onLeave(e,t){e._isLeaving=!0;let n=()=>ee(e,t);io(e,f),e._enterCancelled?(io(e,p),po(e)):(po(e),io(e,p)),oo(()=>{e._isLeaving&&(ao(e,f),io(e,m),eo(x)||co(e,r,_,n))}),$a(x,[e,n])},onEnterCancelled(e){E(e,!1,void 0,!0),$a(b,[e])},onAppearCancelled(e){E(e,!0,void 0,!0),$a(T,[e])},onLeaveCancelled(e){ee(e),$a(S,[e])}})}function no(e){if(e==null)return null;if(b(e))return[ro(e.enter),ro(e.leave)];{let t=ro(e);return[t,t]}}function ro(e){return le(e)}function io(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.add(t)),(e[Ya]||(e[Ya]=new Set)).add(t)}function ao(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.remove(t));let n=e[Ya];n&&(n.delete(t),n.size||(e[Ya]=void 0))}function oo(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}var so=0;function co(e,t,n,r){let i=e._endId=++so,a=()=>{i===e._endId&&r()};if(n!=null)return setTimeout(a,n);let{type:o,timeout:s,propCount:c}=lo(e,t);if(!o)return r();let l=o+`end`,u=0,d=()=>{e.removeEventListener(l,f),a()},f=t=>{t.target===e&&++u>=c&&d()};setTimeout(()=>{u(n[e]||``).split(`, `),i=r(`${qa}Delay`),a=r(`${qa}Duration`),o=uo(i,a),s=r(`${Ja}Delay`),c=r(`${Ja}Duration`),l=uo(s,c),u=null,d=0,f=0;t===qa?o>0&&(u=qa,d=o,f=a.length):t===Ja?l>0&&(u=Ja,d=l,f=c.length):(d=Math.max(o,l),u=d>0?o>l?qa:Ja:null,f=u?u===qa?a.length:c.length:0);let p=u===qa&&/\b(?:transform|all)(?:,|$)/.test(r(`${qa}Property`).toString());return{type:u,timeout:d,propCount:f,hasTransform:p}}function uo(e,t){for(;e.lengthfo(t)+fo(e[n])))}function fo(e){return e===`auto`?0:Number(e.slice(0,-1).replace(`,`,`.`))*1e3}function po(e){return(e?e.ownerDocument:document).body.offsetHeight}function mo(e,t,n){let r=e[Ya];r&&(t=(t?[t,...r]:[...r]).join(` `)),t==null?e.removeAttribute(`class`):n?e.setAttribute(`class`,t):e.className=t}var ho=Symbol(`_vod`),go=Symbol(`_vsh`),_o=Symbol(``),vo=/(?:^|;)\s*display\s*:/;function yo(e,t,n){let r=e.style,i=v(n),a=!1;if(n&&!i){if(t)if(v(t))for(let e of t.split(`;`)){let t=e.slice(0,e.indexOf(`:`)).trim();n[t]??xo(r,t,``)}else for(let e in t)n[e]??xo(r,e,``);for(let e in n)e===`display`&&(a=!0),xo(r,e,n[e])}else if(i){if(t!==n){let e=r[_o];e&&(n+=`;`+e),r.cssText=n,a=vo.test(n)}}else t&&e.removeAttribute(`style`);ho in e&&(e[ho]=a?r.display:``,e[go]&&(r.display=`none`))}var bo=/\s*!important$/;function xo(e,t,n){if(p(n))n.forEach(n=>xo(e,t,n));else if(n??=``,t.startsWith(`--`))e.setProperty(t,n);else{let r=wo(e,t);bo.test(n)?e.setProperty(re(r),n.replace(bo,``),`important`):e[r]=n}}var So=[`Webkit`,`Moz`,`ms`],Co={};function wo(e,t){let n=Co[t];if(n)return n;let r=O(t);if(r!==`filter`&&r in e)return Co[t]=r;r=ie(r);for(let n=0;nPo||=(Fo.then(()=>Po=0),Date.now());function Lo(e,t){let n=e=>{if(!e._vts)e._vts=Date.now();else if(e._vts<=n.attached)return;un(Ro(e,n.value),t,5,[e])};return n.value=e,n.attached=Io(),n}function Ro(e,t){if(p(t)){let n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(e=>t=>!t._stopped&&e&&e(t))}else return t}var zo=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Bo=(e,t,n,r,i,a)=>{let o=i===`svg`;t===`class`?mo(e,r,o):t===`style`?yo(e,n,r):s(t)?c(t)||jo(e,t,n,r,a):(t[0]===`.`?(t=t.slice(1),!0):t[0]===`^`?(t=t.slice(1),!1):Vo(e,t,r,o))?(Do(e,t,r),!e.tagName.includes(`-`)&&(t===`value`||t===`checked`||t===`selected`)&&Eo(e,t,r,o,a,t!==`value`)):e._isVueCE&&(Ho(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!v(r)))?Do(e,O(t),r,a,t):(t===`true-value`?e._trueValue=r:t===`false-value`&&(e._falseValue=r),Eo(e,t,r,o))};function Vo(e,t,n,r){if(r)return!!(t===`innerHTML`||t===`textContent`||t in e&&zo(t)&&_(n));if(t===`spellcheck`||t===`draggable`||t===`translate`||t===`autocorrect`||t===`sandbox`&&e.tagName===`IFRAME`||t===`form`||t===`list`&&e.tagName===`INPUT`||t===`type`&&e.tagName===`TEXTAREA`)return!1;if(t===`width`||t===`height`){let t=e.tagName;if(t===`IMG`||t===`VIDEO`||t===`CANVAS`||t===`SOURCE`)return!1}return zo(t)&&v(n)?!1:t in e}function Ho(e,t){let n=e._def.props;if(!n)return!1;let r=O(t);return Array.isArray(n)?n.some(e=>O(e)===r):Object.keys(n).some(e=>O(e)===r)}var Uo=e=>{let t=e.props[`onUpdate:modelValue`]||!1;return p(t)?e=>oe(t,e):t};function Wo(e){e.target.composing=!0}function Go(e){let t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event(`input`)))}var Ko=Symbol(`_assign`);function qo(e,t,n){return t&&(e=e.trim()),n&&(e=ce(e)),e}var Jo={created(e,{modifiers:{lazy:t,trim:n,number:r}},i){e[Ko]=Uo(i);let a=r||i.props&&i.props.type===`number`;Oo(e,t?`change`:`input`,t=>{t.target.composing||e[Ko](qo(e.value,n,a))}),(n||a)&&Oo(e,`change`,()=>{e.value=qo(e.value,n,a)}),t||(Oo(e,`compositionstart`,Wo),Oo(e,`compositionend`,Go),Oo(e,`change`,Go))},mounted(e,{value:t}){e.value=t??``},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:i,number:a}},o){if(e[Ko]=Uo(o),e.composing)return;let s=(a||e.type===`number`)&&!/^0\d/.test(e.value)?ce(e.value):e.value,c=t??``;s!==c&&(document.activeElement===e&&e.type!==`range`&&(r&&t===n||i&&e.value.trim()===c)||(e.value=c))}},Yo={deep:!0,created(e,t,n){e[Ko]=Uo(n),Oo(e,`change`,()=>{let t=e._modelValue,n=$o(e),r=e.checked,i=e[Ko];if(p(t)){let e=Ce(t,n),a=e!==-1;if(r&&!a)i(t.concat(n));else if(!r&&a){let n=[...t];n.splice(e,1),i(n)}}else if(h(t)){let e=new Set(t);r?e.add(n):e.delete(n),i(e)}else i(es(e,r))})},mounted:Xo,beforeUpdate(e,t,n){e[Ko]=Uo(n),Xo(e,t,n)}};function Xo(e,{value:t,oldValue:n},r){e._modelValue=t;let i;if(p(t))i=Ce(t,r.props.value)>-1;else if(h(t))i=t.has(r.props.value);else{if(t===n)return;i=Se(t,es(e,!0))}e.checked!==i&&(e.checked=i)}var Zo={deep:!0,created(e,{value:t,modifiers:{number:n}},r){let i=h(t);Oo(e,`change`,()=>{let t=Array.prototype.filter.call(e.options,e=>e.selected).map(e=>n?ce($o(e)):$o(e));e[Ko](e.multiple?i?new Set(t):t:t[0]),e._assigning=!0,yn(()=>{e._assigning=!1})}),e[Ko]=Uo(r)},mounted(e,{value:t}){Qo(e,t)},beforeUpdate(e,t,n){e[Ko]=Uo(n)},updated(e,{value:t}){e._assigning||Qo(e,t)}};function Qo(e,t){let n=e.multiple,r=p(t);if(!(n&&!r&&!h(t))){for(let i=0,a=e.options.length;iString(e)===String(o)):a.selected=Ce(t,o)>-1}else a.selected=t.has(o);else if(Se($o(a),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!n&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function $o(e){return`_value`in e?e._value:e.value}function es(e,t){let n=t?`_trueValue`:`_falseValue`;return n in e?e[n]:t}var ts=[`ctrl`,`shift`,`alt`,`meta`],ns={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>`button`in e&&e.button!==0,middle:e=>`button`in e&&e.button!==1,right:e=>`button`in e&&e.button!==2,exact:(e,t)=>ts.some(n=>e[`${n}Key`]&&!t.includes(n))},rs=(e,t)=>{if(!e)return e;let n=e._withMods||={},r=t.join(`.`);return n[r]||(n[r]=((n,...r)=>{for(let e=0;e{let n=e._withKeys||={},r=t.join(`.`);return n[r]||(n[r]=(n=>{if(!(`key`in n))return;let r=re(n.key);if(t.some(e=>e===r||is[e]===r))return e(n)}))},os=l({patchProp:Bo},Ka),ss;function cs(){return ss||=Pi(os)}var ls=((...e)=>{let t=cs().createApp(...e),{mount:n}=t;return t.mount=e=>{let r=ds(e);if(!r)return;let i=t._component;!_(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent=``);let a=n(r,!1,us(r));return r instanceof Element&&(r.removeAttribute(`v-cloak`),r.setAttribute(`data-v-app`,``)),a},t});function us(e){if(e instanceof SVGElement)return`svg`;if(typeof MathMLElement==`function`&&e instanceof MathMLElement)return`mathml`}function ds(e){return v(e)?document.querySelector(e):e}function fs(e,t){return function(){return e.apply(t,arguments)}}var{toString:ps}=Object.prototype,{getPrototypeOf:ms}=Object,{iterator:hs,toStringTag:gs}=Symbol,_s=(e=>t=>{let n=ps.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),vs=e=>(e=e.toLowerCase(),t=>_s(t)===e),ys=e=>t=>typeof t===e,{isArray:bs}=Array,xs=ys(`undefined`);function Ss(e){return e!==null&&!xs(e)&&e.constructor!==null&&!xs(e.constructor)&&Es(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var Cs=vs(`ArrayBuffer`);function ws(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&Cs(e.buffer),t}var Ts=ys(`string`),Es=ys(`function`),Ds=ys(`number`),Os=e=>typeof e==`object`&&!!e,ks=e=>e===!0||e===!1,As=e=>{if(_s(e)!==`object`)return!1;let t=ms(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(gs in e)&&!(hs in e)},js=e=>{if(!Os(e)||Ss(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Ms=vs(`Date`),Ns=vs(`File`),Ps=e=>!!(e&&e.uri!==void 0),Fs=e=>e&&e.getParts!==void 0,Is=vs(`Blob`),Ls=vs(`FileList`),Rs=e=>Os(e)&&Es(e.pipe);function zs(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var Bs=zs(),Vs=Bs.FormData===void 0?void 0:Bs.FormData,Hs=e=>{let t;return e&&(Vs&&e instanceof Vs||Es(e.append)&&((t=_s(e))===`formdata`||t===`object`&&Es(e.toString)&&e.toString()===`[object FormData]`))},Us=vs(`URLSearchParams`),[Ws,Gs,Ks,qs]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(vs),Js=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function Ys(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),bs(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var Zs=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,Qs=e=>!xs(e)&&e!==Zs;function $s(){let{caseless:e,skipUndefined:t}=Qs(this)&&this||{},n={},r=(r,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=e&&Xs(n,i)||i;As(n[a])&&As(r)?n[a]=$s(n[a],r):As(r)?n[a]=$s({},r):bs(r)?n[a]=r.slice():(!t||!xs(r))&&(n[a]=r)};for(let e=0,t=arguments.length;e(Ys(t,(t,r)=>{n&&Es(t)?Object.defineProperty(e,r,{value:fs(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),tc=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),nc=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,`constructor`,{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,`super`,{value:t.prototype}),n&&Object.assign(e.prototype,n)},rc=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-- >0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&ms(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},ic=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},ac=e=>{if(!e)return null;if(bs(e))return e;let t=e.length;if(!Ds(t))return null;let n=Array(t);for(;t-- >0;)n[t]=e[t];return n},oc=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&ms(Uint8Array)),sc=(e,t)=>{let n=(e&&e[hs]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},cc=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},lc=vs(`HTMLFormElement`),uc=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),dc=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),fc=vs(`RegExp`),pc=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};Ys(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},mc=e=>{pc(e,(t,n)=>{if(Es(e)&&[`arguments`,`caller`,`callee`].indexOf(n)!==-1)return!1;let r=e[n];if(Es(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},hc=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return bs(e)?r(e):r(String(e).split(t)),n},gc=()=>{},_c=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function vc(e){return!!(e&&Es(e.append)&&e[gs]===`FormData`&&e[hs])}var yc=e=>{let t=Array(10),n=(e,r)=>{if(Os(e)){if(t.indexOf(e)>=0)return;if(Ss(e))return e;if(!(`toJSON`in e)){t[r]=e;let i=bs(e)?[]:{};return Ys(e,(e,t)=>{let a=n(e,r+1);!xs(a)&&(i[t]=a)}),t[r]=void 0,i}}return e};return n(e,0)},bc=vs(`AsyncFunction`),xc=e=>e&&(Os(e)||Es(e))&&Es(e.then)&&Es(e.catch),Sc=((e,t)=>e?setImmediate:t?((e,t)=>(Zs.addEventListener(`message`,({source:n,data:r})=>{n===Zs&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),Zs.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,Es(Zs.postMessage)),q={isArray:bs,isArrayBuffer:Cs,isBuffer:Ss,isFormData:Hs,isArrayBufferView:ws,isString:Ts,isNumber:Ds,isBoolean:ks,isObject:Os,isPlainObject:As,isEmptyObject:js,isReadableStream:Ws,isRequest:Gs,isResponse:Ks,isHeaders:qs,isUndefined:xs,isDate:Ms,isFile:Ns,isReactNativeBlob:Ps,isReactNative:Fs,isBlob:Is,isRegExp:fc,isFunction:Es,isStream:Rs,isURLSearchParams:Us,isTypedArray:oc,isFileList:Ls,forEach:Ys,merge:$s,extend:ec,trim:Js,stripBOM:tc,inherits:nc,toFlatObject:rc,kindOf:_s,kindOfTest:vs,endsWith:ic,toArray:ac,forEachEntry:sc,matchAll:cc,isHTMLForm:lc,hasOwnProperty:dc,hasOwnProp:dc,reduceDescriptors:pc,freezeMethods:mc,toObjectSet:hc,toCamelCase:uc,noop:gc,toFiniteNumber:_c,findKey:Xs,global:Zs,isContextDefined:Qs,isSpecCompliantForm:vc,toJSONObject:yc,isAsyncFn:bc,isThenable:xc,setImmediate:Sc,asap:typeof queueMicrotask<`u`?queueMicrotask.bind(Zs):typeof process<`u`&&process.nextTick||Sc,isIterable:e=>e!=null&&Es(e[hs])},J=class e extends Error{static from(t,n,r,i,a,o){let s=new e(t.message,n||t.code,r,i,a);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),o&&Object.assign(s,o),s}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,`message`,{value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:q.toJSONObject(this.config),code:this.code,status:this.status}}};J.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,J.ERR_BAD_OPTION=`ERR_BAD_OPTION`,J.ECONNABORTED=`ECONNABORTED`,J.ETIMEDOUT=`ETIMEDOUT`,J.ERR_NETWORK=`ERR_NETWORK`,J.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,J.ERR_DEPRECATED=`ERR_DEPRECATED`,J.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,J.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,J.ERR_CANCELED=`ERR_CANCELED`,J.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,J.ERR_INVALID_URL=`ERR_INVALID_URL`;function Cc(e){return q.isPlainObject(e)||q.isArray(e)}function wc(e){return q.endsWith(e,`[]`)?e.slice(0,-2):e}function Tc(e,t,n){return e?e.concat(t).map(function(e,t){return e=wc(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function Ec(e){return q.isArray(e)&&!e.some(Cc)}var Dc=q.toFlatObject(q,{},null,function(e){return/^is[A-Z]/.test(e)});function Oc(e,t,n){if(!q.isObject(e))throw TypeError(`target must be an object`);t||=new FormData,n=q.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!q.isUndefined(t[e])});let r=n.metaTokens,i=n.visitor||l,a=n.dots,o=n.indexes,s=(n.Blob||typeof Blob<`u`&&Blob)&&q.isSpecCompliantForm(t);if(!q.isFunction(i))throw TypeError(`visitor must be a function`);function c(e){if(e===null)return``;if(q.isDate(e))return e.toISOString();if(q.isBoolean(e))return e.toString();if(!s&&q.isBlob(e))throw new J(`Blob is not supported. Use a Buffer instead.`);return q.isArrayBuffer(e)||q.isTypedArray(e)?s&&typeof Blob==`function`?new Blob([e]):Buffer.from(e):e}function l(e,n,i){let s=e;if(q.isReactNative(t)&&q.isReactNativeBlob(e))return t.append(Tc(i,n,a),c(e)),!1;if(e&&!i&&typeof e==`object`){if(q.endsWith(n,`{}`))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(q.isArray(e)&&Ec(e)||(q.isFileList(e)||q.endsWith(n,`[]`))&&(s=q.toArray(e)))return n=wc(n),s.forEach(function(e,r){!(q.isUndefined(e)||e===null)&&t.append(o===!0?Tc([n],r,a):o===null?n:n+`[]`,c(e))}),!1}return Cc(e)?!0:(t.append(Tc(i,n,a),c(e)),!1)}let u=[],d=Object.assign(Dc,{defaultVisitor:l,convertValue:c,isVisitable:Cc});function f(e,n){if(!q.isUndefined(e)){if(u.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));u.push(e),q.forEach(e,function(e,r){(!(q.isUndefined(e)||e===null)&&i.call(t,e,q.isString(r)?r.trim():r,n,d))===!0&&f(e,n?n.concat(r):[r])}),u.pop()}}if(!q.isObject(e))throw TypeError(`data must be an object`);return f(e),t}function kc(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`,"%00":`\0`};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function Ac(e,t){this._pairs=[],e&&Oc(e,this,t)}var jc=Ac.prototype;jc.append=function(e,t){this._pairs.push([e,t])},jc.toString=function(e){let t=e?function(t){return e.call(this,t,kc)}:kc;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Mc(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function Nc(e,t,n){if(!t)return e;let r=n&&n.encode||Mc,i=q.isFunction(n)?{serialize:n}:n,a=i&&i.serialize,o;if(o=a?a(t,i):q.isURLSearchParams(t)?t.toString():new Ac(t,i).toString(r),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var Pc=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&=[]}forEach(e){q.forEach(this.handlers,function(t){t!==null&&e(t)})}},Fc={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},Ic={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Ac,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},Lc=t({hasBrowserEnv:()=>Rc,hasStandardBrowserEnv:()=>Bc,hasStandardBrowserWebWorkerEnv:()=>Vc,navigator:()=>zc,origin:()=>Hc}),Rc=typeof window<`u`&&typeof document<`u`,zc=typeof navigator==`object`&&navigator||void 0,Bc=Rc&&(!zc||[`ReactNative`,`NativeScript`,`NS`].indexOf(zc.product)<0),Vc=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Hc=Rc&&window.location.href||`http://localhost`,Y={...Lc,...Ic};function Uc(e,t){return Oc(e,new Y.classes.URLSearchParams,{visitor:function(e,t,n,r){return Y.isNode&&q.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}function Wc(e){return q.matchAll(/\w+|\[(\w*)]/g,e).map(e=>e[0]===`[]`?``:e[1]||e[0])}function Gc(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&q.isArray(r)?r.length:a,s?(q.hasOwnProp(r,a)?r[a]=[r[a],n]:r[a]=n,!o):((!r[a]||!q.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&q.isArray(r[a])&&(r[a]=Gc(r[a])),!o)}if(q.isFormData(e)&&q.isFunction(e.entries)){let n={};return q.forEachEntry(e,(e,r)=>{t(Wc(e),r,n,0)}),n}return null}function qc(e,t,n){if(q.isString(e))try{return(t||JSON.parse)(e),q.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Jc={transitional:Fc,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=q.isObject(e);if(i&&q.isHTMLForm(e)&&(e=new FormData(e)),q.isFormData(e))return r?JSON.stringify(Kc(e)):e;if(q.isArrayBuffer(e)||q.isBuffer(e)||q.isStream(e)||q.isFile(e)||q.isBlob(e)||q.isReadableStream(e))return e;if(q.isArrayBufferView(e))return e.buffer;if(q.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return Uc(e,this.formSerializer).toString();if((a=q.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let t=this.env&&this.env.FormData;return Oc(a?{"files[]":e}:e,t&&new t,this.formSerializer)}}return i||r?(t.setContentType(`application/json`,!1),qc(e)):e}],transformResponse:[function(e){let t=this.transitional||Jc.transitional,n=t&&t.forcedJSONParsing,r=this.responseType===`json`;if(q.isResponse(e)||q.isReadableStream(e))return e;if(e&&q.isString(e)&&(n&&!this.responseType||r)){let n=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(n)throw e.name===`SyntaxError`?J.from(e,J.ERR_BAD_RESPONSE,this,null,this.response):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Y.classes.FormData,Blob:Y.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};q.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`],e=>{Jc.headers[e]={}});var Yc=q.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),Xc=e=>{let t={},n,r,i;return e&&e.split(` +`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim(),!(!n||t[n]&&Yc[n])&&(n===`set-cookie`?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+`, `+r:r)}),t},Zc=Symbol(`internals`);function Qc(e){return e&&String(e).trim().toLowerCase()}function $c(e){return e===!1||e==null?e:q.isArray(e)?e.map($c):String(e)}function el(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var tl=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function nl(e,t,n,r,i){if(q.isFunction(r))return r.call(this,t,n);if(i&&(t=n),q.isString(t)){if(q.isString(r))return t.indexOf(r)!==-1;if(q.isRegExp(r))return r.test(t)}}function rl(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function il(e,t){let n=q.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var al=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=Qc(t);if(!i)throw Error(`header name must be a non-empty string`);let a=q.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=$c(e))}let a=(e,t)=>q.forEach(e,(e,n)=>i(e,n,t));if(q.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(q.isString(e)&&(e=e.trim())&&!tl(e))a(Xc(e),t);else if(q.isObject(e)&&q.isIterable(e)){let n={},r,i;for(let t of e){if(!q.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);n[i=t[0]]=(r=n[i])?q.isArray(r)?[...r,t[1]]:[r,t[1]]:t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=Qc(e),e){let n=q.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return el(e);if(q.isFunction(t))return t.call(this,e,n);if(q.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=Qc(e),e){let n=q.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||nl(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=Qc(e),e){let i=q.findKey(n,e);i&&(!t||nl(n,n[i],i,t))&&(delete n[i],r=!0)}}return q.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||nl(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return q.forEach(this,(r,i)=>{let a=q.findKey(n,i);if(a){t[a]=$c(r),delete t[i];return}let o=e?rl(i):String(i).trim();o!==i&&delete t[i],t[o]=$c(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return q.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&q.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` +`)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[Zc]=this[Zc]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=Qc(e);t[r]||(il(n,e),t[r]=!0)}return q.isArray(e)?e.forEach(r):r(e),this}};al.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),q.reduceDescriptors(al.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),q.freezeMethods(al);function ol(e,t){let n=this||Jc,r=t||n,i=al.from(r.headers),a=r.data;return q.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function sl(e){return!!(e&&e.__CANCEL__)}var cl=class extends J{constructor(e,t,n){super(e??`canceled`,J.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function ll(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new J(`Request failed with status code `+n.status,[J.ERR_BAD_REQUEST,J.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function ul(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function dl(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var pl=(e,t,n=3)=>{let r=0,i=dl(50,250);return fl(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},ml=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},hl=e=>(...t)=>q.asap(()=>e(...t)),gl=Y.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Y.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Y.origin),Y.navigator&&/(msie|trident)/i.test(Y.navigator.userAgent)):()=>!0,_l=Y.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];q.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),q.isString(r)&&s.push(`path=${r}`),q.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),q.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function vl(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function yl(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function bl(e,t,n){let r=!vl(t);return e&&(r||n==0)?yl(e,t):t}var xl=e=>e instanceof al?{...e}:e;function Sl(e,t){t||={};let n={};function r(e,t,n,r){return q.isPlainObject(e)&&q.isPlainObject(t)?q.merge.call({caseless:r},e,t):q.isPlainObject(t)?q.merge({},t):q.isArray(t)?t.slice():t}function i(e,t,n,i){if(!q.isUndefined(t))return r(e,t,n,i);if(!q.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!q.isUndefined(t))return r(void 0,t)}function o(e,t){if(!q.isUndefined(t))return r(void 0,t);if(!q.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(xl(e),xl(t),n,!0)};return q.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=q.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);q.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Cl=e=>{let t=Sl({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=al.from(o),t.url=Nc(bl(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),q.isFormData(n)){if(Y.hasStandardBrowserEnv||Y.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(q.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(Y.hasStandardBrowserEnv&&(r&&q.isFunction(r)&&(r=r(t)),r||r!==!1&&gl(t.url))){let e=i&&a&&_l.read(a);e&&o.set(i,e)}return t},wl=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Cl(e),i=r.data,a=al.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=al.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());ll(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new J(`Request aborted`,J.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new J(t&&t.message?t.message:`Network Error`,J.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Fc;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new J(t,i.clarifyTimeoutError?J.ETIMEDOUT:J.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&q.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),q.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=pl(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=pl(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new cl(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=ul(r.url);if(_&&Y.protocols.indexOf(_)===-1){n(new J(`Unsupported protocol `+_+`:`,J.ERR_BAD_REQUEST,e));return}h.send(i||null)})},Tl=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof J?t:new cl(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new J(`timeout of ${t}ms exceeded`,J.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>q.asap(o),s}},El=function*(e,t){let n=e.byteLength;if(!t||n{let i=Dl(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Al=64*1024,{isFunction:jl}=q,Ml=(({Request:e,Response:t})=>({Request:e,Response:t}))(q.global),{ReadableStream:Nl,TextEncoder:Pl}=q.global,Fl=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Il=e=>{e=q.merge.call({skipUndefined:!0},Ml,e);let{fetch:t,Request:n,Response:r}=e,i=t?jl(t):typeof fetch==`function`,a=jl(n),o=jl(r);if(!i)return!1;let s=i&&jl(Nl),c=i&&(typeof Pl==`function`?(e=>t=>e.encode(t))(new Pl):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&Fl(()=>{let e=!1,t=new n(Y.origin,{body:new Nl,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return e&&!t}),u=o&&s&&Fl(()=>q.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new J(`Response type '${e}' is not supported`,J.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(q.isBlob(e))return e.size;if(q.isSpecCompliantForm(e))return(await new n(Y.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(q.isArrayBufferView(e)||q.isArrayBuffer(e))return e.byteLength;if(q.isURLSearchParams(e)&&(e+=``),q.isString(e))return(await c(e)).byteLength},p=async(e,t)=>q.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Cl(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=Tl([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),T;try{if(g&&l&&o!==`get`&&o!==`head`&&(T=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(q.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=ml(T,pl(hl(g)));s=kl(e.body,Al,t,n)}}q.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=q.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&ml(t,pl(hl(h),!0))||[];f=new r(kl(f.body,Al,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let E=await d[q.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{ll(t,n,{data:E,headers:al.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new J(`Network Error`,J.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):J.from(t,t&&t.code,e,C,t&&t.response)}}},Ll=new Map,Rl=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Ll;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:Il(t)),l=c;return c};Rl();var zl={http:null,xhr:wl,fetch:{get:Rl}};q.forEach(zl,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Bl=e=>`- ${e}`,Vl=e=>q.isFunction(e)||e===null||e===!1;function Hl(e,t){e=q.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new J(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : +`+e.map(Bl).join(` +`):` `+Bl(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var Ul={getAdapter:Hl,adapters:zl};function Wl(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new cl(null,e)}function Gl(e){return Wl(e),e.headers=al.from(e.headers),e.data=ol.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),Ul.getAdapter(e.adapter||Jc.adapter,e)(e).then(function(t){return Wl(e),t.data=ol.call(e,e.transformResponse,t),t.headers=al.from(t.headers),t},function(t){return sl(t)||(Wl(e),t&&t.response&&(t.response.data=ol.call(e,e.transformResponse,t.response),t.response.headers=al.from(t.response.headers))),Promise.reject(t)})}var Kl=`1.13.6`,ql={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{ql[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var Jl={};ql.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Kl+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new J(r(i,` has been removed`+(t?` in `+t:``)),J.ERR_DEPRECATED);return t&&!Jl[i]&&(Jl[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},ql.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function Yl(e,t,n){if(typeof e!=`object`)throw new J(`options must be an object`,J.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new J(`option `+a+` must be `+n,J.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new J(`Unknown option `+a,J.ERR_BAD_OPTION)}}var Xl={assertOptions:Yl,validators:ql},Zl=Xl.validators,Ql=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Pc,response:new Pc}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` +`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Sl(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&Xl.assertOptions(n,{silentJSONParsing:Zl.transitional(Zl.boolean),forcedJSONParsing:Zl.transitional(Zl.boolean),clarifyTimeoutError:Zl.transitional(Zl.boolean),legacyInterceptorReqResOrdering:Zl.transitional(Zl.boolean)},!1),r!=null&&(q.isFunction(r)?t.paramsSerializer={serialize:r}:Xl.assertOptions(r,{encode:Zl.function,serialize:Zl.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),Xl.assertOptions(t,{baseUrl:Zl.spelling(`baseURL`),withXsrfToken:Zl.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&q.merge(i.common,i[t.method]);i&&q.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=al.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Fc;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[Gl.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new cl(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function eu(e){return function(t){return e.apply(null,t)}}function tu(e){return q.isObject(e)&&e.isAxiosError===!0}var nu={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(nu).forEach(([e,t])=>{nu[t]=e});function ru(e){let t=new Ql(e),n=fs(Ql.prototype.request,t);return q.extend(n,Ql.prototype,t,{allOwnKeys:!0}),q.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return ru(Sl(e,t))},n}var X=ru(Jc);X.Axios=Ql,X.CanceledError=cl,X.CancelToken=$l,X.isCancel=sl,X.VERSION=Kl,X.toFormData=Oc,X.AxiosError=J,X.Cancel=X.CanceledError,X.all=function(e){return Promise.all(e)},X.spread=eu,X.isAxiosError=tu,X.mergeConfig=Sl,X.AxiosHeaders=al,X.formToJSON=e=>Kc(q.isHTMLForm(e)?new FormData(e):e),X.getAdapter=Ul.getAdapter,X.HttpStatusCode=nu,X.default=X;var Z=X.create({baseURL:`/api`,timeout:1e4,headers:{"Content-Type":`application/json`}}),iu=(e,t=`error`)=>{window.showToast?window.showToast(e,t):window.alert(e)};Z.interceptors.response.use(e=>{let t=e.data;if(console.log(`API Response:`,e.config.url,t),t&&typeof t==`object`&&(t.success===!1||t.Success===!1)){let e=t.errors||t.Errors,n=t.message||t.Message,r=e&&Array.isArray(e)&&e.length>0?e.join(` +`):n||`操作失败`;return console.error(`Business Error:`,r),iu(r,`error`),Promise.reject(Error(r))}return e},e=>{console.error(`API Error:`,e);let t=`网络错误,请稍后再试`;if(e.response&&e.response.data){let n=e.response.data,r=n.errors||n.Errors,i=n.message||n.Message;r&&Array.isArray(r)&&r.length>0?t=r.join(` +`):i&&(t=i)}else e.message&&(t=e.message);return iu(t,`error`),Promise.reject(e)});var au=e=>{if(e===0||e===1||e===2)return e;if(typeof e==`number`&&Number.isFinite(e))return e<=0?0:e===1?1:2;if(typeof e==`string`){let t=e.trim();if(t===`0`||t===`1`||t===`2`)return Number(t);let n=t.toLowerCase();if(n===`low`)return 0;if(n===`medium`||n===`mid`)return 1;if(n===`high`)return 2}return 1},ou=e=>typeof e==`string`&&e.trim()?e:e instanceof Date&&Number.isFinite(e.getTime())?e.toISOString():typeof e==`number`&&Number.isFinite(e)?new Date(e).toISOString():new Date().toISOString(),su=e=>{let t={...e??{}};return t.id=typeof t.id==`number`?String(t.id):t.id??``,t.code=typeof t.code==`string`&&t.code.trim()?t.code:void 0,t.title=typeof t.title==`string`?t.title:String(t.title??``),t.priority=au(t.priority),t.isCompleted=typeof t.isCompleted==`boolean`?t.isCompleted:!!t.isCompleted,t.createdAt=ou(t.createdAt),t.updatedAt=ou(t.updatedAt),t.parentTaskId===null||t.parentTaskId===0?t.parentTaskId=void 0:typeof t.parentTaskId==`number`&&(t.parentTaskId=String(t.parentTaskId)),Array.isArray(t.subTasks)?t.subTasks=t.subTasks.map(su):t.subTasks=[],t},cu=e=>Array.isArray(e)?e.map(su):[],lu=`Hua.Todo_tasks`,uu=`Hua.Todo_sync_status`,Q=class{static saveTasks(e){try{localStorage.setItem(lu,JSON.stringify(cu(e)))}catch(e){console.error(`Failed to save tasks to localStorage:`,e)}}static loadTasks(){try{let e=localStorage.getItem(lu);return e?cu(JSON.parse(e)):[]}catch(e){return console.error(`Failed to load tasks from localStorage:`,e),[]}}static clearTasks(){try{localStorage.removeItem(lu)}catch(e){console.error(`Failed to clear tasks from localStorage:`,e)}}static saveSyncStatus(e){try{localStorage.setItem(uu,JSON.stringify(e))}catch(e){console.error(`Failed to save sync status:`,e)}}static loadSyncStatus(){try{let e=localStorage.getItem(uu);return e?JSON.parse(e):{lastSyncTime:0,isOnline:!0,pendingChanges:0}}catch(e){return console.error(`Failed to load sync status:`,e),{lastSyncTime:0,isOnline:!0,pendingChanges:0}}}static isOnline(){return navigator.onLine}static ALLOW_PERSIST_KEY=`Hua.Todo_allow_persist`;static setAllowPersist(e){try{let t=this.getAllowPersist();localStorage.setItem(this.ALLOW_PERSIST_KEY,String(e)),t&&!e&&(localStorage.removeItem(lu),localStorage.removeItem(uu),console.log(`allowPersist 已禁止:所有持久化任务数据已清空`))}catch(e){console.error(`Failed to save allowPersist to localStorage:`,e)}}static getAllowPersist(){try{return localStorage.getItem(this.ALLOW_PERSIST_KEY)!==`false`}catch{return!0}}},du={async getTasks(e){if(!Q.isOnline()){let t=Q.loadTasks();return{success:!0,data:e===void 0?t:t.filter(t=>t.isCompleted===e),message:`从本地加载任务`}}let t=e===!0?`/task/completed`:e===!1?`/task/active`:`/task`,n=await Z.get(t),r;if(r=Array.isArray(n.data)?{success:!0,data:n.data,message:`获取任务成功`}:n.data,r.success&&r.data){r.data=cu(r.data),Q.saveTasks(r.data);let e=Q.loadSyncStatus();e.lastSyncTime=Date.now(),e.isOnline=!0,e.pendingChanges=0,Q.saveSyncStatus(e)}return r},async getTask(e){if(!Q.isOnline()){let t=Q.loadTasks().find(t=>t.id===e);return{success:!0,data:t||null,message:t?`从本地加载任务`:`任务不存在`}}let t=await Z.get(`/task/${e}`),n;return n=t.data&&`id`in t.data?{success:!0,data:t.data,message:`获取任务成功`}:t.data,n.success&&n.data&&(n.data=su(n.data)),n},async createTask(e){let t={id:crypto.randomUUID(),title:e.title,priority:e.priority,isCompleted:!1,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),parentTaskId:e.parentTaskId,taskType:e.taskType??0,description:e.description,subTasks:[]};if(!Q.isOnline()){let e=Q.loadTasks();e.unshift(t),Q.saveTasks(e);let n=Q.loadSyncStatus();return n.pendingChanges++,Q.saveSyncStatus(n),{success:!0,data:t,message:`离线创建任务成功`}}let n=await Z.post(`/task`,e),r;if(r=n.data&&`id`in n.data?{success:!0,data:n.data,message:`创建任务成功`}:n.data,r.success&&r.data){r.data=su(r.data);let e=Q.loadTasks(),n=e.findIndex(e=>e.id===t.id);n===-1?e.unshift(r.data):e[n]=r.data,Q.saveTasks(e)}return r},async updateTask(e,t){let n=t;if(!Q.isOnline()){let n=Q.loadTasks(),r=n.findIndex(t=>t.id===e);if(r!==-1){t.title&&(n[r].title=t.title),t.priority!==void 0&&(n[r].priority=t.priority),t.description!==void 0&&(n[r].description=t.description),t.taskType!==void 0&&(n[r].taskType=t.taskType),n[r].updatedAt=new Date().toISOString(),Q.saveTasks(n);let e=Q.loadSyncStatus();return e.pendingChanges++,Q.saveSyncStatus(e),{success:!0,data:n[r],message:`离线更新任务成功`}}return{success:!1,message:`任务不存在`}}let r=await Z.put(`/task`,n),i;if(i=r.data&&`id`in r.data?{success:!0,data:r.data,message:`更新任务成功`}:r.data,i.success&&i.data){i.data=su(i.data);let t=Q.loadTasks(),n=t.findIndex(t=>t.id===e);n!==-1&&(t[n]=i.data,Q.saveTasks(t))}return i},async toggleComplete(e){if(!Q.isOnline()){let t=Q.loadTasks(),n=t.findIndex(t=>t.id===e);if(n!==-1){t[n].isCompleted=!t[n].isCompleted,t[n].updatedAt=new Date().toISOString(),Q.saveTasks(t);let e=Q.loadSyncStatus();return e.pendingChanges++,Q.saveSyncStatus(e),{success:!0,data:t[n],message:`离线切换任务状态成功`}}return{success:!1,message:`任务不存在`}}let t=await Z.patch(`/task/${e}/toggle`),n;if(n=t.data&&`id`in t.data?{success:!0,data:t.data,message:`切换任务状态成功`}:t.data,n.success&&n.data){n.data=su(n.data);let t=Q.loadTasks(),r=t.findIndex(t=>t.id===e);r!==-1&&(t[r]=n.data,Q.saveTasks(t))}return n},async deleteTask(e){if(!Q.isOnline()){let t=Q.loadTasks(),n=t.findIndex(t=>t.id===e);if(n!==-1){t.splice(n,1),Q.saveTasks(t);let e=Q.loadSyncStatus();return e.pendingChanges++,Q.saveSyncStatus(e),{success:!0,message:`离线删除任务成功`}}return{success:!1,message:`任务不存在`}}let t=await Z.delete(`/task/${e}`),n;if(n=t.data&&typeof t.data==`object`&&`success`in t.data?t.data:{success:!0,message:`删除任务成功`},n.success){let t=Q.loadTasks(),n=t.findIndex(t=>t.id===e);n!==-1&&(t.splice(n,1),Q.saveTasks(t))}return n},async syncToServer(){if(!Q.isOnline())return{success:!1,message:`当前离线,无法同步`};let e=Q.loadTasks(),t=Q.loadSyncStatus();if(t.pendingChanges===0)return{success:!0,data:e,message:`没有待同步的更改`};try{let e=await Z.get(`/task`),n;return n=Array.isArray(e.data)?{success:!0,data:e.data,message:`获取任务成功`}:e.data,n.success&&n.data?(n.data=cu(n.data),Q.saveTasks(n.data),t.lastSyncTime=Date.now(),t.isOnline=!0,t.pendingChanges=0,Q.saveSyncStatus(t),{success:!0,data:n.data,message:`同步成功`}):{success:!1,message:`同步失败`}}catch(e){return console.error(`Sync failed:`,e),{success:!1,message:`同步失败`}}}};async function fu(e,t,n,r){return(await Z.post(`/meeting/transcribeAudio`,{taskId:e,audioBase64:t,format:n,audioDuration:r})).data}async function pu(e,t){await Z.post(`/meeting/saveNotes`,{taskId:e,notes:t})}async function mu(e){return(await Z.post(`/meeting/breakdown`,{taskId:e})).data}async function hu(e,t){return(await Z.post(`/meeting/confirmBreakdown`,{taskId:e,subTasks:t})).data}var gu={async upload(e,t,n,r){let i=await Z.post(`/attachment/upload`,{taskId:e,fileName:t,base64Content:n,contentType:r});return i.data&&`success`in i.data?i.data:{success:!0,data:i.data,message:`上传成功`}},async addLink(e,t,n){let r=await Z.post(`/attachment/addLink`,{taskId:e,url:t,fileName:n});return r.data&&`success`in r.data?r.data:{success:!0,data:r.data,message:`链接已添加`}},async list(e){let t=await Z.get(`/attachment?taskId=${e}`);return Array.isArray(t.data)?{success:!0,data:t.data,message:`获取成功`}:t.data},async remove(e){let t=await Z.delete(`/attachment?id=${e}`);return t.data&&`success`in t.data?t.data:{success:!0,message:`已删除`}},async open(e){let t=await Z.post(`/attachment/open?id=${e}`);return t.data&&`success`in t.data?t.data:{success:!0,data:t.data,message:`已打开`}},async download(e){let t=await Z.get(`/attachment?taskId=0&id=${e}`);return t.data&&`success`in t.data?t.data:{success:!0,data:t.data,message:`获取成功`}}};function _u(){let e=F([]),t=F(!1),n=F(``);async function r(r){t.value=!0,n.value=``;try{let t=await gu.list(r);t.success&&t.data&&(e.value=t.data.map(e=>({...e,isMissing:!1})))}catch(e){n.value=e?.message||`加载附件失败`,console.error(`loadAttachments failed:`,e)}finally{t.value=!1}}async function i(r,i){if(i.size>50*1024*1024)return n.value=`文件大小不能超过 50MB`,!1;if(e.value.length>=20)return n.value=`每个待办项最多 20 个附件`,!1;t.value=!0,n.value=``;try{let t=await vu(i),a=await gu.upload(r,i.name,t,i.type);return a.success&&a.data?(e.value.push({...a.data,isMissing:!1}),!0):(n.value=a.message||`上传失败`,!1)}catch(e){return n.value=e?.message||`上传失败`,console.error(`uploadFile failed:`,e),!1}finally{t.value=!1}}async function a(r,i,a){if(e.value.length>=20)return n.value=`每个待办项最多 20 个附件`,!1;t.value=!0,n.value=``;try{let t=await gu.addLink(r,i,a);return t.success&&t.data?(e.value.push({...t.data,isMissing:!1}),!0):(n.value=t.message||`添加链接失败`,!1)}catch(e){return n.value=e?.message||`添加链接失败`,console.error(`addLinkAttachment failed:`,e),!1}finally{t.value=!1}}async function o(r){t.value=!0,n.value=``;try{let t=await gu.remove(r);return t.success?(e.value=e.value.filter(e=>e.id!==r),!0):(n.value=t.message||`删除失败`,!1)}catch(e){return n.value=e?.message||`删除失败`,console.error(`removeAttachment failed:`,e),!1}finally{t.value=!1}}async function s(e){try{let t=await gu.open(e);return t.success?!0:(n.value=t.message||`打开失败`,!1)}catch(e){return n.value=e?.message||`打开失败`,console.error(`openAttachment failed:`,e),!1}}function c(t){if(t.attachments){e.value=t.attachments.map(e=>({...e,isMissing:!1}));return}t.attachmentCount!==void 0&&t.attachmentCount>0?r(t.id):e.value=[]}return{attachments:e,isLoading:t,error:n,loadAttachments:r,uploadFile:i,addLinkAttachment:a,removeAttachment:o,openAttachment:s,initFromTask:c}}function vu(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{let e=r.result;t(e.split(`,`)[1]||e)},r.onerror=()=>n(Error(`读取文件失败`)),r.readAsDataURL(e)})}var yu={class:`attachment-section`},bu={class:`attachment-header`},xu={class:`attachment-title`},Su={class:`attachment-actions`},Cu={key:0,class:`attachment-empty`},wu={key:1,class:`attachment-list`},Tu={class:`file-icon`},Eu={class:`file-info`},Du=[`title`],Ou={key:0,class:`file-size`},ku={key:1,class:`missing-tag`},Au={class:`file-actions`},ju=[`onClick`],Mu=[`onClick`],Nu=ir({__name:`AttachmentList`,props:{items:{}},emits:[`upload`,`addLink`,`open`,`remove`],setup(e){function t(e){return e?e.startsWith(`image/`)?`🖼`:e.startsWith(`audio/`)||e.includes(`wav`)||e.includes(`webm`)?`🎵`:e.startsWith(`video/`)?`🎬`:e.includes(`pdf`)?`📕`:e.includes(`word`)||e.includes(`document`)?`📝`:e.includes(`sheet`)||e.includes(`excel`)?`📊`:e.includes(`zip`)||e.includes(`rar`)||e.includes(`tar`)?`📦`:`📄`:`📄`}function n(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}return(r,i)=>(V(),H(`div`,yu,[U(`div`,bu,[U(`span`,xu,`📎 附件 (`+A(e.items.length)+`)`,1),U(`div`,Su,[U(`button`,{class:`btn-attach`,onClick:i[0]||=e=>r.$emit(`upload`),title:`上传文件`},[...i[2]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12`})],-1),ca(` 上传 `,-1)]]),U(`button`,{class:`btn-link`,onClick:i[1]||=e=>r.$emit(`addLink`),title:`添加链接`},[...i[3]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71`}),U(`path`,{d:`M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71`})],-1),ca(` 链接 `,-1)]])])]),e.items.length===0?(V(),H(`div`,Cu,` 暂无附件 `)):(V(),H(`div`,wu,[(V(!0),H(z,null,Nr(e.items,e=>(V(),H(`div`,{key:e.id,class:_e([`attachment-item`,{"is-missing":e.isMissing}])},[U(`span`,Tu,A(e.attachmentType===1?`🔗`:t(e.contentType)),1),U(`div`,Eu,[U(`span`,{class:`file-name`,title:e.fileName},A(e.fileName),9,Du),e.fileSize>0?(V(),H(`span`,Ou,A(n(e.fileSize)),1)):G(``,!0),e.isMissing?(V(),H(`span`,ku,`⚠ 文件丢失`)):G(``,!0)]),U(`div`,Au,[e.isMissing?G(``,!0):(V(),H(`button`,{key:0,class:`btn-file btn-open`,onClick:t=>r.$emit(`open`,e.id),title:`打开`},[...i[4]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3`})],-1)]],8,ju)),U(`button`,{class:`btn-file btn-delete`,onClick:t=>r.$emit(`remove`,e.id),title:`删除`},[...i[5]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),U(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,Mu)])],2))),128))]))]))}}),Pu=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},Fu=Pu(Nu,[[`__scopeId`,`data-v-5b5fc0e2`]]),Iu={class:`link-dialog`},Lu={class:`link-header`},Ru={class:`link-body`},zu={class:`form-group`},Bu={class:`form-group`},Vu={class:`link-footer`},Hu=[`disabled`],Uu=Pu(ir({__name:`LinkInputDialog`,props:{visible:{type:Boolean}},emits:[`close`,`confirmed`],setup(e,{emit:t}){let n=t,r=F(``),i=F(``);function a(){let e=r.value.trim();if(!e)return;let t=e;/^https?:\/\//i.test(t)||(t=`https://`+t),n(`confirmed`,t,i.value.trim()||t),r.value=``,i.value=``,n(`close`)}return(t,n)=>e.visible?(V(),H(`div`,{key:0,class:`link-overlay`,onClick:n[4]||=rs(e=>t.$emit(`close`),[`self`])},[U(`div`,Iu,[U(`div`,Lu,[n[5]||=U(`h4`,null,`添加外部链接`,-1),U(`button`,{class:`btn-close`,onClick:n[0]||=e=>t.$emit(`close`)},`×`)]),U(`div`,Ru,[U(`div`,zu,[n[6]||=U(`label`,{class:`form-label`},`链接地址`,-1),L(U(`input`,{"onUpdate:modelValue":n[1]||=e=>r.value=e,type:`url`,placeholder:`https://example.com/doc`,class:`form-input`,onKeyup:as(a,[`enter`])},null,544),[[Jo,r.value]])]),U(`div`,Bu,[n[7]||=U(`label`,{class:`form-label`},`显示名称(可选)`,-1),L(U(`input`,{"onUpdate:modelValue":n[2]||=e=>i.value=e,type:`text`,placeholder:`留空则显示链接地址`,class:`form-input`,onKeyup:as(a,[`enter`])},null,544),[[Jo,i.value]])])]),U(`div`,Vu,[U(`button`,{class:`btn-cancel`,onClick:n[3]||=e=>t.$emit(`close`)},`取消`),U(`button`,{class:`btn-confirm`,disabled:!r.value.trim(),onClick:a},` 添加链接 `,8,Hu)])])])):G(``,!0)}}),[[`__scopeId`,`data-v-5d086492`]]),Wu={class:`dialog-content`},Gu={class:`dialog-body`},Ku={class:`form-group`},qu={class:`form-group`},Ju={class:`form-group`},Yu={class:`char-count`},Xu={key:0,class:`attach-error`},Zu={class:`dialog-footer`},Qu=[`disabled`],$u=Pu(ir({__name:`TaskEditDialog`,props:{visible:{type:Boolean},task:{}},emits:[`update:visible`,`saved`],setup(e,{emit:t}){let n=e,r=t,i=F(``),a=F(1),o=F(``),s=F(!1),c=F(null),l=_u();Ln(()=>l.attachments.value.length,e=>{n.task&&(n.task.attachmentCount=e,n.task.attachments=l.attachments.value.map(e=>({id:e.id,taskId:e.taskId,fileName:e.fileName,filePath:e.filePath,fileSize:e.fileSize,contentType:e.contentType,attachmentType:e.attachmentType,createdAt:e.createdAt})))}),Ln(()=>n.task,e=>{e&&(i.value=e.title,a.value=e.priority,o.value=e.description||``,l.initFromTask(e))},{immediate:!0});function u(){c.value?.click()}async function d(e){let t=e.target,r=t.files?.[0];!r||!n.task||(await l.uploadFile(n.task.id,r)&&h(`附件上传成功`,`success`),t.value=``)}async function f(e){await l.openAttachment(e)}async function p(e){confirm(`确定要删除此附件吗?`)&&await l.removeAttachment(e)&&h(`附件已删除`,`success`)}async function m(e,t){n.task&&await l.addLinkAttachment(n.task.id,e,t)&&h(`链接已添加`,`success`)}function h(e,t=`error`){window.showToast?window.showToast(e,t):t===`error`?window.alert(e):console.log(e)}let g=async()=>{if(!n.task||!i.value.trim()){alert(`请输入任务标题`);return}try{let e=await du.updateTask(n.task.id,{id:n.task.id,title:i.value.trim(),priority:a.value,description:o.value});e.success&&e.data&&(n.task.attachments&&(e.data.attachments=n.task.attachments,e.data.attachmentCount=n.task.attachmentCount),r(`saved`,e.data),r(`update:visible`,!1))}catch(e){console.error(`Failed to update task:`,e),alert(`更新任务失败`)}},_=()=>{r(`update:visible`,!1)};return(t,n)=>e.visible?(V(),H(`div`,{key:0,class:`dialog-overlay`,onClick:rs(_,[`self`])},[U(`div`,Wu,[U(`div`,{class:`dialog-header`},[n[5]||=U(`h2`,{class:`dialog-title`},`编辑待办项`,-1),U(`button`,{onClick:_,class:`btn-close`},`×`)]),U(`div`,Gu,[U(`div`,Ku,[n[6]||=U(`label`,{class:`form-label`},`标题`,-1),L(U(`input`,{"onUpdate:modelValue":n[0]||=e=>i.value=e,type:`text`,placeholder:`输入任务标题...`,class:`form-input`,onKeyup:as(g,[`enter`])},null,544),[[Jo,i.value]])]),U(`div`,qu,[n[8]||=U(`label`,{class:`form-label`},`优先级`,-1),L(U(`select`,{"onUpdate:modelValue":n[1]||=e=>a.value=e,class:`form-select`},[...n[7]||=[U(`option`,{value:``,disabled:``},`请选择优先级`,-1),U(`option`,{value:0},`低`,-1),U(`option`,{value:1},`中`,-1),U(`option`,{value:2},`高`,-1)]],512),[[Zo,a.value]])]),U(`div`,Ju,[n[9]||=U(`label`,{class:`form-label`},`描述`,-1),L(U(`textarea`,{"onUpdate:modelValue":n[2]||=e=>o.value=e,placeholder:`添加备注、步骤说明...`,class:`form-textarea`,maxlength:`5000`},null,512),[[Jo,o.value]]),U(`span`,Yu,A(o.value.length)+`/5000`,1)]),W(Fu,{items:Zt(l).attachments.value,onUpload:u,onAddLink:n[3]||=e=>s.value=!0,onOpen:f,onRemove:p},null,8,[`items`]),Zt(l).error?(V(),H(`div`,Xu,A(Zt(l).error),1)):G(``,!0),U(`input`,{ref_key:`fileInput`,ref:c,type:`file`,class:`hidden-file-input`,onChange:d},null,544)]),U(`div`,Zu,[U(`button`,{onClick:_,class:`btn-cancel`},`取消`),U(`button`,{onClick:g,class:`btn-save`,disabled:Zt(l).isLoading.value},A(Zt(l).isLoading.value?`保存中...`:`保存`),9,Qu)])]),W(Uu,{visible:s.value,onClose:n[4]||=e=>s.value=!1,onConfirmed:m},null,8,[`visible`])])):G(``,!0)}}),[[`__scopeId`,`data-v-7854be41`]]);function ed(){let e=F(!1),t=F(!1),n=F(0),r=F(null),i=F(null),a=F(null),o=F(typeof MediaRecorder<`u`),s=null,c=null,l=[];function u(){for(let e of[`audio/webm`,`audio/webm;codecs=opus`,`audio/mp4`,`audio/ogg;codecs=opus`])if(MediaRecorder.isTypeSupported(e))return e;return`audio/webm`}function d(){let e=u();return e.includes(`mp4`)?`mp4`:e.includes(`ogg`)?`ogg`:`webm`}async function f(){if(a.value=null,!o.value){a.value=`当前浏览器不支持录音,请使用文字输入`;return}try{let t=await navigator.mediaDevices.getUserMedia({audio:!0}),n=u();s=new MediaRecorder(t,{mimeType:n}),l=[],s.ondataavailable=e=>{e.data.size>0&&l.push(e.data)},s.onstop=()=>{t.getTracks().forEach(e=>e.stop());let e=s?.mimeType||n;r.value=new Blob(l,{type:e}),i.value&&URL.revokeObjectURL(i.value),i.value=URL.createObjectURL(r.value)},s.onerror=()=>{a.value=`录音过程中发生错误`,_(),e.value=!1},s.start(),e.value=!0,g()}catch(e){e.name===`NotAllowedError`||e.name===`PermissionDeniedError`?a.value=`无法访问麦克风,请使用文字输入`:a.value=`无法启动录音:`+(e.message||`未知错误`)}}function p(){!s||s.state===`inactive`||(_(),s.stop(),e.value=!1)}function m(){_(),s&&s.state!==`inactive`&&s.stop(),e.value=!1,t.value=!1,n.value=0,i.value&&URL.revokeObjectURL(i.value),r.value=null,i.value=null,a.value=null,l=[]}function h(){return r.value}function g(){_(),n.value=0,c=setInterval(()=>{n.value++,n.value>=7200&&p()},1e3)}function _(){c!==null&&(clearInterval(c),c=null)}return Cr(()=>{_(),s&&s.state!==`inactive`&&s.stop(),i.value&&URL.revokeObjectURL(i.value)}),{isRecording:e,isPaused:t,duration:n,audioBlob:r,audioUrl:i,error:a,isSupported:o,getFormat:d,startRecording:f,stopRecording:p,resetRecording:m,getAudioBlob:h}}var td={class:`audio-recorder`},nd={key:0,class:`error-message`},rd={key:1,class:`unsupported-message`},id={key:2},ad={key:0,class:`ready-state`},od={key:1,class:`recording-state`},sd={class:`recording-header`},cd={class:`timer`},ld={class:`waveform`},ud={key:2,class:`stopped-state`},dd={class:`stopped-info`},fd={class:`stopped-actions`},pd=[`disabled`],md={key:3,class:`transcribing-state`},hd=Pu(ir({__name:`AudioRecorder`,props:{taskId:{}},emits:[`transcribed`,`error`],setup(e,{emit:t}){let n=e,r=t,{isRecording:i,duration:a,audioBlob:o,error:s,isSupported:c,getFormat:l,startRecording:u,stopRecording:d,resetRecording:f}=ed(),p=F(!1),m=F(null),h=Ia(()=>m.value||s.value);Ln(s,e=>{e&&r(`error`,e)});let g=Ia(()=>{let e=a.value,t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60,i=e=>e.toString().padStart(2,`0`);return t>0?`${i(t)}:${i(n)}:${i(r)}`:`${i(n)}:${i(r)}`});async function _(){m.value=null,await u()}function v(){d()}function y(){f(),m.value=null}async function b(){if(o.value){p.value=!0,m.value=null;try{let e=await o.value.arrayBuffer(),t=new Uint8Array(e),i=``;for(let e=0;e(V(),H(`div`,td,[h.value?(V(),H(`div`,nd,A(h.value),1)):G(``,!0),Zt(c)?(V(),H(`div`,id,[!Zt(i)&&!Zt(o)&&!p.value?(V(),H(`div`,ad,[U(`button`,{class:`record-btn`,onClick:_},`🎤 开始录音`)])):Zt(i)?(V(),H(`div`,od,[U(`div`,sd,[t[0]||=U(`span`,{class:`recording-indicator`},`● 录制中...`,-1),U(`span`,cd,A(g.value),1)]),U(`div`,ld,[(V(),H(z,null,Nr(12,e=>U(`div`,{key:e,class:`waveform-bar`,style:fe({animationDelay:`${e*.08}s`,height:`${20+Math.sin(Date.now()/200+e)*15}px`})},null,4)),64))]),U(`button`,{class:`stop-btn`,onClick:v},`⏹ 停止录音`)])):Zt(o)&&!p.value?(V(),H(`div`,ud,[U(`div`,dd,[U(`span`,null,`录音完成 (`+A(g.value)+`)`,1)]),U(`div`,fd,[U(`button`,{class:`submit-btn`,disabled:p.value,onClick:b},A(p.value?`转写中...`:`提交转写`),9,pd),U(`button`,{class:`reset-btn`,onClick:y},`重新录制`)])])):G(``,!0),p.value?(V(),H(`div`,md,[...t[1]||=[U(`span`,{class:`transcribing-text`},`转写中,请稍候...`,-1)]])):G(``,!0)])):(V(),H(`div`,rd,` 当前浏览器不支持录音,请使用文字输入 `))]))}}),[[`__scopeId`,`data-v-3824a835`]]),gd={class:`dialog`},_d={class:`dialog-header`},vd={key:0,class:`loading-state`},yd={key:1,class:`error-state`},bd={class:`error-text`},xd={key:2,class:`suggestions-body`},Sd={class:`suggestions-list`},Cd={class:`item-row`},wd={class:`checkbox-label`},Td=[`onUpdate:modelValue`],Ed=[`onUpdate:modelValue`,`onBlur`,`onKeyup`],Dd=[`onDblclick`],Od=[`onUpdate:modelValue`],kd=[`onClick`],Ad={key:0,class:`reason`},jd={key:1,class:`reason manual-tag`},Md={class:`dialog-footer`},Nd={class:`selected-count`},Pd={class:`footer-actions`},Fd=[`disabled`],Id=[`disabled`],Ld=Pu(ir({__name:`MeetingBreakdownDialog`,props:{task:{},visible:{type:Boolean}},emits:[`close`,`created`],setup(e,{emit:t}){let n=e,r=t,i=F(!1),a=F(!1),o=F(``),s=F([]),c=F([]),l=Ia(()=>c.value.filter(e=>e.checked).length);async function u(){i.value=!0,o.value=``,s.value=[],c.value=[];try{let e=(await mu(n.task.id)).suggestions||[];if(e.length===0){o.value=`未从会议内容中提取到待办项,请确认纪要内容是否充分。`;return}s.value=e,c.value=e.map(e=>({title:e.title,priority:e.priority,checked:!0,reason:e.reason,isManual:!1,editing:!1}))}catch(e){o.value=`AI 拆分失败:${e?.message||String(e)}`}finally{i.value=!1}}function d(){c.value.push({title:`新待办项`,priority:1,checked:!0,reason:void 0,isManual:!0,editing:!0})}function f(e){c.value.splice(e,1)}function p(e){e.editing=!0}async function m(){let e=c.value.filter(e=>e.checked).map(e=>({title:e.title,priority:e.priority}));if(e.length!==0){a.value=!0;try{h(`已成功创建 ${(await hu(n.task.id,e)).createdCount??e.length} 个待办项`,`success`),r(`created`),r(`close`)}catch(e){h(`创建失败:${e?.message||String(e)}`,`error`)}finally{a.value=!1}}}function h(e,t=`error`){window.showToast?window.showToast(e,t):window.alert(e)}return Ln(()=>n.visible,e=>{e&&u()}),(t,n)=>e.visible?(V(),H(`div`,{key:0,class:`overlay`,onClick:n[2]||=rs(e=>r(`close`),[`self`])},[U(`div`,gd,[U(`div`,_d,[U(`h3`,null,`会议任务拆分 — `+A(e.task.title),1),U(`button`,{class:`close-button`,onClick:n[0]||=e=>r(`close`)},`✕`)]),i.value?(V(),H(`div`,vd,[...n[3]||=[U(`span`,{class:`spinner`},null,-1),U(`p`,null,`AI 正在分析会议内容,生成待办项建议...`,-1)]])):o.value?(V(),H(`div`,yd,[U(`p`,bd,A(o.value),1),U(`button`,{class:`btn btn-secondary`,onClick:u},`🔄 重试`)])):(V(),H(`div`,xd,[n[5]||=U(`p`,{class:`hint`},` AI 根据会议内容生成了以下待办项建议。请勾选需要创建的条目,可双击标题编辑。 `,-1),U(`div`,Sd,[(V(!0),H(z,null,Nr(c.value,(e,t)=>(V(),H(`div`,{key:t,class:`suggestion-item`},[U(`div`,Cd,[U(`label`,wd,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":t=>e.checked=t},null,8,Td),[[Yo,e.checked]]),e.editing?L((V(),H(`input`,{key:0,ref_for:!0,ref:`titleInputs`,"onUpdate:modelValue":t=>e.title=t,class:`title-input`,onBlur:t=>e.editing=!1,onKeyup:as(t=>e.editing=!1,[`enter`])},null,40,Ed)),[[Jo,e.title]]):(V(),H(`span`,{key:1,class:`title-text`,onDblclick:t=>p(e)},A(e.title),41,Dd))]),L(U(`select`,{"onUpdate:modelValue":t=>e.priority=t,class:`priority-select`},[...n[4]||=[U(`option`,{value:2},`🔴 高`,-1),U(`option`,{value:1},`🟡 中`,-1),U(`option`,{value:0},`🟢 低`,-1)]],8,Od),[[Zo,e.priority]]),U(`button`,{class:`remove-btn`,onClick:e=>f(t),title:`移除此条目`},`✕`,8,kd)]),e.reason?(V(),H(`div`,Ad,`📝 `+A(e.reason),1)):G(``,!0),e.isManual?(V(),H(`div`,jd,`手动添加`)):G(``,!0)]))),128))]),U(`button`,{class:`add-btn`,onClick:d},`+ 手动添加待办项`)])),U(`div`,Md,[U(`span`,Nd,`已选 `+A(l.value)+` 项`,1),U(`div`,Pd,[U(`button`,{class:`btn btn-secondary`,onClick:n[1]||=e=>r(`close`),disabled:a.value},`取消`,8,Fd),U(`button`,{class:`btn btn-primary`,disabled:l.value===0||a.value,onClick:m},A(a.value?`创建中...`:`✅ 确认创建 (${l.value})`),9,Id)])])])])):G(``,!0)}}),[[`__scopeId`,`data-v-fd688f75`]]),Rd={key:0,class:`meeting-badge`,title:`会议`},zd=[`checked`],Bd=[`title`],Vd={class:`expand-icon`},Hd={class:`task-info`},Ud={class:`task-title`},Wd={key:0,class:`task-desc-preview`},Gd={class:`task-meta`},Kd={class:`task-id task-id--compact`},qd={class:`task-priority task-priority--compact`},Jd={key:0,class:`task-attach-badge`},Yd={class:`task-created`},Xd={key:1,class:`meeting-actions`},Zd=[`title`],Qd=[`disabled`],$d={key:2,class:`notes-editor`},ef={class:`notes-actions`},tf={key:0,class:`audio-info`},nf=[`disabled`],rf={key:4,class:`add-subtask-form`},af={key:5,class:`subtasks`},of=Pu(ir({__name:`TaskItem`,props:{task:{}},emits:[`updated`,`deleted`,`subtask-created`],setup(e,{emit:t}){let n=e,r=t,i=F(!1),a=F(!1),o=F(!1),s=F(!1),c=F(!1),l=F(!1),u=F(``),d=F(!1),f=F(``),p=F(1),m=F(null);Ln(c,e=>{e&&(u.value=n.task.meetingNotes||``)});let h=async()=>{a.value=!0,await yn(),m.value&&m.value.focus()},g=Ia(()=>{switch(n.task.priority){case 0:return`低`;case 2:return`高`;default:return`中`}}),_=Ia(()=>g.value),v=e=>{let t=new Date(e);if(!Number.isFinite(t.getTime()))return``;let n=e=>String(e).padStart(2,`0`);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`},y=Ia(()=>v(n.task.createdAt)),b=()=>{i.value=!i.value},x=async()=>{try{let e=await du.toggleComplete(n.task.id);e.success&&e.data&&r(`updated`,e.data)}catch(e){console.error(`Failed to toggle task:`,e)}},S=async()=>{try{(await du.deleteTask(n.task.id)).success&&r(`deleted`,n.task.id)}catch(e){console.error(`Failed to delete task:`,e)}},C=async()=>{if(!f.value.trim()){alert(`请输入子任务标题`);return}try{let e=await du.createTask({title:f.value.trim(),priority:p.value,parentTaskId:n.task.id});e.success&&e.data&&(r(`subtask-created`,e.data),a.value=!1,f.value=``,p.value=1)}catch(e){console.error(`Failed to create subtask:`,e)}},w=e=>{if(n.task.subTasks){let t=n.task.subTasks.findIndex(t=>t.id===e.id);t!==-1&&(n.task.subTasks[t]=e,r(`updated`,n.task))}},T=e=>{n.task.subTasks&&(n.task.subTasks=n.task.subTasks.filter(t=>t.id!==e),r(`updated`,n.task))},E=()=>{o.value=!0};function ee(){s.value=!s.value}function D(e,t){u.value||=n.task.meetingNotes||``,e&&(u.value=u.value?u.value+` + +`+e:e),n.task.audioDuration=t,s.value=!1}function te(){l.value=!0}function O(){r(`updated`,n.task)}async function ne(){if(n.task){d.value=!0;try{await pu(n.task.id,u.value),n.task.meetingNotes=u.value,c.value=!1,ie(`纪要已保存`,`success`)}catch(e){ie(`保存纪要失败`,`error`),console.error(`saveNotes failed:`,e)}finally{d.value=!1}}}function re(e){let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=Math.floor(e%60);return t>0?`${t}小时${n}分${r}秒`:n>0?`${n}分${r}秒`:`${r}秒`}function ie(e,t=`error`){window.showToast&&window.showToast(e,t)}let ae=e=>{r(`updated`,e)};return(t,n)=>{let r=kr(`TaskItem`,!0);return V(),H(`div`,{class:_e([`task-item`,{completed:e.task.isCompleted,"priority-low":e.task.priority===0,"priority-medium":e.task.priority===1,"priority-high":e.task.priority===2}])},[e.task.taskType===1?(V(),H(`span`,Rd,`🎙`)):G(``,!0),U(`div`,{class:`task-content`,onDblclick:E},[U(`input`,{type:`checkbox`,checked:e.task.isCompleted,onChange:x,class:`task-checkbox`},null,40,zd),e.task.subTasks&&e.task.subTasks.length>0?(V(),H(`button`,{key:0,class:`task-expand`,onClick:rs(b,[`stop`]),title:i.value?`收起`:`展开`},[U(`span`,Vd,A(i.value?`▼`:`▶`),1)],8,Bd)):G(``,!0),U(`button`,{onClick:h,class:`btn-add-subtask-icon`,title:`添加子任务`},[...n[8]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`},[U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z`})],-1)]]),U(`div`,Hd,[U(`span`,Ud,A(e.task.title),1),e.task.description?(V(),H(`span`,Wd,A(e.task.description),1)):G(``,!0),U(`div`,Gd,[U(`span`,Kd,`#`+A(e.task.code||e.task.id),1),U(`span`,qd,A(_.value),1),(e.task.attachmentCount??0)>0?(V(),H(`span`,Jd,`📎 `+A(e.task.attachmentCount),1)):G(``,!0),U(`span`,Yd,`创建`+A(y.value),1)])]),U(`div`,{class:`task-actions`},[U(`button`,{onClick:E,class:`btn-edit-icon`,title:`编辑`},[...n[9]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M12 20h9`}),U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5Z`})],-1)]]),U(`button`,{onClick:S,class:`btn-delete-icon`,title:`删除`},[...n[10]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M6 18L18 6M6 6l12 12`})],-1)]])])],32),e.task.taskType===1?(V(),H(`div`,Xd,[U(`button`,{class:`btn-meeting`,onClick:rs(ee,[`stop`]),title:s.value?`关闭录音`:`录音`},A(s.value?`⏹`:`🎤`),9,Zd),U(`button`,{class:`btn-meeting`,onClick:n[0]||=rs(e=>c.value=!c.value,[`stop`]),title:`纪要`},` 📝 `),U(`button`,{class:`btn-meeting`,onClick:rs(te,[`stop`]),title:`AI 拆分`,disabled:!e.task.meetingNotes},` 🤖 `,8,Qd)])):G(``,!0),c.value&&e.task.taskType===1?(V(),H(`div`,$d,[L(U(`textarea`,{"onUpdate:modelValue":n[1]||=e=>u.value=e,class:`notes-textarea`,placeholder:`输入或粘贴会议纪要...`,rows:`4`},null,512),[[Jo,u.value]]),U(`div`,ef,[e.task.audioDuration?(V(),H(`span`,tf,`录音时长:`+A(re(e.task.audioDuration)),1)):G(``,!0),U(`button`,{class:`btn-save-notes`,onClick:ne,disabled:d.value},A(d.value?`保存中...`:`💾 保存纪要`),9,nf)])])):G(``,!0),s.value&&e.task.taskType===1?(V(),ea(hd,{key:3,"task-id":e.task.id,onTranscribed:D},null,8,[`task-id`])):G(``,!0),a.value?(V(),H(`div`,rf,[L(U(`input`,{ref_key:`subTaskInput`,ref:m,"onUpdate:modelValue":n[2]||=e=>f.value=e,type:`text`,placeholder:`输入子任务标题...`,onKeyup:as(C,[`enter`]),class:`subtask-input`},null,544),[[Jo,f.value]]),L(U(`select`,{"onUpdate:modelValue":n[3]||=e=>p.value=e,class:`subtask-select`},[...n[11]||=[U(`option`,{value:0},`低`,-1),U(`option`,{value:1},`中`,-1),U(`option`,{value:2},`高`,-1)]],512),[[Zo,p.value]]),U(`button`,{onClick:C,class:`btn-create-subtask`,title:`创建子任务`},[...n[12]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`},[U(`line`,{x1:`12`,y1:`5`,x2:`12`,y2:`19`}),U(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`})],-1)]]),U(`button`,{onClick:n[4]||=e=>a.value=!1,class:`btn-cancel`,title:`取消`},[...n[13]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M18 6L6 18M6 6l12 12`})],-1)]])])):G(``,!0),i.value&&e.task.subTasks&&e.task.subTasks.length>0?(V(),H(`div`,af,[(V(!0),H(z,null,Nr(e.task.subTasks,e=>(V(),ea(r,{key:e.id,task:e,onUpdated:w,onDeleted:T,onSubtaskCreated:n[5]||=e=>t.$emit(`subtask-created`,e)},null,8,[`task`]))),128))])):G(``,!0),W($u,{visible:o.value,"onUpdate:visible":n[6]||=e=>o.value=e,task:e.task,onSaved:ae},null,8,[`visible`,`task`]),W(Ld,{visible:l.value,task:e.task,onClose:n[7]||=e=>l.value=!1,onCreated:O},null,8,[`visible`,`task`])],2)}}}),[[`__scopeId`,`data-v-4cce1be2`]]),sf=`Hua.Todo_cloud_sync_settings`,cf=`Hua.Todo_cloud_sync_session`,$=class{static loadSettings(){try{let e=localStorage.getItem(sf);if(!e)return{serverUrl:``,enabled:!1};let t=JSON.parse(e);return{serverUrl:typeof t.serverUrl==`string`?t.serverUrl:``,enabled:typeof t.enabled==`boolean`?t.enabled:!1}}catch{return{serverUrl:``,enabled:!1}}}static saveSettings(e){localStorage.setItem(sf,JSON.stringify(e))}static clearSettings(){localStorage.removeItem(sf)}static loadSession(){try{let e=sessionStorage.getItem(cf);if(!e)return null;let t=JSON.parse(e);return!t.accessToken||typeof t.accessToken!=`string`?null:{accessToken:t.accessToken,expiresAtUtc:typeof t.expiresAtUtc==`string`?t.expiresAtUtc:``,userId:typeof t.userId==`string`?t.userId:``,role:typeof t.role==`string`?t.role:``,permissions:Array.isArray(t.permissions)?t.permissions.filter(e=>typeof e==`string`):[]}}catch{return null}}static saveSession(e){sessionStorage.setItem(cf,JSON.stringify(e))}static clearSession(){sessionStorage.removeItem(cf)}},lf=(e,t=`error`)=>{window.showToast?window.showToast(e,t):window.alert(e)},uf=0,df=()=>{let e=Date.now();e-uf<1500||(uf=e,window.dispatchEvent(new CustomEvent(`openCloudSyncSettings`)))},ff=X.create({timeout:1e4,headers:{"Content-Type":`application/json`}});ff.interceptors.request.use(e=>{let t=$.loadSession();return t?.accessToken&&(e.headers=e.headers??{},e.headers.Authorization=`Bearer ${t.accessToken}`),e}),ff.interceptors.response.use(e=>e,e=>{let t=`网络错误,请稍后再试`,n=e?.response?.status,r=e?.response?.data,i=r?.code??r?.Code;if(n===403&&i===`FORBIDDEN`)return lf(`云同步权限不足:请确认安全策略中已启用 AllowSync`,`error`),Promise.reject(e);if(n===401||n===403)return n===401?($.clearSession(),df(),lf(`云同步会话已过期,请重新登录`,`error`)):lf(`云同步请求被拒绝(${i||n})`,`error`),Promise.reject(e);if(e?.response?.data){let n=e.response.data;if(typeof n==`string`&&n.trim())t=n;else if(n&&typeof n==`object`){let e=n.message??n.Message,r=n.errors??n.Errors;Array.isArray(r)&&r.length>0?t=r.join(` +`):typeof e==`string`&&e.trim()&&(t=e)}}else typeof e?.message==`string`&&e.message.trim()&&(t=e.message);return lf(t,`error`),Promise.reject(e)});function pf(e){return!!(e&&typeof e==`object`&&`success`in e&&`data`in e)}function mf(e){if(pf(e)){if(!e.success)throw Error(e.errors?.join(` +`)||e.message||`云同步请求失败`);return e.data}return e}var hf=e=>{let t=new Map;for(let n of e)t.set(n.id,{id:n.id,title:n.title??``,priority:n.priority??1,isCompleted:!!n.isCompleted,code:n.code??``,parentTaskId:n.parentTaskId??void 0,createdAt:n.creationTime,updatedAt:n.lastModificationTime??n.creationTime,creationTime:n.creationTime,creatorId:n.creatorId,lastModificationTime:n.lastModificationTime,lastModifierId:n.lastModifierId,isDeleted:n.isDeleted,deletionTime:n.deletionTime,deleterId:n.deleterId,subTasks:[]});let n=[];for(let e of t.values()){let r=e.parentTaskId;r!=null&&t.has(r)?t.get(r).subTasks.push(e):n.push(e)}return n},gf=e=>{let t=[],n=new Set,r=e=>{for(let i of e){let e=_f(i.id)?i.id:null;e!=null&&n.has(e)||(e!=null&&n.add(e),t.push({id:e,title:i.title,priority:i.priority,isCompleted:!!i.isCompleted,code:i.code??``,parentTaskId:_f(i.parentTaskId??``)?i.parentTaskId:null,lastModificationTime:i.lastModificationTime||new Date().toISOString()}),i.subTasks&&i.subTasks.length>0&&r(i.subTasks))}};return r(e),t};function _f(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e)}var vf={async login(e){let t=mf((await ff.post(`/api/auth/login`,e)).data);if(!t)throw Error(`登录失败:服务端返回空响应`);return $.saveSession({accessToken:t.accessToken,expiresAtUtc:t.expiresAtUtc,userId:t.userId,role:t.role,permissions:Array.isArray(t.permissions)?t.permissions:[]}),t},async logout(){try{await ff.post(`/api/auth/logout`)}catch{}$.clearSession()},async getTasks(){let e=mf((await ff.get(`/api/tasks/`)).data);return hf(Array.isArray(e)?e:[])},async syncTasks(e,t=[]){let n={upserts:gf(e),deletes:t},r=mf((await ff.post(`/api/tasks`,n)).data);return{tasks:hf(Array.isArray(r?.tasks)?r.tasks:[]),serverTimeUtc:r?.serverTimeUtc}},async getSecurityPolicy(){return mf((await ff.get(`/api/security/policy`)).data)},async probeServerUrl(e){return mf((await ff.post(`/api/cloud-sync/probe`,{targetUrl:e})).data)}},yf=Rt({session:null,policy:{allowPersist:!0,allowSync:!0}}),bf={async executeCommand(e){return(await Z.post(`/voice/command`,{text:e})).data},async confirmCommand(e,t,n){return(await Z.post(`/voice/command/confirm`,{intent:e,targetId:t,newTitle:n})).data},async getBreakdown(e){return(await Z.post(`/voice/ai-breakdown`,{taskId:e})).data},async confirmBreakdown(e,t){return{success:!0,data:(await Z.post(`/voice/ai-breakdown/confirm`,{parentTaskId:e,subTasks:t})).data,message:`创建成功`}}};function xf(){let e=F(!1),t=F(``),n=F(``),r=F(!1),i=F(null),a=Ia(()=>!!window.SpeechRecognition||!!window.webkitSpeechRecognition),o=null;async function s(s){if(!a.value){n.value=`当前浏览器不支持语音识别`;return}if(!e.value){o=new(window.SpeechRecognition||window.webkitSpeechRecognition),o.lang=`zh-CN`,o.interimResults=!1,o.maxAlternatives=1,o.continuous=!1,o.onresult=async e=>{let a=e.results[0][0].transcript;if(t.value=a,a.trim()){r.value=!0,n.value=``;try{let e=await bf.executeCommand(a);i.value=e,s?.(e),u(e.result.message)}catch(e){n.value=e?.message||`语音指令执行失败`,u(`指令执行失败`)}finally{r.value=!1}}},o.onerror=t=>{n.value=`语音识别错误: ${t.error}`,e.value=!1},o.onend=()=>{e.value=!1};try{await o.start(),e.value=!0}catch(t){n.value=t?.message||`启动语音识别失败`,e.value=!1}}}function c(){o&&o.stop(),e.value=!1}async function l(e,t,a){r.value=!0,n.value=``;try{let n=await bf.confirmCommand(e,t,a);return i.value=n,u(n.result.message),n}catch(e){return n.value=e?.message||`确认执行失败`,null}finally{r.value=!1}}function u(e){if(!(!e||!(`speechSynthesis`in window)))try{let t=new SpeechSynthesisUtterance(e);t.lang=`zh-CN`,t.rate=1,window.speechSynthesis.speak(t)}catch{}}return{isListening:e,isExecuting:r,recognizedText:t,error:n,lastResult:i,isSupported:a,startListening:s,stopListening:c,confirmAmbiguity:l}}var Sf={class:`task-list`},Cf={class:`page-header`},wf={class:`header-controls`},Tf={class:`sort-group`},Ef=[`title`],Df={key:0,class:`sort-order-icon`,viewBox:`0 0 24 24`,fill:`currentColor`},Of={key:1,class:`sort-order-icon`,viewBox:`0 0 24 24`,fill:`currentColor`},kf={class:`checkbox-wrapper`},Af=[`title`],jf={key:0,width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},Mf={key:1,class:`voice-pulse`},Nf={class:`task-info`},Pf={class:`info-item`},Ff={key:0,class:`info-item info-memory`},If={key:1,class:`info-item`},Lf=[`title`],Rf={class:`info-item info-user`},zf=[`disabled`,`title`],Bf={key:0,class:`voice-indicator`},Vf={key:0,class:`voice-dot`},Hf={class:`voice-text`},Uf={key:1,class:`voice-error`},Wf={key:2,class:`voice-candidates`},Gf=[`onClick`],Kf={class:`task-form`},qf={key:0,class:`empty`},Jf={key:1,class:`tasks-shell`},Yf={class:`tasks-scroll`},Xf=Pu(ir({__name:`TaskList`,setup(e){let t=F(``),n=F(1),r=F(0),i=F([]),a=F(!1),o=F(`createdAt`),s=F(`desc`),c=F(Q.isOnline()),l=F(0),u=F(0),d=Ia(()=>Q.getAllowPersist()&&yf.policy.allowPersist),f=F(!1),p=F(``),m=F(``),h=Ia(()=>{let e=m.value;if(!e)return``;try{return new URL(e).hostname}catch{return e.length>20?e.slice(0,20)+`...`:e}}),g=F(!1);function _(){let e=$.loadSession();f.value=!!e?.accessToken,e?(p.value=e.role===`admin`?`👑 ${e.userId?.slice(0,8)||``}`:`👤 ${e.userId?.slice(0,8)||``}`,yf.session=e):(p.value=``,yf.session=null),m.value=$.loadSettings().serverUrl}async function v(){g.value||await ge()}async function y(){try{await vf.logout(),$.clearSession(),_(),yf.policy={allowPersist:!0,allowSync:!0},Q.setAllowPersist(!0),b(`已登出云同步`,`success`)}catch(e){console.error(`Logout failed:`,e),b(`登出失败`,`error`)}}function b(e,t=`error`){window.showToast&&window.showToast(e,t)}let x=()=>{s.value=s.value===`asc`?`desc`:`asc`},S=()=>{c.value=Q.isOnline()},C=e=>{let t=new Date(e),n=new Date().getTime()-t.getTime(),r=Math.floor(n/6e4);return r<1?`刚刚`:r<60?`${r}分钟前`:r<1440?`${Math.floor(r/60)}小时前`:`${Math.floor(r/1440)}天前`},w=Ia(()=>{let e=i.value.filter(e=>!e.parentTaskId);return a.value?T(e):T(E(e,!1))}),T=e=>{let t={2:3,1:2,0:1};return[...e].sort((e,n)=>{let r=0;switch(o.value){case`priority`:r=t[n.priority]-t[e.priority],r===0&&(r=new Date(n.createdAt).getTime()-new Date(e.createdAt).getTime());break;case`completedAt`:{let t=e.isCompleted?new Date(e.updatedAt).getTime():0;r=(n.isCompleted?new Date(n.updatedAt).getTime():0)-t,r===0&&(r=new Date(n.createdAt).getTime()-new Date(e.createdAt).getTime());break}default:r=new Date(n.createdAt).getTime()-new Date(e.createdAt).getTime();break}return s.value===`asc`?-r:r}).map(e=>e.subTasks&&e.subTasks.length>0?{...e,subTasks:T(e.subTasks)}:e)},E=(e,t)=>e.map(e=>({...e})).filter(e=>{if(e.isCompleted===t)return e.subTasks&&e.subTasks.length>0&&(e.subTasks=E(e.subTasks,t)),!0;if(e.subTasks&&e.subTasks.length>0){let n=E(e.subTasks,t);if(n.length>0)return e.subTasks=n,!0}return!1}),ee=async()=>{try{let e=await du.getTasks();if(e.success&&e.data){i.value=e.data;let t=Q.loadSyncStatus();l.value=t.lastSyncTime,u.value=t.pendingChanges}}catch(e){console.error(`Failed to load tasks:`,e),alert(`加载任务失败`)}},D=async()=>{if(!t.value.trim()){alert(`请输入任务标题`);return}try{let e=await du.createTask({title:t.value.trim(),priority:n.value,taskType:r.value});e.success&&e.data&&(i.value.unshift(e.data),t.value=``,n.value=1,r.value=0)}catch(e){console.error(`Failed to create task:`,e),alert(`创建任务失败`)}},te=e=>{O(i.value,e)},O=(e,t)=>{for(let n=0;n0&&O(e[n].subTasks,t))return!0}return!1},ne=e=>{re(i.value,e)},re=(e,t)=>{for(let n=0;n0&&re(e[n].subTasks,t))return!0}return!1},ie=e=>{ae(i.value,e)},ae=(e,t)=>{for(let n=0;n0&&ae(e[n].subTasks,t))return!0}return!1},k=()=>{let e=new CustomEvent(`openHotKeySettings`,{detail:{modifiers:`Alt`,key:`X`,isEnabled:!0}});window.dispatchEvent(e)},oe=e=>{if(!d.value&&i.value.length>0)return e.preventDefault(),e.returnValue=`数据未同步且本地禁止落盘,退出将导致数据丢失。确定退出吗?`,e.returnValue},se=()=>{window.dispatchEvent(new CustomEvent(`openCloudSyncSettings`))},ce=xf(),le=F(!1),ue=F(``),de=F(``),fe=F([]),pe=``;async function me(){if(le.value){ce.stopListening(),le.value=!1;return}le.value=!0,de.value=``,ue.value=``,fe.value=[],await ce.startListening(e=>{le.value=!1;let t=e.result;t.ambiguity&&t.candidates?.length>0?(ue.value=t.message||`找到多个匹配项`,fe.value=t.candidates,pe=e.intent):t.success?(ue.value=t.message,ee(),setTimeout(()=>{ue.value=``},3e3)):ue.value=t.message||`指令未能识别`,ce.error.value&&(de.value=ce.error.value)})}async function he(e,t){if(!pe)return;let n=await ce.confirmAmbiguity(pe,e);n?.result.success?(ue.value=n.result.message,ee()):ue.value=n?.result.message||`执行失败`,fe.value=[],pe=``,setTimeout(()=>{ue.value=``},3e3)}let ge=async()=>{if(!$.loadSession()?.accessToken){b(`请先登录云同步`,`error`);return}g.value=!0;try{let{tasks:e}=await vf.syncTasks(i.value,[]);i.value=e,l.value=Date.now(),u.value=0,Q.saveSyncStatus({lastSyncTime:l.value,isOnline:!0,pendingChanges:0}),b(`云同步完成`,`success`)}catch(e){console.error(`Cloud sync failed:`,e),b(`同步失败:${e?.response?.data?.message||e?.message||`同步失败`}`,`error`)}finally{g.value=!1}},ve=e=>{let t=e.detail;_(),t?.enabled&&t?.isLoggedIn&&ye()},ye=async()=>{try{i.value=await vf.getTasks(),l.value=Date.now(),Q.saveSyncStatus({lastSyncTime:l.value,isOnline:!0,pendingChanges:0})}catch(e){console.error(`Pull cloud tasks failed:`,e)}};return yr(()=>{ee(),S(),_(),window.addEventListener(`online`,S),window.addEventListener(`offline`,S),window.addEventListener(`cloudSyncTasksRequested`,ge),window.addEventListener(`cloudSyncStateChanged`,ve),window.addEventListener(`beforeunload`,oe)}),Cr(()=>{window.removeEventListener(`online`,S),window.removeEventListener(`offline`,S),window.removeEventListener(`cloudSyncTasksRequested`,ge),window.removeEventListener(`cloudSyncStateChanged`,ve),window.removeEventListener(`beforeunload`,oe)}),(e,i)=>(V(),H(`div`,Sf,[U(`div`,Cf,[U(`div`,wf,[U(`div`,Tf,[L(U(`select`,{"onUpdate:modelValue":i[0]||=e=>o.value=e,class:`sort-select`},[...i[5]||=[U(`option`,{value:`createdAt`},`创建时间`,-1),U(`option`,{value:`completedAt`},`完成时间`,-1),U(`option`,{value:`priority`},`优先级`,-1)]],512),[[Zo,o.value]]),U(`button`,{type:`button`,class:`sort-select sort-order-btn`,onClick:x,title:s.value===`desc`?`切换为升序`:`切换为降序`},[s.value===`desc`?(V(),H(`svg`,Df,[...i[6]||=[U(`path`,{d:`M11 4h2v11h3l-4 4-4-4h3V4z`},null,-1)]])):(V(),H(`svg`,Of,[...i[7]||=[U(`path`,{d:`M11 20h2V9h3l-4-4-4 4h3v11z`},null,-1)]]))],8,Ef),U(`label`,kf,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":i[1]||=e=>a.value=e,class:`checkbox-input`},null,512),[[Yo,a.value]]),i[8]||=U(`span`,{class:`checkbox-label`},`已完成`,-1)]),U(`button`,{class:`hotkey-settings-btn`,onClick:k,title:`设置快捷键`},[...i[9]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3z`})],-1),U(`span`,null,`快捷键`,-1)]]),U(`button`,{class:`hotkey-settings-btn voice-btn`,onClick:me,title:le.value?`停止语音`:`语音输入`},[le.value?(V(),H(`span`,Mf,`⏺`)):(V(),H(`svg`,jf,[...i[10]||=[U(`path`,{d:`M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z`},null,-1),U(`path`,{d:`M19 10v2a7 7 0 0 1-14 0v-2`},null,-1),U(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`},null,-1),U(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`},null,-1)]]))],8,Af)])]),U(`div`,Nf,[U(`span`,Pf,`计数:`+A(w.value.length),1),d.value?G(``,!0):(V(),H(`span`,Ff,`⚡ 内存模式`)),l.value>0?(V(),H(`span`,If,[U(`span`,null,`更新:`+A(C(l.value)),1)])):G(``,!0),f.value?(V(),H(z,{key:3},[m.value?(V(),H(`span`,{key:0,class:`info-item`,title:m.value},`☁ `+A(h.value),9,Lf)):G(``,!0),U(`span`,Rf,A(p.value),1),U(`button`,{class:_e([`info-item info-sync-btn`,{syncing:g.value}]),disabled:g.value,onClick:v,title:g.value?`同步中...`:`同步到云端`},A(g.value?`⏳`:`🔄`),11,zf),U(`button`,{class:`info-item info-settings-btn`,onClick:se,title:`云同步设置`},` ⚙ `),U(`button`,{class:`info-item info-logout-btn`,onClick:y,title:`登出云同步`},` 登出 `)],64)):(V(),H(`button`,{key:2,class:`info-item info-login-btn`,onClick:se,title:`登录云同步`},` 🔑 登录 `))]),le.value||ue.value?(V(),H(`div`,Bf,[le.value?(V(),H(`span`,Vf)):G(``,!0),U(`span`,Hf,A(le.value?`正在聆听...`:ue.value),1),de.value?(V(),H(`span`,Uf,A(de.value),1)):G(``,!0),fe.value.length>0?(V(),H(`div`,Wf,[i[11]||=U(`span`,{class:`candidates-hint`},`找到多个匹配,请选择:`,-1),(V(!0),H(z,null,Nr(fe.value,e=>(V(),H(`button`,{key:e.id,class:`candidate-btn`,onClick:t=>he(e.id,e.title)},A(e.title),9,Gf))),128))])):G(``,!0)])):G(``,!0)]),U(`div`,Kf,[L(U(`input`,{"onUpdate:modelValue":i[2]||=e=>t.value=e,type:`text`,placeholder:`输入新任务...`,onKeyup:as(D,[`enter`]),class:`task-input`},null,544),[[Jo,t.value]]),L(U(`select`,{"onUpdate:modelValue":i[3]||=e=>n.value=e,class:`task-select`},[...i[12]||=[U(`option`,{value:0},`低`,-1),U(`option`,{value:1},`中`,-1),U(`option`,{value:2},`高`,-1)]],512),[[Zo,n.value]]),L(U(`select`,{"onUpdate:modelValue":i[4]||=e=>r.value=e,class:`task-select`},[...i[13]||=[U(`option`,{value:0},`📋 普通`,-1),U(`option`,{value:1},`🎙 会议`,-1)]],512),[[Zo,r.value]]),U(`button`,{onClick:D,class:`btn-add`},[...i[14]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`},[U(`line`,{x1:`12`,y1:`5`,x2:`12`,y2:`19`}),U(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`})],-1)]])]),w.value.length===0?(V(),H(`div`,qf,`暂无任务`)):(V(),H(`div`,Jf,[U(`div`,Yf,[(V(!0),H(z,null,Nr(w.value,e=>(V(),ea(of,{key:e.id,task:e,onUpdated:te,onDeleted:ne,onSubtaskCreated:ie},null,8,[`task`]))),128))])]))]))}}),[[`__scopeId`,`data-v-0b9b025b`]]),Zf={class:`hotkey-settings-dialog`},Qf={class:`dialog-body`},$f={class:`form-group`},ep={class:`form-group`},tp=[`value`],np={class:`form-group`},rp={class:`checkbox-label`},ip={class:`preview`},ap={class:`hotkey-preview`},op=Pu(ir({__name:`HotKeySettingsDialog`,setup(e){let t=F(!1),n=Rt({modifiers:`Alt`,key:`X`,isEnabled:!0}),r=`ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`.split(``);yr(()=>{window.addEventListener(`openHotKeySettings`,i)});function i(e){let r=e.detail;n.modifiers=r.modifiers,n.key=r.key,n.isEnabled=r.isEnabled,t.value=!0}function a(){t.value=!1}function o(){return n.isEnabled?`${n.modifiers.split(`,`).map(e=>e.trim()).join(` + `)} + ${n.key}`:`未启用`}function s(){let e=new CustomEvent(`hotKeyConfigChanged`,{detail:{modifiers:n.modifiers,key:n.key,isEnabled:n.isEnabled}});window.dispatchEvent(e)}function c(){n.modifiers=`Alt`,n.key=`X`,n.isEnabled=!0,s()}function l(){let e={modifiers:n.modifiers,key:n.key,isEnabled:n.isEnabled};window.chrome?.webview?.postMessage&&window.chrome.webview.postMessage(`HOTKEY_CONFIG:${JSON.stringify(e)}`),a()}return(e,i)=>t.value?(V(),H(`div`,{key:0,class:`hotkey-settings-overlay`,onClick:rs(a,[`self`])},[U(`div`,Zf,[U(`div`,{class:`dialog-header`},[i[3]||=U(`h2`,null,`⌨️ 设置快捷键`,-1),U(`button`,{class:`close-button`,onClick:a},`×`)]),U(`div`,Qf,[U(`div`,$f,[i[5]||=U(`label`,null,`修饰键:`,-1),L(U(`select`,{"onUpdate:modelValue":i[0]||=e=>n.modifiers=e,onChange:s},[...i[4]||=[la(``,6)]],544),[[Zo,n.modifiers]])]),U(`div`,ep,[i[6]||=U(`label`,null,`主键:`,-1),L(U(`select`,{"onUpdate:modelValue":i[1]||=e=>n.key=e,onChange:s},[(V(),H(z,null,Nr(r,e=>U(`option`,{key:e,value:e},A(e),9,tp)),64))],544),[[Zo,n.key]])]),U(`div`,np,[U(`label`,rp,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":i[2]||=e=>n.isEnabled=e,onChange:s},null,544),[[Yo,n.isEnabled]]),i[7]||=U(`span`,null,`启用快捷键`,-1)])]),U(`div`,ip,[i[8]||=U(`strong`,null,`当前快捷键:`,-1),U(`span`,ap,A(o()),1)])]),U(`div`,{class:`dialog-footer`},[U(`button`,{class:`btn btn-secondary`,onClick:c},`重置默认`),U(`button`,{class:`btn btn-primary`,onClick:l},`保存`)])])])):G(``,!0)}}),[[`__scopeId`,`data-v-ae88d5d6`]]),sp={class:`dialog`},cp={class:`dialog-body`},lp={class:`section`},up={class:`checkbox-row`},dp={class:`section`},fp={class:`form-row`},pp=[`disabled`],mp={key:0,class:`hint`},hp={class:`mono`},gp={class:`probe-title`},_p={class:`probe-desc`},vp={class:`section`},yp={key:0,class:`logged-in`},bp={class:`hint`},xp={class:`mono`},Sp={class:`sync-status`},Cp={class:`sync-status-item`},wp={class:`sync-status-item`},Tp={class:`mono`},Ep={class:`sync-status-item`},Dp={key:0,class:`policy-info`},Op={class:`policy-item`},kp={class:`policy-item`},Ap={class:`button-row`},jp=[`disabled`],Mp={key:0},Np={key:1},Pp={key:1,class:`login-form`},Fp={class:`form-row`},Ip=[`disabled`],Lp=Pu(ir({__name:`CloudSyncSettingsDialog`,setup(e){let t=F(!1),n=F(!1),r=F(``),i=F(``),a=F(!1),o=F(null),s=F(``),c=F(``),l=F(!1),u=F(null),d=F({lastSyncTime:0,isOnline:!1,pendingChanges:0}),f=F(!1),p=Ia(()=>n.value&&v.value&&u.value?.allowSync!==!1),m=async()=>{b();try{let e=(await X.get(`/api/cloudSyncProxySettings`)).data?.data?.serverUrl??``;i.value=e,r.value=e||`http://localhost:5173`}catch{let e=$.loadSettings();i.value=e.serverUrl,r.value=e.serverUrl||`http://localhost:5173`}n.value=$.loadSettings().enabled!==!1,g(),v.value&&h()},h=async()=>{if(!$.loadSession()?.accessToken)return u.value=null,yf.policy={allowPersist:!0,allowSync:!0},Q.setAllowPersist(!0),!1;try{let e=await vf.getSecurityPolicy();return u.value=e,yf.policy=e,Q.setAllowPersist(e.allowPersist),!0}catch(e){return X.isAxiosError(e)&&e.response?.status===401?($.clearSession(),b(),u.value=null,yf.policy={allowPersist:!0,allowSync:!0},Q.setAllowPersist(!0),w(),!1):(console.error(`Failed to fetch security policy:`,e),!1)}},g=()=>{d.value=Q.loadSyncStatus()},_=e=>{if(!e)return`从未同步`;let t=Date.now()-e;if(t<6e4)return`刚刚`;if(t<36e5)return`${Math.floor(t/6e4)} 分钟前`;if(t<864e5)return`${Math.floor(t/36e5)} 小时前`;let n=new Date(e),r=new Date;return n.toDateString()===r.toDateString()?`今天 ${n.toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`})}`:n.toLocaleString(`zh-CN`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`})},v=F(!1),y=F(``),b=()=>{let e=$.loadSession();v.value=!!e?.accessToken,y.value=e?`${e.role||`User`}@${e.userId?e.userId.slice(0,8):``}`:``},x=Ia(()=>!n.value||!i.value||!s.value.trim()||!c.value?!1:!l.value),S=(e,t=`error`)=>{window.showToast?window.showToast(e,t):window.alert(e)},C=e=>{let t=e.trim();if(!t)return{ok:!1,message:`请输入服务端地址`};let n;try{n=new URL(t)}catch{return{ok:!1,message:`地址格式不正确,请包含协议(http:// 或 https://)`}}if(n.protocol!==`http:`&&n.protocol!==`https:`)return{ok:!1,message:`仅支持 http:// 或 https:// 地址`};if(!n.hostname)return{ok:!1,message:`地址缺少主机名(host)`};let r=n.origin,i=n.pathname&&n.pathname!==`/`?`已忽略地址中的路径部分,仅保留 origin。`:void 0;return{ok:!0,normalized:r.replace(/\/+$/,``),warn:i}},w=()=>{let e=$.loadSettings(),t=$.loadSession(),n=new CustomEvent(`cloudSyncStateChanged`,{detail:{enabled:e.enabled,serverUrl:i.value||e.serverUrl,isLoggedIn:!!t?.accessToken}});window.dispatchEvent(n)},T=()=>{if(n.value&&!i.value){n.value=!1,S(`启用云同步前请先配置并保存服务端地址`,`error`);return}$.saveSettings({serverUrl:i.value,enabled:n.value}),w()},E=async()=>{let e=C(r.value);if(!e.ok||!e.normalized){S(e.message||`地址不合法`,`error`);return}i.value=e.normalized;try{await X.post(`/api/cloudSyncProxySettings`,{serverUrl:i.value})}catch{$.saveSettings({serverUrl:i.value,enabled:n.value})}e.warn?S(e.warn,`success`):S(`地址已保存`,`success`),a.value=!0;try{let e=await vf.probeServerUrl(i.value);o.value={type:e.type,title:e.title,desc:e.description}}catch{o.value={type:`error`,title:`探测失败`,desc:`无法连接到服务端,请检查地址是否正确`}}finally{a.value=!1}w()},ee=async()=>{if(x.value){l.value=!0;try{if(await vf.login({userName:s.value.trim(),password:c.value}),b(),!await h()){S(`登录失败:无法获取安全策略,请检查服务端认证配置`,`error`);return}S(`登录成功`,`success`),w()}finally{l.value=!1}}},D=async()=>{await vf.logout(),b(),u.value=null,yf.policy={allowPersist:!0,allowSync:!0},Q.setAllowPersist(!0),S(`已登出`,`success`),w()},te=async()=>{if(!(!p.value||f.value)){f.value=!0;try{g();let e=new CustomEvent(`cloudSyncTasksRequested`);window.dispatchEvent(e),setTimeout(()=>g(),500)}finally{setTimeout(()=>{f.value=!1},1500)}}};function O(){m(),o.value=null,v.value||(s.value||=`admin`,c.value||=`123456`),t.value=!0}function ne(){t.value=!1}return yr(()=>{window.addEventListener(`openCloudSyncSettings`,O),g(),v.value&&h()}),(e,m)=>t.value?(V(),H(`div`,{key:0,class:`overlay`,onClick:rs(ne,[`self`])},[U(`div`,sp,[U(`div`,{class:`dialog-header`},[m[4]||=U(`h2`,null,`☁️ 云同步设置`,-1),U(`button`,{class:`close-button`,onClick:ne},`×`)]),U(`div`,cp,[U(`div`,lp,[m[6]||=U(`div`,{class:`section-title`},`同步开关`,-1),U(`label`,up,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":m[0]||=e=>n.value=e,onChange:T},null,544),[[Yo,n.value]]),m[5]||=U(`span`,null,`启用云同步(支持双向同步)`,-1)]),m[7]||=U(`div`,{class:`hint`},` 启用后,任务将与云端同步,支持双向同步功能。 `,-1)]),U(`div`,dp,[m[9]||=U(`div`,{class:`section-title`},`服务端地址`,-1),U(`div`,fp,[L(U(`input`,{"onUpdate:modelValue":m[1]||=e=>r.value=e,class:`text-input`,type:`text`,placeholder:`例如:https://example.com`,autocomplete:`off`},null,512),[[Jo,r.value]]),U(`button`,{class:`btn btn-secondary`,disabled:a.value,onClick:E},A(a.value?`探测中...`:`保存并探测`),9,pp)]),i.value?(V(),H(`div`,mp,[m[8]||=ca(` 已保存:`,-1),U(`span`,hp,A(i.value),1)])):G(``,!0),o.value?(V(),H(`div`,{key:1,class:_e([`probe`,o.value.type])},[U(`div`,gp,A(o.value.title),1),U(`div`,_p,A(o.value.desc),1)],2)):G(``,!0)]),U(`div`,vp,[m[17]||=U(`div`,{class:`section-title`},`登录`,-1),v.value?(V(),H(`div`,yp,[U(`div`,bp,[m[10]||=ca(` 已登录:`,-1),U(`span`,xp,A(y.value),1)]),U(`div`,Sp,[U(`div`,Cp,[m[11]||=U(`span`,{class:`label`},`连接状态:`,-1),U(`span`,{class:_e(d.value.isOnline?`success`:`offline`)},A(d.value.isOnline?`在线`:`离线`),3)]),U(`div`,wp,[m[12]||=U(`span`,{class:`label`},`上次同步:`,-1),U(`span`,Tp,A(d.value.lastSyncTime>0?_(d.value.lastSyncTime):`从未同步`),1)]),U(`div`,Ep,[m[13]||=U(`span`,{class:`label`},`待同步:`,-1),U(`span`,{class:_e(d.value.pendingChanges>0?`warn`:``)},A(d.value.pendingChanges)+` 项变更 `,3)])]),u.value?(V(),H(`div`,Dp,[U(`div`,Op,[m[14]||=U(`span`,{class:`label`},`落盘策略:`,-1),U(`span`,{class:_e(u.value.allowPersist?`success`:`warn`)},A(u.value.allowPersist?`允许(本地持久化)`:`禁止(仅限内存)`),3)]),U(`div`,kp,[m[15]||=U(`span`,{class:`label`},`同步权限:`,-1),U(`span`,{class:_e(u.value.allowSync?`success`:`warn`)},A(u.value.allowSync?`正常`:`已禁用`),3)])])):G(``,!0),U(`div`,Ap,[U(`button`,{class:`btn btn-secondary`,onClick:D},`登出`),U(`button`,{class:`btn btn-outline`,disabled:!p.value||f.value,onClick:te},[f.value?(V(),H(`span`,Mp,`同步中...`)):(V(),H(`span`,Np,`立即同步`))],8,jp)])])):(V(),H(`div`,Pp,[U(`div`,Fp,[L(U(`input`,{"onUpdate:modelValue":m[2]||=e=>s.value=e,class:`text-input`,type:`text`,placeholder:`用户名`,autocomplete:`username`},null,512),[[Jo,s.value]]),L(U(`input`,{"onUpdate:modelValue":m[3]||=e=>c.value=e,class:`text-input`,type:`password`,placeholder:`密码`,autocomplete:`current-password`},null,512),[[Jo,c.value]]),U(`button`,{class:`btn btn-primary`,disabled:!x.value,onClick:ee},A(l.value?`登录中...`:`登录`),9,Ip)]),m[16]||=U(`div`,{class:`hint`},` 说明:服务端使用 Bearer 会话(AccessToken=SessionId)。会话默认仅保存在本次运行(sessionStorage)。 `,-1)]))])]),U(`div`,{class:`dialog-footer`},[U(`button`,{class:`btn btn-secondary`,onClick:ne},`关闭`)])])])):G(``,!0)}}),[[`__scopeId`,`data-v-ab8b3536`]]),Rp={class:`app`},zp={class:`toast-content`},Bp={class:`toast-icon`},Vp={class:`toast-message`};ls(ir({__name:`App`,setup(e){let t=F(``),n=F(`error`),r=F(!1),i=(e,i=`error`)=>{t.value=e,n.value=i,r.value=!0,setTimeout(()=>{r.value=!1},5e3)};return yr(()=>{window.showToast=i}),(e,i)=>(V(),H(`div`,Rp,[W(Xf),W(op),W(Lp),W(Qa,{name:`toast`},{default:jn(()=>[r.value?(V(),H(`div`,{key:0,class:_e([`toast`,n.value])},[U(`div`,zp,[U(`span`,Bp,A(n.value===`error`?`❌`:`✅`),1),U(`span`,Vp,A(t.value),1)]),U(`button`,{onClick:i[0]||=e=>r.value=!1,class:`toast-close`},`×`)],2)):G(``,!0)]),_:1})]))}})).mount(`#app`); \ No newline at end of file diff --git a/src/Hua.Todo.Host/wwwroot/spa/assets/index-xkexrwRb.js b/src/Hua.Todo.Host/wwwroot/spa/assets/index-xkexrwRb.js new file mode 100644 index 0000000..80fcf52 --- /dev/null +++ b/src/Hua.Todo.Host/wwwroot/spa/assets/index-xkexrwRb.js @@ -0,0 +1,12 @@ +var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function n(e){let t=Object.create(null);for(let n of e.split(`,`))t[n]=1;return e=>e in t}var r={},i=[],a=()=>{},o=()=>!1,s=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),c=e=>e.startsWith(`onUpdate:`),l=Object.assign,u=(e,t)=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)},d=Object.prototype.hasOwnProperty,f=(e,t)=>d.call(e,t),p=Array.isArray,m=e=>C(e)===`[object Map]`,h=e=>C(e)===`[object Set]`,g=e=>C(e)===`[object Date]`,_=e=>typeof e==`function`,v=e=>typeof e==`string`,y=e=>typeof e==`symbol`,b=e=>typeof e==`object`&&!!e,x=e=>(b(e)||_(e))&&_(e.then)&&_(e.catch),S=Object.prototype.toString,C=e=>S.call(e),w=e=>C(e).slice(8,-1),T=e=>C(e)===`[object Object]`,E=e=>v(e)&&e!==`NaN`&&e[0]!==`-`&&``+parseInt(e,10)===e,ee=n(`,key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted`),D=e=>{let t=Object.create(null);return(n=>t[n]||(t[n]=e(n)))},te=/-\w/g,O=D(e=>e.replace(te,e=>e.slice(1).toUpperCase())),ne=/\B([A-Z])/g,re=D(e=>e.replace(ne,`-$1`).toLowerCase()),ie=D(e=>e.charAt(0).toUpperCase()+e.slice(1)),ae=D(e=>e?`on${ie(e)}`:``),k=(e,t)=>!Object.is(e,t),oe=(e,...t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},ce=e=>{let t=parseFloat(e);return isNaN(t)?e:t},le=e=>{let t=v(e)?Number(e):NaN;return isNaN(t)?e:t},ue,de=()=>ue||=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{};function fe(e){if(p(e)){let t={};for(let n=0;n{if(e){let n=e.split(me);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function _e(e){let t=``;if(v(e))t=e;else if(p(e))for(let n=0;nSe(e,t))}var we=e=>!!(e&&e.__v_isRef===!0),A=e=>v(e)?e:e==null?``:p(e)||b(e)&&(e.toString===S||!_(e.toString))?we(e)?A(e.value):JSON.stringify(e,Te,2):String(e),Te=(e,t)=>we(t)?Te(e,t.value):m(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((e,[t,n],r)=>(e[Ee(t,r)+` =>`]=n,e),{})}:h(t)?{[`Set(${t.size})`]:[...t.values()].map(e=>Ee(e))}:y(t)?Ee(t):b(t)&&!p(t)&&!T(t)?String(t):t,Ee=(e,t=``)=>y(e)?`Symbol(${e.description??t})`:e,De,Oe=class{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.__v_skip=!0,this.parent=De,!e&&De&&(this.index=(De.scopes||=[]).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,t;if(this.scopes)for(e=0,t=this.scopes.length;e0&&--this._on===0&&(De=this.prevScope,this.prevScope=void 0)}stop(e){if(this._active){this._active=!1;let t,n;for(t=0,n=this.effects.length;t0)return;if(Pe){let e=Pe;for(Pe=void 0;e;){let t=e.next;e.next=void 0,e.flags&=-9,e=t}}let e;for(;Ne;){let t=Ne;for(Ne=void 0;t;){let n=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(t){e||=t}t=n}}if(e)throw e}function Re(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function ze(e){let t,n=e.depsTail,r=n;for(;r;){let e=r.prevDep;r.version===-1?(r===n&&(n=e),He(r),Ue(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=e}e.deps=t,e.depsTail=n}function Be(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Ve(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Ve(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Ye)||(e.globalVersion=Ye,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Be(e))))return;e.flags|=2;let t=e.dep,n=j,r=We;j=e,We=!0;try{Re(e);let n=e.fn(e._value);(t.version===0||k(n,e._value))&&(e.flags|=128,e._value=n,t.version++)}catch(e){throw t.version++,e}finally{j=n,We=r,ze(e),e.flags&=-3}}function He(e,t=!1){let{dep:n,prevSub:r,nextSub:i}=e;if(r&&(r.nextSub=i,e.prevSub=void 0),i&&(i.prevSub=r,e.nextSub=void 0),n.subs===e&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let e=n.computed.deps;e;e=e.nextDep)He(e,!0)}!t&&!--n.sc&&n.map&&n.map.delete(n.key)}function Ue(e){let{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}var We=!0,Ge=[];function Ke(){Ge.push(We),We=!1}function qe(){let e=Ge.pop();We=e===void 0?!0:e}function Je(e){let{cleanup:t}=e;if(e.cleanup=void 0,t){let e=j;j=void 0;try{t()}finally{j=e}}}var Ye=0,Xe=class{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}},Ze=class{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!j||!We||j===this.computed)return;let t=this.activeLink;if(t===void 0||t.sub!==j)t=this.activeLink=new Xe(j,this),j.deps?(t.prevDep=j.depsTail,j.depsTail.nextDep=t,j.depsTail=t):j.deps=j.depsTail=t,Qe(t);else if(t.version===-1&&(t.version=this.version,t.nextDep)){let e=t.nextDep;e.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=e),t.prevDep=j.depsTail,t.nextDep=void 0,j.depsTail.nextDep=t,j.depsTail=t,j.deps===t&&(j.deps=e)}return t}trigger(e){this.version++,Ye++,this.notify(e)}notify(e){Ie();try{for(let e=this.subs;e;e=e.prevSub)e.sub.notify()&&e.sub.dep.notify()}finally{Le()}}};function Qe(e){if(e.dep.sc++,e.sub.flags&4){let t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let e=t.deps;e;e=e.nextDep)Qe(e)}let n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}var $e=new WeakMap,et=Symbol(``),tt=Symbol(``),nt=Symbol(``);function M(e,t,n){if(We&&j){let t=$e.get(e);t||$e.set(e,t=new Map);let r=t.get(n);r||(t.set(n,r=new Ze),r.map=t,r.key=n),r.track()}}function rt(e,t,n,r,i,a){let o=$e.get(e);if(!o){Ye++;return}let s=e=>{e&&e.trigger()};if(Ie(),t===`clear`)o.forEach(s);else{let i=p(e),a=i&&E(n);if(i&&n===`length`){let e=Number(r);o.forEach((t,n)=>{(n===`length`||n===nt||!y(n)&&n>=e)&&s(t)})}else switch((n!==void 0||o.has(void 0))&&s(o.get(n)),a&&s(o.get(nt)),t){case`add`:i?a&&s(o.get(`length`)):(s(o.get(et)),m(e)&&s(o.get(tt)));break;case`delete`:i||(s(o.get(et)),m(e)&&s(o.get(tt)));break;case`set`:m(e)&&s(o.get(et));break}}Le()}function it(e){let t=N(e);return t===e?t:(M(t,`iterate`,nt),Wt(e)?t:t.map(qt))}function at(e){return M(e=N(e),`iterate`,nt),e}function ot(e,t){return Ut(e)?Ht(e)?Jt(qt(t)):Jt(t):qt(t)}var st={__proto__:null,[Symbol.iterator](){return ct(this,Symbol.iterator,e=>ot(this,e))},concat(...e){return it(this).concat(...e.map(e=>p(e)?it(e):e))},entries(){return ct(this,`entries`,e=>(e[1]=ot(this,e[1]),e))},every(e,t){return ut(this,`every`,e,t,void 0,arguments)},filter(e,t){return ut(this,`filter`,e,t,e=>e.map(e=>ot(this,e)),arguments)},find(e,t){return ut(this,`find`,e,t,e=>ot(this,e),arguments)},findIndex(e,t){return ut(this,`findIndex`,e,t,void 0,arguments)},findLast(e,t){return ut(this,`findLast`,e,t,e=>ot(this,e),arguments)},findLastIndex(e,t){return ut(this,`findLastIndex`,e,t,void 0,arguments)},forEach(e,t){return ut(this,`forEach`,e,t,void 0,arguments)},includes(...e){return ft(this,`includes`,e)},indexOf(...e){return ft(this,`indexOf`,e)},join(e){return it(this).join(e)},lastIndexOf(...e){return ft(this,`lastIndexOf`,e)},map(e,t){return ut(this,`map`,e,t,void 0,arguments)},pop(){return pt(this,`pop`)},push(...e){return pt(this,`push`,e)},reduce(e,...t){return dt(this,`reduce`,e,t)},reduceRight(e,...t){return dt(this,`reduceRight`,e,t)},shift(){return pt(this,`shift`)},some(e,t){return ut(this,`some`,e,t,void 0,arguments)},splice(...e){return pt(this,`splice`,e)},toReversed(){return it(this).toReversed()},toSorted(e){return it(this).toSorted(e)},toSpliced(...e){return it(this).toSpliced(...e)},unshift(...e){return pt(this,`unshift`,e)},values(){return ct(this,`values`,e=>ot(this,e))}};function ct(e,t,n){let r=at(e),i=r[t]();return r!==e&&!Wt(e)&&(i._next=i.next,i.next=()=>{let e=i._next();return e.done||(e.value=n(e.value)),e}),i}var lt=Array.prototype;function ut(e,t,n,r,i,a){let o=at(e),s=o!==e&&!Wt(e),c=o[t];if(c!==lt[t]){let t=c.apply(e,a);return s?qt(t):t}let l=n;o!==e&&(s?l=function(t,r){return n.call(this,ot(e,t),r,e)}:n.length>2&&(l=function(t,r){return n.call(this,t,r,e)}));let u=c.call(o,l,r);return s&&i?i(u):u}function dt(e,t,n,r){let i=at(e),a=i!==e&&!Wt(e),o=n,s=!1;i!==e&&(a?(s=r.length===0,o=function(t,r,i){return s&&(s=!1,t=ot(e,t)),n.call(this,t,ot(e,r),i,e)}):n.length>3&&(o=function(t,r,i){return n.call(this,t,r,i,e)}));let c=i[t](o,...r);return s?ot(e,c):c}function ft(e,t,n){let r=N(e);M(r,`iterate`,nt);let i=r[t](...n);return(i===-1||i===!1)&&Gt(n[0])?(n[0]=N(n[0]),r[t](...n)):i}function pt(e,t,n=[]){Ke(),Ie();let r=N(e)[t].apply(e,n);return Le(),qe(),r}var mt=n(`__proto__,__v_isRef,__isVue`),ht=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!==`arguments`&&e!==`caller`).map(e=>Symbol[e]).filter(y));function gt(e){y(e)||(e=String(e));let t=N(this);return M(t,`has`,e),t.hasOwnProperty(e)}var _t=class{constructor(e=!1,t=!1){this._isReadonly=e,this._isShallow=t}get(e,t,n){if(t===`__v_skip`)return e.__v_skip;let r=this._isReadonly,i=this._isShallow;if(t===`__v_isReactive`)return!r;if(t===`__v_isReadonly`)return r;if(t===`__v_isShallow`)return i;if(t===`__v_raw`)return n===(r?i?Ft:Pt:i?Nt:Mt).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;let a=p(e);if(!r){let e;if(a&&(e=st[t]))return e;if(t===`hasOwnProperty`)return gt}let o=Reflect.get(e,t,P(e)?e:n);if((y(t)?ht.has(t):mt(t))||(r||M(e,`get`,t),i))return o;if(P(o)){let e=a&&E(t)?o:o.value;return r&&b(e)?Bt(e):e}return b(o)?r?Bt(o):Rt(o):o}},vt=class extends _t{constructor(e=!1){super(!1,e)}set(e,t,n,r){let i=e[t],a=p(e)&&E(t);if(!this._isShallow){let e=Ut(i);if(!Wt(n)&&!Ut(n)&&(i=N(i),n=N(n)),!a&&P(i)&&!P(n))return e||(i.value=n),!0}let o=a?Number(t)e,wt=e=>Reflect.getPrototypeOf(e);function Tt(e,t,n){return function(...r){let i=this.__v_raw,a=N(i),o=m(a),s=e===`entries`||e===Symbol.iterator&&o,c=e===`keys`&&o,u=i[e](...r),d=n?Ct:t?Jt:qt;return!t&&M(a,`iterate`,c?tt:et),l(Object.create(u),{next(){let{value:e,done:t}=u.next();return t?{value:e,done:t}:{value:s?[d(e[0]),d(e[1])]:d(e),done:t}}})}}function Et(e){return function(...t){return e===`delete`?!1:e===`clear`?void 0:this}}function Dt(e,t){let n={get(n){let r=this.__v_raw,i=N(r),a=N(n);e||(k(n,a)&&M(i,`get`,n),M(i,`get`,a));let{has:o}=wt(i),s=t?Ct:e?Jt:qt;if(o.call(i,n))return s(r.get(n));if(o.call(i,a))return s(r.get(a));r!==i&&r.get(n)},get size(){let t=this.__v_raw;return!e&&M(N(t),`iterate`,et),t.size},has(t){let n=this.__v_raw,r=N(n),i=N(t);return e||(k(t,i)&&M(r,`has`,t),M(r,`has`,i)),t===i?n.has(t):n.has(t)||n.has(i)},forEach(n,r){let i=this,a=i.__v_raw,o=N(a),s=t?Ct:e?Jt:qt;return!e&&M(o,`iterate`,et),a.forEach((e,t)=>n.call(r,s(e),s(t),i))}};return l(n,e?{add:Et(`add`),set:Et(`set`),delete:Et(`delete`),clear:Et(`clear`)}:{add(e){let n=N(this),r=wt(n),i=N(e),a=!t&&!Wt(e)&&!Ut(e)?i:e;return r.has.call(n,a)||k(e,a)&&r.has.call(n,e)||k(i,a)&&r.has.call(n,i)||(n.add(a),rt(n,`add`,a,a)),this},set(e,n){!t&&!Wt(n)&&!Ut(n)&&(n=N(n));let r=N(this),{has:i,get:a}=wt(r),o=i.call(r,e);o||=(e=N(e),i.call(r,e));let s=a.call(r,e);return r.set(e,n),o?k(n,s)&&rt(r,`set`,e,n,s):rt(r,`add`,e,n),this},delete(e){let t=N(this),{has:n,get:r}=wt(t),i=n.call(t,e);i||=(e=N(e),n.call(t,e));let a=r?r.call(t,e):void 0,o=t.delete(e);return i&&rt(t,`delete`,e,void 0,a),o},clear(){let e=N(this),t=e.size!==0,n=e.clear();return t&&rt(e,`clear`,void 0,void 0,void 0),n}}),[`keys`,`values`,`entries`,Symbol.iterator].forEach(r=>{n[r]=Tt(r,e,t)}),n}function Ot(e,t){let n=Dt(e,t);return(t,r,i)=>r===`__v_isReactive`?!e:r===`__v_isReadonly`?e:r===`__v_raw`?t:Reflect.get(f(n,r)&&r in t?n:t,r,i)}var kt={get:Ot(!1,!1)},At={get:Ot(!1,!0)},jt={get:Ot(!0,!1)},Mt=new WeakMap,Nt=new WeakMap,Pt=new WeakMap,Ft=new WeakMap;function It(e){switch(e){case`Object`:case`Array`:return 1;case`Map`:case`Set`:case`WeakMap`:case`WeakSet`:return 2;default:return 0}}function Lt(e){return e.__v_skip||!Object.isExtensible(e)?0:It(w(e))}function Rt(e){return Ut(e)?e:Vt(e,!1,bt,kt,Mt)}function zt(e){return Vt(e,!1,St,At,Nt)}function Bt(e){return Vt(e,!0,xt,jt,Pt)}function Vt(e,t,n,r,i){if(!b(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;let a=Lt(e);if(a===0)return e;let o=i.get(e);if(o)return o;let s=new Proxy(e,a===2?r:n);return i.set(e,s),s}function Ht(e){return Ut(e)?Ht(e.__v_raw):!!(e&&e.__v_isReactive)}function Ut(e){return!!(e&&e.__v_isReadonly)}function Wt(e){return!!(e&&e.__v_isShallow)}function Gt(e){return e?!!e.__v_raw:!1}function N(e){let t=e&&e.__v_raw;return t?N(t):e}function Kt(e){return!f(e,`__v_skip`)&&Object.isExtensible(e)&&se(e,`__v_skip`,!0),e}var qt=e=>b(e)?Rt(e):e,Jt=e=>b(e)?Bt(e):e;function P(e){return e?e.__v_isRef===!0:!1}function F(e){return Yt(e,!1)}function Yt(e,t){return P(e)?e:new Xt(e,t)}var Xt=class{constructor(e,t){this.dep=new Ze,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=t?e:N(e),this._value=t?e:qt(e),this.__v_isShallow=t}get value(){return this.dep.track(),this._value}set value(e){let t=this._rawValue,n=this.__v_isShallow||Wt(e)||Ut(e);e=n?e:N(e),k(e,t)&&(this._rawValue=e,this._value=n?e:qt(e),this.dep.trigger())}};function Zt(e){return P(e)?e.value:e}var Qt={get:(e,t,n)=>t===`__v_raw`?e:Zt(Reflect.get(e,t,n)),set:(e,t,n,r)=>{let i=e[t];return P(i)&&!P(n)?(i.value=n,!0):Reflect.set(e,t,n,r)}};function $t(e){return Ht(e)?e:new Proxy(e,Qt)}var en=class{constructor(e,t,n){this.fn=e,this.setter=t,this._value=void 0,this.dep=new Ze(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Ye-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!t,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&j!==this)return Fe(this,!0),!0}get value(){let e=this.dep.track();return Ve(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}};function tn(e,t,n=!1){let r,i;return _(e)?r=e:(r=e.get,i=e.set),new en(r,i,n)}var nn={},rn=new WeakMap,an=void 0;function on(e,t=!1,n=an){if(n){let t=rn.get(n);t||rn.set(n,t=[]),t.push(e)}}function sn(e,t,n=r){let{immediate:i,deep:o,once:s,scheduler:c,augmentJob:l,call:d}=n,f=e=>o?e:Wt(e)||o===!1||o===0?cn(e,1):cn(e),m,h,g,v,y=!1,b=!1;if(P(e)?(h=()=>e.value,y=Wt(e)):Ht(e)?(h=()=>f(e),y=!0):p(e)?(b=!0,y=e.some(e=>Ht(e)||Wt(e)),h=()=>e.map(e=>{if(P(e))return e.value;if(Ht(e))return f(e);if(_(e))return d?d(e,2):e()})):h=_(e)?t?d?()=>d(e,2):e:()=>{if(g){Ke();try{g()}finally{qe()}}let t=an;an=m;try{return d?d(e,3,[v]):e(v)}finally{an=t}}:a,t&&o){let e=h,t=o===!0?1/0:o;h=()=>cn(e(),t)}let x=ke(),S=()=>{m.stop(),x&&x.active&&u(x.effects,m)};if(s&&t){let e=t;t=(...t)=>{e(...t),S()}}let C=b?Array(e.length).fill(nn):nn,w=e=>{if(!(!(m.flags&1)||!m.dirty&&!e))if(t){let e=m.run();if(o||y||(b?e.some((e,t)=>k(e,C[t])):k(e,C))){g&&g();let n=an;an=m;try{let n=[e,C===nn?void 0:b&&C[0]===nn?[]:C,v];C=e,d?d(t,3,n):t(...n)}finally{an=n}}}else m.run()};return l&&l(w),m=new je(h),m.scheduler=c?()=>c(w,!1):w,v=e=>on(e,!1,m),g=m.onStop=()=>{let e=rn.get(m);if(e){if(d)d(e,4);else for(let t of e)t();rn.delete(m)}},t?i?w(!0):C=m.run():c?c(w.bind(null,!0),!0):m.run(),S.pause=m.pause.bind(m),S.resume=m.resume.bind(m),S.stop=S,S}function cn(e,t=1/0,n){if(t<=0||!b(e)||e.__v_skip||(n||=new Map,(n.get(e)||0)>=t))return e;if(n.set(e,t),t--,P(e))cn(e.value,t,n);else if(p(e))for(let r=0;r{cn(e,t,n)});else if(T(e)){for(let r in e)cn(e[r],t,n);for(let r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&cn(e[r],t,n)}return e}function ln(e,t,n,r){try{return r?e(...r):e()}catch(e){dn(e,t,n)}}function un(e,t,n,r){if(_(e)){let i=ln(e,t,n,r);return i&&x(i)&&i.catch(e=>{dn(e,t,n)}),i}if(p(e)){let i=[];for(let a=0;a>>1,i=I[r],a=En(i);a=En(n)?I.push(e):I.splice(bn(t),0,e),e.flags|=1,Sn()}}function Sn(){vn||=_n.then(Dn)}function Cn(e){p(e)?mn.push(...e):hn&&e.id===-1?hn.splice(gn+1,0,e):e.flags&1||(mn.push(e),e.flags|=1),Sn()}function wn(e,t,n=pn+1){for(;nEn(e)-En(t));if(mn.length=0,hn){hn.push(...e);return}for(hn=e,gn=0;gne.id==null?e.flags&2?-1:1/0:e.id;function Dn(e){try{for(pn=0;pn{r._d&&Qi(-1);let i=An(t),a;try{a=e(...n)}finally{An(i),r._d&&Qi(1)}return a};return r._n=!0,r._c=!0,r._d=!0,r}function L(e,t){if(On===null)return e;let n=Na(On),i=e.dirs||=[];for(let e=0;e1)return n&&_(t)?t.call(r&&r.proxy):t}}var Fn=Symbol.for(`v-scx`),In=()=>Pn(Fn);function Ln(e,t,n){return Rn(e,t,n)}function Rn(e,t,n=r){let{immediate:i,deep:o,flush:s,once:c}=n,u=l({},n),d=t&&i||!t&&s!==`post`,f;if(wa){if(s===`sync`){let e=In();f=e.__watcherHandles||=[]}else if(!d){let e=()=>{};return e.stop=a,e.resume=a,e.pause=a,e}}let p=K;u.call=(e,t,n)=>un(e,p,t,n);let m=!1;s===`post`?u.scheduler=e=>{Ni(e,p&&p.suspense)}:s!==`sync`&&(m=!0,u.scheduler=(e,t)=>{t?e():xn(e)}),u.augmentJob=e=>{t&&(e.flags|=4),m&&(e.flags|=2,p&&(e.id=p.uid,e.i=p))};let h=sn(e,t,u);return wa&&(f?f.push(h):d&&h()),h}function zn(e,t,n){let r=this.proxy,i=v(e)?e.includes(`.`)?Bn(r,e):()=>r[e]:e.bind(r,r),a;_(t)?a=t:(a=t.handler,n=t);let o=xa(this),s=Rn(i,a.bind(r),n);return o(),s}function Bn(e,t){let n=t.split(`.`);return()=>{let t=e;for(let e=0;ee.__isTeleport,Un=Symbol(`_leaveCb`),Wn=Symbol(`_enterCb`);function Gn(){let e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return yr(()=>{e.isMounted=!0}),Sr(()=>{e.isUnmounting=!0}),e}var Kn=[Function,Array],qn={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Kn,onEnter:Kn,onAfterEnter:Kn,onEnterCancelled:Kn,onBeforeLeave:Kn,onLeave:Kn,onAfterLeave:Kn,onLeaveCancelled:Kn,onBeforeAppear:Kn,onAppear:Kn,onAfterAppear:Kn,onAppearCancelled:Kn},Jn=e=>{let t=e.subTree;return t.component?Jn(t.component):t},Yn={name:`BaseTransition`,props:qn,setup(e,{slots:t}){let n=va(),r=Gn();return()=>{let i=t.default&&rr(t.default(),!0);if(!i||!i.length)return;let a=Xn(i),o=N(e),{mode:s}=o;if(r.isLeaving)return er(a);let c=tr(a);if(!c)return er(a);let l=$n(c,o,r,n,e=>l=e);c.type!==B&&nr(c,l);let u=n.subTree&&tr(n.subTree);if(u&&u.type!==B&&!na(u,c)&&Jn(n).type!==B){let e=$n(u,o,r,n);if(nr(u,e),s===`out-in`&&c.type!==B)return r.isLeaving=!0,e.afterLeave=()=>{r.isLeaving=!1,n.job.flags&8||n.update(),delete e.afterLeave,u=void 0},er(a);s===`in-out`&&c.type!==B?e.delayLeave=(e,t,n)=>{let i=Qn(r,u);i[String(u.key)]=u,e[Un]=()=>{t(),e[Un]=void 0,delete l.delayedLeave,u=void 0},l.delayedLeave=()=>{n(),delete l.delayedLeave,u=void 0}}:u=void 0}else u&&=void 0;return a}}};function Xn(e){let t=e[0];if(e.length>1){for(let n of e)if(n.type!==B){t=n;break}}return t}var Zn=Yn;function Qn(e,t){let{leavingVNodes:n}=e,r=n.get(t.type);return r||(r=Object.create(null),n.set(t.type,r)),r}function $n(e,t,n,r,i){let{appear:a,mode:o,persisted:s=!1,onBeforeEnter:c,onEnter:l,onAfterEnter:u,onEnterCancelled:d,onBeforeLeave:f,onLeave:m,onAfterLeave:h,onLeaveCancelled:g,onBeforeAppear:_,onAppear:v,onAfterAppear:y,onAppearCancelled:b}=t,x=String(e.key),S=Qn(n,e),C=(e,t)=>{e&&un(e,r,9,t)},w=(e,t)=>{let n=t[1];C(e,t),p(e)?e.every(e=>e.length<=1)&&n():e.length<=1&&n()},T={mode:o,persisted:s,beforeEnter(t){let r=c;if(!n.isMounted)if(a)r=_||c;else return;t[Un]&&t[Un](!0);let i=S[x];i&&na(e,i)&&i.el[Un]&&i.el[Un](),C(r,[t])},enter(t){if(S[x]===e)return;let r=l,i=u,o=d;if(!n.isMounted)if(a)r=v||l,i=y||u,o=b||d;else return;let s=!1;t[Wn]=e=>{s||(s=!0,C(e?o:i,[t]),T.delayedLeave&&T.delayedLeave(),t[Wn]=void 0)};let c=t[Wn].bind(null,!1);r?w(r,[t,c]):c()},leave(t,r){let i=String(e.key);if(t[Wn]&&t[Wn](!0),n.isUnmounting)return r();C(f,[t]);let a=!1;t[Un]=n=>{a||(a=!0,r(),C(n?g:h,[t]),t[Un]=void 0,S[i]===e&&delete S[i])};let o=t[Un].bind(null,!1);S[i]=e,m?w(m,[t,o]):o()},clone(e){let a=$n(e,t,n,r,i);return i&&i(a),a}};return T}function er(e){if(dr(e))return e=sa(e),e.children=null,e}function tr(e){if(!dr(e))return Hn(e.type)&&e.children?Xn(e.children):e;if(e.component)return e.component.subTree;let{shapeFlag:t,children:n}=e;if(n){if(t&16)return n[0];if(t&32&&_(n.default))return n.default()}}function nr(e,t){e.shapeFlag&6&&e.component?(e.transition=t,nr(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function rr(e,t=!1,n){let r=[],i=0;for(let a=0;a1)for(let e=0;ecr(e,t&&(p(t)?t[r]:t),n,i,a));return}if(ur(i)&&!a){i.shapeFlag&512&&i.type.__asyncResolved&&i.component.subTree.component&&cr(e,t,n,i.component.subTree);return}let s=i.shapeFlag&4?Na(i.component):i.el,c=a?null:s,{i:l,r:d}=e,m=t&&t.r,h=l.refs===r?l.refs={}:l.refs,g=l.setupState,y=N(g),b=g===r?o:e=>or(h,e)?!1:f(y,e),x=(e,t)=>!(t&&or(h,t));if(m!=null&&m!==d){if(lr(t),v(m))h[m]=null,b(m)&&(g[m]=null);else if(P(m)){let e=t;x(m,e.k)&&(m.value=null),e.k&&(h[e.k]=null)}}if(_(d))ln(d,l,12,[c,h]);else{let t=v(d),r=P(d);if(t||r){let i=()=>{if(e.f){let n=t?b(d)?g[d]:h[d]:x(d)||!e.k?d.value:h[e.k];if(a)p(n)&&u(n,s);else if(p(n))n.includes(s)||n.push(s);else if(t)h[d]=[s],b(d)&&(g[d]=h[d]);else{let t=[s];x(d,e.k)&&(d.value=t),e.k&&(h[e.k]=t)}}else t?(h[d]=c,b(d)&&(g[d]=c)):r&&(x(d,e.k)&&(d.value=c),e.k&&(h[e.k]=c))};if(c){let t=()=>{i(),sr.delete(e)};t.id=-1,sr.set(e,t),Ni(t,n)}else lr(e),i()}}}function lr(e){let t=sr.get(e);t&&(t.flags|=8,sr.delete(e))}de().requestIdleCallback,de().cancelIdleCallback;var ur=e=>!!e.type.__asyncLoader,dr=e=>e.type.__isKeepAlive;function fr(e,t){mr(e,`a`,t)}function pr(e,t){mr(e,`da`,t)}function mr(e,t,n=K){let r=e.__wdc||=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()};if(gr(t,r,n),n){let e=n.parent;for(;e&&e.parent;)dr(e.parent.vnode)&&hr(r,t,n,e),e=e.parent}}function hr(e,t,n,r){let i=gr(t,e,r,!0);Cr(()=>{u(r[t],i)},n)}function gr(e,t,n=K,r=!1){if(n){let i=n[e]||(n[e]=[]),a=t.__weh||=(...r)=>{Ke();let i=xa(n),a=un(t,n,e,r);return i(),qe(),a};return r?i.unshift(a):i.push(a),a}}var _r=e=>(t,n=K)=>{(!wa||e===`sp`)&&gr(e,(...e)=>t(...e),n)},vr=_r(`bm`),yr=_r(`m`),br=_r(`bu`),xr=_r(`u`),Sr=_r(`bum`),Cr=_r(`um`),wr=_r(`sp`),Tr=_r(`rtg`),Er=_r(`rtc`);function Dr(e,t=K){gr(`ec`,e,t)}var Or=`components`;function kr(e,t){return jr(Or,e,!0,t)||e}var Ar=Symbol.for(`v-ndc`);function jr(e,t,n=!0,r=!1){let i=On||K;if(i){let n=i.type;if(e===Or){let e=Pa(n,!1);if(e&&(e===t||e===O(t)||e===ie(O(t))))return n}let a=Mr(i[e]||n[e],t)||Mr(i.appContext[e],t);return!a&&r?n:a}}function Mr(e,t){return e&&(e[t]||e[O(t)]||e[ie(O(t))])}function Nr(e,t,n,r){let i,a=n&&n[r],o=p(e);if(o||v(e)){let n=o&&Ht(e),r=!1,s=!1;n&&(r=!Wt(e),s=Ut(e),e=at(e)),i=Array(e.length);for(let n=0,o=e.length;nt(e,n,void 0,a&&a[n]));else{let n=Object.keys(e);i=Array(n.length);for(let r=0,o=n.length;re?Ca(e)?Na(e):Pr(e.parent):null,Fr=l(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Pr(e.parent),$root:e=>Pr(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Wr(e),$forceUpdate:e=>e.f||=()=>{xn(e.update)},$nextTick:e=>e.n||=yn.bind(e.proxy),$watch:e=>zn.bind(e)}),Ir=(e,t)=>e!==r&&!e.__isScriptSetup&&f(e,t),Lr={get({_:e},t){if(t===`__v_skip`)return!0;let{ctx:n,setupState:i,data:a,props:o,accessCache:s,type:c,appContext:l}=e;if(t[0]!==`$`){let e=s[t];if(e!==void 0)switch(e){case 1:return i[t];case 2:return a[t];case 4:return n[t];case 3:return o[t]}else if(Ir(i,t))return s[t]=1,i[t];else if(a!==r&&f(a,t))return s[t]=2,a[t];else if(f(o,t))return s[t]=3,o[t];else if(n!==r&&f(n,t))return s[t]=4,n[t];else zr&&(s[t]=0)}let u=Fr[t],d,p;if(u)return t===`$attrs`&&M(e.attrs,`get`,``),u(e);if((d=c.__cssModules)&&(d=d[t]))return d;if(n!==r&&f(n,t))return s[t]=4,n[t];if(p=l.config.globalProperties,f(p,t))return p[t]},set({_:e},t,n){let{data:i,setupState:a,ctx:o}=e;return Ir(a,t)?(a[t]=n,!0):i!==r&&f(i,t)?(i[t]=n,!0):f(e.props,t)||t[0]===`$`&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:i,appContext:a,props:o,type:s}},c){let l;return!!(n[c]||e!==r&&c[0]!==`$`&&f(e,c)||Ir(t,c)||f(o,c)||f(i,c)||f(Fr,c)||f(a.config.globalProperties,c)||(l=s.__cssModules)&&l[c])},defineProperty(e,t,n){return n.get==null?f(n,`value`)&&this.set(e,t,n.value,null):e._.accessCache[t]=0,Reflect.defineProperty(e,t,n)}};function Rr(e){return p(e)?e.reduce((e,t)=>(e[t]=null,e),{}):e}var zr=!0;function Br(e){let t=Wr(e),n=e.proxy,r=e.ctx;zr=!1,t.beforeCreate&&Hr(t.beforeCreate,e,`bc`);let{data:i,computed:o,methods:s,watch:c,provide:l,inject:u,created:d,beforeMount:f,mounted:m,beforeUpdate:h,updated:g,activated:v,deactivated:y,beforeDestroy:x,beforeUnmount:S,destroyed:C,unmounted:w,render:T,renderTracked:E,renderTriggered:ee,errorCaptured:D,serverPrefetch:te,expose:O,inheritAttrs:ne,components:re,directives:ie,filters:ae}=t;if(u&&Vr(u,r,null),s)for(let e in s){let t=s[e];_(t)&&(r[e]=t.bind(n))}if(i){let t=i.call(n,n);b(t)&&(e.data=Rt(t))}if(zr=!0,o)for(let e in o){let t=o[e],i=Ia({get:_(t)?t.bind(n,n):_(t.get)?t.get.bind(n,n):a,set:!_(t)&&_(t.set)?t.set.bind(n):a});Object.defineProperty(r,e,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e})}if(c)for(let e in c)Ur(c[e],r,n,e);if(l){let e=_(l)?l.call(n):l;Reflect.ownKeys(e).forEach(t=>{Nn(t,e[t])})}d&&Hr(d,e,`c`);function k(e,t){p(t)?t.forEach(t=>e(t.bind(n))):t&&e(t.bind(n))}if(k(vr,f),k(yr,m),k(br,h),k(xr,g),k(fr,v),k(pr,y),k(Dr,D),k(Er,E),k(Tr,ee),k(Sr,S),k(Cr,w),k(wr,te),p(O))if(O.length){let t=e.exposed||={};O.forEach(e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t,enumerable:!0})})}else e.exposed||={};T&&e.render===a&&(e.render=T),ne!=null&&(e.inheritAttrs=ne),re&&(e.components=re),ie&&(e.directives=ie),te&&ar(e)}function Vr(e,t,n=a){p(e)&&(e=Yr(e));for(let n in e){let r=e[n],i;i=b(r)?`default`in r?Pn(r.from||n,r.default,!0):Pn(r.from||n):Pn(r),P(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e}):t[n]=i}}function Hr(e,t,n){un(p(e)?e.map(e=>e.bind(t.proxy)):e.bind(t.proxy),t,n)}function Ur(e,t,n,r){let i=r.includes(`.`)?Bn(n,r):()=>n[r];if(v(e)){let n=t[e];_(n)&&Ln(i,n)}else if(_(e))Ln(i,e.bind(n));else if(b(e))if(p(e))e.forEach(e=>Ur(e,t,n,r));else{let r=_(e.handler)?e.handler.bind(n):t[e.handler];_(r)&&Ln(i,r,e)}}function Wr(e){let t=e.type,{mixins:n,extends:r}=t,{mixins:i,optionsCache:a,config:{optionMergeStrategies:o}}=e.appContext,s=a.get(t),c;return s?c=s:!i.length&&!n&&!r?c=t:(c={},i.length&&i.forEach(e=>Gr(c,e,o,!0)),Gr(c,t,o)),b(t)&&a.set(t,c),c}function Gr(e,t,n,r=!1){let{mixins:i,extends:a}=t;a&&Gr(e,a,n,!0),i&&i.forEach(t=>Gr(e,t,n,!0));for(let i in t)if(!(r&&i===`expose`)){let r=Kr[i]||n&&n[i];e[i]=r?r(e[i],t[i]):t[i]}return e}var Kr={data:qr,props:Zr,emits:Zr,methods:Xr,computed:Xr,beforeCreate:R,created:R,beforeMount:R,mounted:R,beforeUpdate:R,updated:R,beforeDestroy:R,beforeUnmount:R,destroyed:R,unmounted:R,activated:R,deactivated:R,errorCaptured:R,serverPrefetch:R,components:Xr,directives:Xr,watch:Qr,provide:qr,inject:Jr};function qr(e,t){return t?e?function(){return l(_(e)?e.call(this,this):e,_(t)?t.call(this,this):t)}:t:e}function Jr(e,t){return Xr(Yr(e),Yr(t))}function Yr(e){if(p(e)){let t={};for(let n=0;nt===`modelValue`||t===`model-value`?e.modelModifiers:e[`${t}Modifiers`]||e[`${O(t)}Modifiers`]||e[`${re(t)}Modifiers`];function ii(e,t,...n){if(e.isUnmounted)return;let i=e.vnode.props||r,a=n,o=t.startsWith(`update:`),s=o&&ri(i,t.slice(7));s&&(s.trim&&(a=n.map(e=>v(e)?e.trim():e)),s.number&&(a=n.map(ce)));let c,l=i[c=ae(t)]||i[c=ae(O(t))];!l&&o&&(l=i[c=ae(re(t))]),l&&un(l,e,6,a);let u=i[c+`Once`];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[c])return;e.emitted[c]=!0,un(u,e,6,a)}}var ai=new WeakMap;function oi(e,t,n=!1){let r=n?ai:t.emitsCache,i=r.get(e);if(i!==void 0)return i;let a=e.emits,o={},s=!1;if(!_(e)){let r=e=>{let n=oi(e,t,!0);n&&(s=!0,l(o,n))};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}return!a&&!s?(b(e)&&r.set(e,null),null):(p(a)?a.forEach(e=>o[e]=null):l(o,a),b(e)&&r.set(e,o),o)}function si(e,t){return!e||!s(t)?!1:(t=t.slice(2).replace(/Once$/,``),f(e,t[0].toLowerCase()+t.slice(1))||f(e,re(t))||f(e,t))}function ci(e){let{type:t,vnode:n,proxy:r,withProxy:i,propsOptions:[a],slots:o,attrs:s,emit:l,render:u,renderCache:d,props:f,data:p,setupState:m,ctx:h,inheritAttrs:g}=e,_=An(e),v,y;try{if(n.shapeFlag&4){let e=i||r,t=e;v=ua(u.call(t,e,d,f,m,p,h)),y=s}else{let e=t;v=ua(e.length>1?e(f,{attrs:s,slots:o,emit:l}):e(f,null)),y=t.props?s:li(s)}}catch(t){Ji.length=0,dn(t,e,1),v=W(B)}let b=v;if(y&&g!==!1){let e=Object.keys(y),{shapeFlag:t}=b;e.length&&t&7&&(a&&e.some(c)&&(y=ui(y,a)),b=sa(b,y,!1,!0))}return n.dirs&&(b=sa(b,null,!1,!0),b.dirs=b.dirs?b.dirs.concat(n.dirs):n.dirs),n.transition&&nr(b,n.transition),v=b,An(_),v}var li=e=>{let t;for(let n in e)(n===`class`||n===`style`||s(n))&&((t||={})[n]=e[n]);return t},ui=(e,t)=>{let n={};for(let r in e)(!c(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function di(e,t,n){let{props:r,children:i,component:a}=e,{props:o,children:s,patchFlag:c}=t,l=a.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?fi(r,o,l):!!o;if(c&8){let e=t.dynamicProps;for(let t=0;tObject.create(hi),_i=e=>Object.getPrototypeOf(e)===hi;function vi(e,t,n,r=!1){let i={},a=gi();e.propsDefaults=Object.create(null),bi(e,t,i,a);for(let t in e.propsOptions[0])t in i||(i[t]=void 0);n?e.props=r?i:zt(i):e.type.props?e.props=i:e.props=a,e.attrs=a}function yi(e,t,n,r){let{props:i,attrs:a,vnode:{patchFlag:o}}=e,s=N(i),[c]=e.propsOptions,l=!1;if((r||o>0)&&!(o&16)){if(o&8){let n=e.vnode.dynamicProps;for(let r=0;r{d=!0;let[n,r]=Ci(e,t,!0);l(c,n),r&&u.push(...r)};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}if(!s&&!d)return b(e)&&a.set(e,i),i;if(p(s))for(let e=0;ee===`_`||e===`_ctx`||e===`$stable`,Ei=e=>p(e)?e.map(ua):[ua(e)],Di=(e,t,n)=>{if(t._n)return t;let r=jn((...e)=>Ei(t(...e)),n);return r._c=!1,r},Oi=(e,t,n)=>{let r=e._ctx;for(let n in e){if(Ti(n))continue;let i=e[n];if(_(i))t[n]=Di(n,i,r);else if(i!=null){let e=Ei(i);t[n]=()=>e}}},ki=(e,t)=>{let n=Ei(t);e.slots.default=()=>n},Ai=(e,t,n)=>{for(let r in t)(n||!Ti(r))&&(e[r]=t[r])},ji=(e,t,n)=>{let r=e.slots=gi();if(e.vnode.shapeFlag&32){let e=t._;e?(Ai(r,t,n),n&&se(r,`_`,e,!0)):Oi(t,r)}else t&&ki(e,t)},Mi=(e,t,n)=>{let{vnode:i,slots:a}=e,o=!0,s=r;if(i.shapeFlag&32){let e=t._;e?n&&e===1?o=!1:Ai(a,t,n):(o=!t.$stable,Oi(t,a)),s=t}else t&&(ki(e,t),s={default:1});if(o)for(let e in a)!Ti(e)&&s[e]==null&&delete a[e]},Ni=Gi;function Pi(e){return Fi(e)}function Fi(e,t){let n=de();n.__VUE__=!0;let{insert:o,remove:s,patchProp:c,createElement:l,createText:u,createComment:d,setText:f,setElementText:p,parentNode:m,nextSibling:h,setScopeId:g=a,insertStaticContent:_}=e,v=(e,t,n,r=null,i=null,a=null,o=void 0,s=null,c=!!t.dynamicChildren)=>{if(e===t)return;e&&!na(e,t)&&(r=ye(e),me(e,i,a,!0),e=null),t.patchFlag===-2&&(c=!1,t.dynamicChildren=null);let{type:l,ref:u,shapeFlag:d}=t;switch(l){case Ki:y(e,t,n,r);break;case B:b(e,t,n,r);break;case qi:e??x(t,n,r,o);break;case z:re(e,t,n,r,i,a,o,s,c);break;default:d&1?w(e,t,n,r,i,a,o,s,c):d&6?ie(e,t,n,r,i,a,o,s,c):(d&64||d&128)&&l.process(e,t,n,r,i,a,o,s,c,Se)}u!=null&&i?cr(u,e&&e.ref,a,t||e,!t):u==null&&e&&e.ref!=null&&cr(e.ref,null,a,e,!0)},y=(e,t,n,r)=>{if(e==null)o(t.el=u(t.children),n,r);else{let n=t.el=e.el;t.children!==e.children&&f(n,t.children)}},b=(e,t,n,r)=>{e==null?o(t.el=d(t.children||``),n,r):t.el=e.el},x=(e,t,n,r)=>{[e.el,e.anchor]=_(e.children,t,n,r,e.el,e.anchor)},S=({el:e,anchor:t},n,r)=>{let i;for(;e&&e!==t;)i=h(e),o(e,n,r),e=i;o(t,n,r)},C=({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=h(e),s(e),e=n;s(t)},w=(e,t,n,r,i,a,o,s,c)=>{if(t.type===`svg`?o=`svg`:t.type===`math`&&(o=`mathml`),e==null)T(t,n,r,i,a,o,s,c);else{let n=e.el&&e.el._isVueCE?e.el:null;try{n&&n._beginPatch(),te(e,t,i,a,o,s,c)}finally{n&&n._endPatch()}}},T=(e,t,n,r,i,a,s,u)=>{let d,f,{props:m,shapeFlag:h,transition:g,dirs:_}=e;if(d=e.el=l(e.type,a,m&&m.is,m),h&8?p(d,e.children):h&16&&D(e.children,d,null,r,i,Ii(e,a),s,u),_&&Mn(e,null,r,`created`),E(d,e,e.scopeId,s,r),m){for(let e in m)e!==`value`&&!ee(e)&&c(d,e,null,m[e],a,r);`value`in m&&c(d,`value`,null,m.value,a),(f=m.onVnodeBeforeMount)&&ma(f,r,e)}_&&Mn(e,null,r,`beforeMount`);let v=Ri(i,g);v&&g.beforeEnter(d),o(d,t,n),((f=m&&m.onVnodeMounted)||v||_)&&Ni(()=>{f&&ma(f,r,e),v&&g.enter(d),_&&Mn(e,null,r,`mounted`)},i)},E=(e,t,n,r,i)=>{if(n&&g(e,n),r)for(let t=0;t{for(let l=c;l{let l=t.el=e.el,{patchFlag:u,dynamicChildren:d,dirs:f}=t;u|=e.patchFlag&16;let m=e.props||r,h=t.props||r,g;if(n&&Li(n,!1),(g=h.onVnodeBeforeUpdate)&&ma(g,n,t,e),f&&Mn(t,e,n,`beforeUpdate`),n&&Li(n,!0),(m.innerHTML&&h.innerHTML==null||m.textContent&&h.textContent==null)&&p(l,``),d?O(e.dynamicChildren,d,l,n,i,Ii(t,a),o):s||le(e,t,l,null,n,i,Ii(t,a),o,!1),u>0){if(u&16)ne(l,m,h,n,a);else if(u&2&&m.class!==h.class&&c(l,`class`,null,h.class,a),u&4&&c(l,`style`,m.style,h.style,a),u&8){let e=t.dynamicProps;for(let t=0;t{g&&ma(g,n,t,e),f&&Mn(t,e,n,`updated`)},i)},O=(e,t,n,r,i,a,o)=>{for(let s=0;s{if(t!==n){if(t!==r)for(let r in t)!ee(r)&&!(r in n)&&c(e,r,t[r],null,a,i);for(let r in n){if(ee(r))continue;let o=n[r],s=t[r];o!==s&&r!==`value`&&c(e,r,s,o,a,i)}`value`in n&&c(e,`value`,t.value,n.value,a)}},re=(e,t,n,r,i,a,s,c,l)=>{let d=t.el=e?e.el:u(``),f=t.anchor=e?e.anchor:u(``),{patchFlag:p,dynamicChildren:m,slotScopeIds:h}=t;h&&(c=c?c.concat(h):h),e==null?(o(d,n,r),o(f,n,r),D(t.children||[],n,f,i,a,s,c,l)):p>0&&p&64&&m&&e.dynamicChildren&&e.dynamicChildren.length===m.length?(O(e.dynamicChildren,m,n,i,a,s,c),(t.key!=null||i&&t===i.subTree)&&zi(e,t,!0)):le(e,t,n,f,i,a,s,c,l)},ie=(e,t,n,r,i,a,o,s,c)=>{t.slotScopeIds=s,e==null?t.shapeFlag&512?i.ctx.activate(t,n,r,o,c):ae(t,n,r,i,a,o,c):k(e,t,c)},ae=(e,t,n,r,i,a,o)=>{let s=e.component=_a(e,r,i);if(dr(e)&&(s.ctx.renderer=Se),Ta(s,!1,o),s.asyncDep){if(i&&i.registerDep(s,se,o),!e.el){let r=s.subTree=W(B);b(null,r,t,n),e.placeholder=r.el}}else se(s,e,t,n,i,a,o)},k=(e,t,n)=>{let r=t.component=e.component;if(di(e,t,n))if(r.asyncDep&&!r.asyncResolved){ce(r,t,n);return}else r.next=t,r.update();else t.el=e.el,r.vnode=t},se=(e,t,n,r,i,a,o)=>{let s=()=>{if(e.isMounted){let{next:t,bu:n,u:r,parent:s,vnode:c}=e;{let n=Vi(e);if(n){t&&(t.el=c.el,ce(e,t,o)),n.asyncDep.then(()=>{Ni(()=>{e.isUnmounted||l()},i)});return}}let u=t,d;Li(e,!1),t?(t.el=c.el,ce(e,t,o)):t=c,n&&oe(n),(d=t.props&&t.props.onVnodeBeforeUpdate)&&ma(d,s,t,c),Li(e,!0);let f=ci(e),p=e.subTree;e.subTree=f,v(p,f,m(p.el),ye(p),e,i,a),t.el=f.el,u===null&&mi(e,f.el),r&&Ni(r,i),(d=t.props&&t.props.onVnodeUpdated)&&Ni(()=>ma(d,s,t,c),i)}else{let o,{el:s,props:c}=t,{bm:l,m:u,parent:d,root:f,type:p}=e,m=ur(t);if(Li(e,!1),l&&oe(l),!m&&(o=c&&c.onVnodeBeforeMount)&&ma(o,d,t),Li(e,!0),s&&we){let t=()=>{e.subTree=ci(e),we(s,e.subTree,e,i,null)};m&&p.__asyncHydrate?p.__asyncHydrate(s,e,t):t()}else{f.ce&&f.ce._hasShadowRoot()&&f.ce._injectChildStyle(p,e.parent?e.parent.type:void 0);let o=e.subTree=ci(e);v(null,o,n,r,e,i,a),t.el=o.el}if(u&&Ni(u,i),!m&&(o=c&&c.onVnodeMounted)){let e=t;Ni(()=>ma(o,d,e),i)}(t.shapeFlag&256||d&&ur(d.vnode)&&d.vnode.shapeFlag&256)&&e.a&&Ni(e.a,i),e.isMounted=!0,t=n=r=null}};e.scope.on();let c=e.effect=new je(s);e.scope.off();let l=e.update=c.run.bind(c),u=e.job=c.runIfDirty.bind(c);u.i=e,u.id=e.uid,c.scheduler=()=>xn(u),Li(e,!0),l()},ce=(e,t,n)=>{t.component=e;let r=e.vnode.props;e.vnode=t,e.next=null,yi(e,t.props,r,n),Mi(e,t.children,n),Ke(),wn(e),qe()},le=(e,t,n,r,i,a,o,s,c=!1)=>{let l=e&&e.children,u=e?e.shapeFlag:0,d=t.children,{patchFlag:f,shapeFlag:m}=t;if(f>0){if(f&128){fe(l,d,n,r,i,a,o,s,c);return}else if(f&256){ue(l,d,n,r,i,a,o,s,c);return}}m&8?(u&16&&ve(l,i,a),d!==l&&p(n,d)):u&16?m&16?fe(l,d,n,r,i,a,o,s,c):ve(l,i,a,!0):(u&8&&p(n,``),m&16&&D(d,n,r,i,a,o,s,c))},ue=(e,t,n,r,a,o,s,c,l)=>{e||=i,t||=i;let u=e.length,d=t.length,f=Math.min(u,d),p;for(p=0;pd?ve(e,a,o,!0,!1,f):D(t,n,r,a,o,s,c,l,f)},fe=(e,t,n,r,a,o,s,c,l)=>{let u=0,d=t.length,f=e.length-1,p=d-1;for(;u<=f&&u<=p;){let r=e[u],i=t[u]=l?da(t[u]):ua(t[u]);if(na(r,i))v(r,i,n,null,a,o,s,c,l);else break;u++}for(;u<=f&&u<=p;){let r=e[f],i=t[p]=l?da(t[p]):ua(t[p]);if(na(r,i))v(r,i,n,null,a,o,s,c,l);else break;f--,p--}if(u>f){if(u<=p){let e=p+1,i=ep)for(;u<=f;)me(e[u],a,o,!0),u++;else{let m=u,h=u,g=new Map;for(u=h;u<=p;u++){let e=t[u]=l?da(t[u]):ua(t[u]);e.key!=null&&g.set(e.key,u)}let _,y=0,b=p-h+1,x=!1,S=0,C=Array(b);for(u=0;u=b){me(r,a,o,!0);continue}let i;if(r.key!=null)i=g.get(r.key);else for(_=h;_<=p;_++)if(C[_-h]===0&&na(r,t[_])){i=_;break}i===void 0?me(r,a,o,!0):(C[i-h]=u+1,i>=S?S=i:x=!0,v(r,t[i],n,null,a,o,s,c,l),y++)}let w=x?Bi(C):i;for(_=w.length-1,u=b-1;u>=0;u--){let e=h+u,i=t[e],f=t[e+1],p=e+1{let{el:a,type:c,transition:l,children:u,shapeFlag:d}=e;if(d&6){pe(e.component.subTree,t,n,r);return}if(d&128){e.suspense.move(t,n,r);return}if(d&64){c.move(e,t,n,Se);return}if(c===z){o(a,t,n);for(let e=0;el.enter(a),i);else{let{leave:r,delayLeave:i,afterLeave:c}=l,u=()=>{e.ctx.isUnmounted?s(a):o(a,t,n)},d=()=>{a._isLeaving&&a[Un](!0),r(a,()=>{u(),c&&c()})};i?i(a,u,d):d()}else o(a,t,n)},me=(e,t,n,r=!1,i=!1)=>{let{type:a,props:o,ref:s,children:c,dynamicChildren:l,shapeFlag:u,patchFlag:d,dirs:f,cacheIndex:p}=e;if(d===-2&&(i=!1),s!=null&&(Ke(),cr(s,null,n,e,!0),qe()),p!=null&&(t.renderCache[p]=void 0),u&256){t.ctx.deactivate(e);return}let m=u&1&&f,h=!ur(e),g;if(h&&(g=o&&o.onVnodeBeforeUnmount)&&ma(g,t,e),u&6)_e(e.component,n,r);else{if(u&128){e.suspense.unmount(n,r);return}m&&Mn(e,null,t,`beforeUnmount`),u&64?e.type.remove(e,t,n,Se,r):l&&!l.hasOnce&&(a!==z||d>0&&d&64)?ve(l,t,n,!1,!0):(a===z&&d&384||!i&&u&16)&&ve(c,t,n),r&&he(e)}(h&&(g=o&&o.onVnodeUnmounted)||m)&&Ni(()=>{g&&ma(g,t,e),m&&Mn(e,null,t,`unmounted`)},n)},he=e=>{let{type:t,el:n,anchor:r,transition:i}=e;if(t===z){ge(n,r);return}if(t===qi){C(e);return}let a=()=>{s(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(e.shapeFlag&1&&i&&!i.persisted){let{leave:t,delayLeave:r}=i,o=()=>t(n,a);r?r(e.el,a,o):o()}else a()},ge=(e,t)=>{let n;for(;e!==t;)n=h(e),s(e),e=n;s(t)},_e=(e,t,n)=>{let{bum:r,scope:i,job:a,subTree:o,um:s,m:c,a:l}=e;Hi(c),Hi(l),r&&oe(r),i.stop(),a&&(a.flags|=8,me(o,e,t,n)),s&&Ni(s,t),Ni(()=>{e.isUnmounted=!0},t)},ve=(e,t,n,r=!1,i=!1,a=0)=>{for(let o=a;o{if(e.shapeFlag&6)return ye(e.component.subTree);if(e.shapeFlag&128)return e.suspense.next();let t=h(e.anchor||e.el),n=t&&t[Vn];return n?h(n):t},be=!1,xe=(e,t,n)=>{let r;e==null?t._vnode&&(me(t._vnode,null,null,!0),r=t._vnode.component):v(t._vnode||null,e,t,null,null,null,n),t._vnode=e,be||=(be=!0,wn(r),Tn(),!1)},Se={p:v,um:me,m:pe,r:he,mt:ae,mc:D,pc:le,pbc:O,n:ye,o:e},Ce,we;return t&&([Ce,we]=t(Se)),{render:xe,hydrate:Ce,createApp:ti(xe,Ce)}}function Ii({type:e,props:t},n){return n===`svg`&&e===`foreignObject`||n===`mathml`&&e===`annotation-xml`&&t&&t.encoding&&t.encoding.includes(`html`)?void 0:n}function Li({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Ri(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function zi(e,t,n=!1){let r=e.children,i=t.children;if(p(r)&&p(i))for(let e=0;e>1,e[n[s]]0&&(t[r]=n[a-1]),n[a]=r)}}for(a=n.length,o=n[a-1];a-- >0;)n[a]=o,o=t[o];return n}function Vi(e){let t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Vi(t)}function Hi(e){if(e)for(let t=0;te.__isSuspense;function Gi(e,t){t&&t.pendingBranch?p(e)?t.effects.push(...e):t.effects.push(e):Cn(e)}var z=Symbol.for(`v-fgt`),Ki=Symbol.for(`v-txt`),B=Symbol.for(`v-cmt`),qi=Symbol.for(`v-stc`),Ji=[],Yi=null;function V(e=!1){Ji.push(Yi=e?null:[])}function Xi(){Ji.pop(),Yi=Ji[Ji.length-1]||null}var Zi=1;function Qi(e,t=!1){Zi+=e,e<0&&Yi&&t&&(Yi.hasOnce=!0)}function $i(e){return e.dynamicChildren=Zi>0?Yi||i:null,Xi(),Zi>0&&Yi&&Yi.push(e),e}function H(e,t,n,r,i,a){return $i(U(e,t,n,r,i,a,!0))}function ea(e,t,n,r,i){return $i(W(e,t,n,r,i,!0))}function ta(e){return e?e.__v_isVNode===!0:!1}function na(e,t){return e.type===t.type&&e.key===t.key}var ra=({key:e})=>e??null,ia=({ref:e,ref_key:t,ref_for:n})=>(typeof e==`number`&&(e=``+e),e==null?null:v(e)||P(e)||_(e)?{i:On,r:e,k:t,f:!!n}:e);function U(e,t=null,n=null,r=0,i=null,a=e===z?0:1,o=!1,s=!1){let c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ra(t),ref:t&&ia(t),scopeId:kn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:a,patchFlag:r,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:On};return s?(fa(c,n),a&128&&e.normalize(c)):n&&(c.shapeFlag|=v(n)?8:16),Zi>0&&!o&&Yi&&(c.patchFlag>0||a&6)&&c.patchFlag!==32&&Yi.push(c),c}var W=aa;function aa(e,t=null,n=null,r=0,i=null,a=!1){if((!e||e===Ar)&&(e=B),ta(e)){let r=sa(e,t,!0);return n&&fa(r,n),Zi>0&&!a&&Yi&&(r.shapeFlag&6?Yi[Yi.indexOf(e)]=r:Yi.push(r)),r.patchFlag=-2,r}if(Fa(e)&&(e=e.__vccOpts),t){t=oa(t);let{class:e,style:n}=t;e&&!v(e)&&(t.class=_e(e)),b(n)&&(Gt(n)&&!p(n)&&(n=l({},n)),t.style=fe(n))}let o=v(e)?1:Wi(e)?128:Hn(e)?64:b(e)?4:_(e)?2:0;return U(e,t,n,r,i,o,a,!0)}function oa(e){return e?Gt(e)||_i(e)?l({},e):e:null}function sa(e,t,n=!1,r=!1){let{props:i,ref:a,patchFlag:o,children:s,transition:c}=e,l=t?pa(i||{},t):i,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&ra(l),ref:t&&t.ref?n&&a?p(a)?a.concat(ia(t)):[a,ia(t)]:ia(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:s,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==z?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:c,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&sa(e.ssContent),ssFallback:e.ssFallback&&sa(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return c&&r&&nr(u,c.clone(u)),u}function ca(e=` `,t=0){return W(Ki,null,e,t)}function la(e,t){let n=W(qi,null,e);return n.staticCount=t,n}function G(e=``,t=!1){return t?(V(),ea(B,null,e)):W(B,null,e)}function ua(e){return e==null||typeof e==`boolean`?W(B):p(e)?W(z,null,e.slice()):ta(e)?da(e):W(Ki,null,String(e))}function da(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:sa(e)}function fa(e,t){let n=0,{shapeFlag:r}=e;if(t==null)t=null;else if(p(t))n=16;else if(typeof t==`object`)if(r&65){let n=t.default;n&&(n._c&&(n._d=!1),fa(e,n()),n._c&&(n._d=!0));return}else{n=32;let r=t._;!r&&!_i(t)?t._ctx=On:r===3&&On&&(On.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else _(t)?(t={default:t,_ctx:On},n=32):(t=String(t),r&64?(n=16,t=[ca(t)]):n=8);e.children=t,e.shapeFlag|=n}function pa(...e){let t={};for(let n=0;nK||On,ya,ba;{let e=de(),t=(t,n)=>{let r;return(r=e[t])||(r=e[t]=[]),r.push(n),e=>{r.length>1?r.forEach(t=>t(e)):r[0](e)}};ya=t(`__VUE_INSTANCE_SETTERS__`,e=>K=e),ba=t(`__VUE_SSR_SETTERS__`,e=>wa=e)}var xa=e=>{let t=K;return ya(e),e.scope.on(),()=>{e.scope.off(),ya(t)}},Sa=()=>{K&&K.scope.off(),ya(null)};function Ca(e){return e.vnode.shapeFlag&4}var wa=!1;function Ta(e,t=!1,n=!1){t&&ba(t);let{props:r,children:i}=e.vnode,a=Ca(e);vi(e,r,a,t),ji(e,i,n||t);let o=a?Ea(e,t):void 0;return t&&ba(!1),o}function Ea(e,t){let n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,Lr);let{setup:r}=n;if(r){Ke();let n=e.setupContext=r.length>1?Ma(e):null,i=xa(e),a=ln(r,e,0,[e.props,n]),o=x(a);if(qe(),i(),(o||e.sp)&&!ur(e)&&ar(e),o){if(a.then(Sa,Sa),t)return a.then(n=>{Da(e,n,t)}).catch(t=>{dn(t,e,0)});e.asyncDep=a}else Da(e,a,t)}else Aa(e,t)}function Da(e,t,n){_(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:b(t)&&(e.setupState=$t(t)),Aa(e,n)}var Oa,ka;function Aa(e,t,n){let r=e.type;if(!e.render){if(!t&&Oa&&!r.render){let t=r.template||Wr(e).template;if(t){let{isCustomElement:n,compilerOptions:i}=e.appContext.config,{delimiters:a,compilerOptions:o}=r;r.render=Oa(t,l(l({isCustomElement:n,delimiters:a},i),o))}}e.render=r.render||a,ka&&ka(e)}{let t=xa(e);Ke();try{Br(e)}finally{qe(),t()}}}var ja={get(e,t){return M(e,`get`,``),e[t]}};function Ma(e){return{attrs:new Proxy(e.attrs,ja),slots:e.slots,emit:e.emit,expose:t=>{e.exposed=t||{}}}}function Na(e){return e.exposed?e.exposeProxy||=new Proxy($t(Kt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Fr)return Fr[n](e)},has(e,t){return t in e||t in Fr}}):e.proxy}function Pa(e,t=!0){return _(e)?e.displayName||e.name:e.name||t&&e.__name}function Fa(e){return _(e)&&`__vccOpts`in e}var Ia=(e,t)=>tn(e,t,wa);function La(e,t,n){try{Qi(-1);let r=arguments.length;return r===2?b(t)&&!p(t)?ta(t)?W(e,null,[t]):W(e,t):W(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&ta(n)&&(n=[n]),W(e,t,n))}finally{Qi(1)}}var Ra=`3.5.30`,za=void 0,Ba=typeof window<`u`&&window.trustedTypes;if(Ba)try{za=Ba.createPolicy(`vue`,{createHTML:e=>e})}catch{}var Va=za?e=>za.createHTML(e):e=>e,Ha=`http://www.w3.org/2000/svg`,Ua=`http://www.w3.org/1998/Math/MathML`,Wa=typeof document<`u`?document:null,Ga=Wa&&Wa.createElement(`template`),Ka={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{let t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{let i=t===`svg`?Wa.createElementNS(Ha,e):t===`mathml`?Wa.createElementNS(Ua,e):n?Wa.createElement(e,{is:n}):Wa.createElement(e);return e===`select`&&r&&r.multiple!=null&&i.setAttribute(`multiple`,r.multiple),i},createText:e=>Wa.createTextNode(e),createComment:e=>Wa.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Wa.querySelector(e),setScopeId(e,t){e.setAttribute(t,``)},insertStaticContent(e,t,n,r,i,a){let o=n?n.previousSibling:t.lastChild;if(i&&(i===a||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{Ga.innerHTML=Va(r===`svg`?`${e}`:r===`mathml`?`${e}`:e);let i=Ga.content;if(r===`svg`||r===`mathml`){let e=i.firstChild;for(;e.firstChild;)i.appendChild(e.firstChild);i.removeChild(e)}t.insertBefore(i,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},qa=`transition`,Ja=`animation`,Ya=Symbol(`_vtc`),Xa={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},Za=l({},qn,Xa),Qa=(e=>(e.displayName=`Transition`,e.props=Za,e))((e,{slots:t})=>La(Zn,to(e),t)),$a=(e,t=[])=>{p(e)?e.forEach(e=>e(...t)):e&&e(...t)},eo=e=>e?p(e)?e.some(e=>e.length>1):e.length>1:!1;function to(e){let t={};for(let n in e)n in Xa||(t[n]=e[n]);if(e.css===!1)return t;let{name:n=`v`,type:r,duration:i,enterFromClass:a=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:u=o,appearToClass:d=s,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=e,h=no(i),g=h&&h[0],_=h&&h[1],{onBeforeEnter:v,onEnter:y,onEnterCancelled:b,onLeave:x,onLeaveCancelled:S,onBeforeAppear:C=v,onAppear:w=y,onAppearCancelled:T=b}=t,E=(e,t,n,r)=>{e._enterCancelled=r,ao(e,t?d:s),ao(e,t?u:o),n&&n()},ee=(e,t)=>{e._isLeaving=!1,ao(e,f),ao(e,m),ao(e,p),t&&t()},D=e=>(t,n)=>{let i=e?w:y,o=()=>E(t,e,n);$a(i,[t,o]),oo(()=>{ao(t,e?c:a),io(t,e?d:s),eo(i)||co(t,r,g,o)})};return l(t,{onBeforeEnter(e){$a(v,[e]),io(e,a),io(e,o)},onBeforeAppear(e){$a(C,[e]),io(e,c),io(e,u)},onEnter:D(!1),onAppear:D(!0),onLeave(e,t){e._isLeaving=!0;let n=()=>ee(e,t);io(e,f),e._enterCancelled?(io(e,p),po(e)):(po(e),io(e,p)),oo(()=>{e._isLeaving&&(ao(e,f),io(e,m),eo(x)||co(e,r,_,n))}),$a(x,[e,n])},onEnterCancelled(e){E(e,!1,void 0,!0),$a(b,[e])},onAppearCancelled(e){E(e,!0,void 0,!0),$a(T,[e])},onLeaveCancelled(e){ee(e),$a(S,[e])}})}function no(e){if(e==null)return null;if(b(e))return[ro(e.enter),ro(e.leave)];{let t=ro(e);return[t,t]}}function ro(e){return le(e)}function io(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.add(t)),(e[Ya]||(e[Ya]=new Set)).add(t)}function ao(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.remove(t));let n=e[Ya];n&&(n.delete(t),n.size||(e[Ya]=void 0))}function oo(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}var so=0;function co(e,t,n,r){let i=e._endId=++so,a=()=>{i===e._endId&&r()};if(n!=null)return setTimeout(a,n);let{type:o,timeout:s,propCount:c}=lo(e,t);if(!o)return r();let l=o+`end`,u=0,d=()=>{e.removeEventListener(l,f),a()},f=t=>{t.target===e&&++u>=c&&d()};setTimeout(()=>{u(n[e]||``).split(`, `),i=r(`${qa}Delay`),a=r(`${qa}Duration`),o=uo(i,a),s=r(`${Ja}Delay`),c=r(`${Ja}Duration`),l=uo(s,c),u=null,d=0,f=0;t===qa?o>0&&(u=qa,d=o,f=a.length):t===Ja?l>0&&(u=Ja,d=l,f=c.length):(d=Math.max(o,l),u=d>0?o>l?qa:Ja:null,f=u?u===qa?a.length:c.length:0);let p=u===qa&&/\b(?:transform|all)(?:,|$)/.test(r(`${qa}Property`).toString());return{type:u,timeout:d,propCount:f,hasTransform:p}}function uo(e,t){for(;e.lengthfo(t)+fo(e[n])))}function fo(e){return e===`auto`?0:Number(e.slice(0,-1).replace(`,`,`.`))*1e3}function po(e){return(e?e.ownerDocument:document).body.offsetHeight}function mo(e,t,n){let r=e[Ya];r&&(t=(t?[t,...r]:[...r]).join(` `)),t==null?e.removeAttribute(`class`):n?e.setAttribute(`class`,t):e.className=t}var ho=Symbol(`_vod`),go=Symbol(`_vsh`),_o=Symbol(``),vo=/(?:^|;)\s*display\s*:/;function yo(e,t,n){let r=e.style,i=v(n),a=!1;if(n&&!i){if(t)if(v(t))for(let e of t.split(`;`)){let t=e.slice(0,e.indexOf(`:`)).trim();n[t]??xo(r,t,``)}else for(let e in t)n[e]??xo(r,e,``);for(let e in n)e===`display`&&(a=!0),xo(r,e,n[e])}else if(i){if(t!==n){let e=r[_o];e&&(n+=`;`+e),r.cssText=n,a=vo.test(n)}}else t&&e.removeAttribute(`style`);ho in e&&(e[ho]=a?r.display:``,e[go]&&(r.display=`none`))}var bo=/\s*!important$/;function xo(e,t,n){if(p(n))n.forEach(n=>xo(e,t,n));else if(n??=``,t.startsWith(`--`))e.setProperty(t,n);else{let r=wo(e,t);bo.test(n)?e.setProperty(re(r),n.replace(bo,``),`important`):e[r]=n}}var So=[`Webkit`,`Moz`,`ms`],Co={};function wo(e,t){let n=Co[t];if(n)return n;let r=O(t);if(r!==`filter`&&r in e)return Co[t]=r;r=ie(r);for(let n=0;nPo||=(Fo.then(()=>Po=0),Date.now());function Lo(e,t){let n=e=>{if(!e._vts)e._vts=Date.now();else if(e._vts<=n.attached)return;un(Ro(e,n.value),t,5,[e])};return n.value=e,n.attached=Io(),n}function Ro(e,t){if(p(t)){let n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(e=>t=>!t._stopped&&e&&e(t))}else return t}var zo=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Bo=(e,t,n,r,i,a)=>{let o=i===`svg`;t===`class`?mo(e,r,o):t===`style`?yo(e,n,r):s(t)?c(t)||jo(e,t,n,r,a):(t[0]===`.`?(t=t.slice(1),!0):t[0]===`^`?(t=t.slice(1),!1):Vo(e,t,r,o))?(Do(e,t,r),!e.tagName.includes(`-`)&&(t===`value`||t===`checked`||t===`selected`)&&Eo(e,t,r,o,a,t!==`value`)):e._isVueCE&&(Ho(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!v(r)))?Do(e,O(t),r,a,t):(t===`true-value`?e._trueValue=r:t===`false-value`&&(e._falseValue=r),Eo(e,t,r,o))};function Vo(e,t,n,r){if(r)return!!(t===`innerHTML`||t===`textContent`||t in e&&zo(t)&&_(n));if(t===`spellcheck`||t===`draggable`||t===`translate`||t===`autocorrect`||t===`sandbox`&&e.tagName===`IFRAME`||t===`form`||t===`list`&&e.tagName===`INPUT`||t===`type`&&e.tagName===`TEXTAREA`)return!1;if(t===`width`||t===`height`){let t=e.tagName;if(t===`IMG`||t===`VIDEO`||t===`CANVAS`||t===`SOURCE`)return!1}return zo(t)&&v(n)?!1:t in e}function Ho(e,t){let n=e._def.props;if(!n)return!1;let r=O(t);return Array.isArray(n)?n.some(e=>O(e)===r):Object.keys(n).some(e=>O(e)===r)}var Uo=e=>{let t=e.props[`onUpdate:modelValue`]||!1;return p(t)?e=>oe(t,e):t};function Wo(e){e.target.composing=!0}function Go(e){let t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event(`input`)))}var Ko=Symbol(`_assign`);function qo(e,t,n){return t&&(e=e.trim()),n&&(e=ce(e)),e}var Jo={created(e,{modifiers:{lazy:t,trim:n,number:r}},i){e[Ko]=Uo(i);let a=r||i.props&&i.props.type===`number`;Oo(e,t?`change`:`input`,t=>{t.target.composing||e[Ko](qo(e.value,n,a))}),(n||a)&&Oo(e,`change`,()=>{e.value=qo(e.value,n,a)}),t||(Oo(e,`compositionstart`,Wo),Oo(e,`compositionend`,Go),Oo(e,`change`,Go))},mounted(e,{value:t}){e.value=t??``},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:i,number:a}},o){if(e[Ko]=Uo(o),e.composing)return;let s=(a||e.type===`number`)&&!/^0\d/.test(e.value)?ce(e.value):e.value,c=t??``;s!==c&&(document.activeElement===e&&e.type!==`range`&&(r&&t===n||i&&e.value.trim()===c)||(e.value=c))}},Yo={deep:!0,created(e,t,n){e[Ko]=Uo(n),Oo(e,`change`,()=>{let t=e._modelValue,n=$o(e),r=e.checked,i=e[Ko];if(p(t)){let e=Ce(t,n),a=e!==-1;if(r&&!a)i(t.concat(n));else if(!r&&a){let n=[...t];n.splice(e,1),i(n)}}else if(h(t)){let e=new Set(t);r?e.add(n):e.delete(n),i(e)}else i(es(e,r))})},mounted:Xo,beforeUpdate(e,t,n){e[Ko]=Uo(n),Xo(e,t,n)}};function Xo(e,{value:t,oldValue:n},r){e._modelValue=t;let i;if(p(t))i=Ce(t,r.props.value)>-1;else if(h(t))i=t.has(r.props.value);else{if(t===n)return;i=Se(t,es(e,!0))}e.checked!==i&&(e.checked=i)}var Zo={deep:!0,created(e,{value:t,modifiers:{number:n}},r){let i=h(t);Oo(e,`change`,()=>{let t=Array.prototype.filter.call(e.options,e=>e.selected).map(e=>n?ce($o(e)):$o(e));e[Ko](e.multiple?i?new Set(t):t:t[0]),e._assigning=!0,yn(()=>{e._assigning=!1})}),e[Ko]=Uo(r)},mounted(e,{value:t}){Qo(e,t)},beforeUpdate(e,t,n){e[Ko]=Uo(n)},updated(e,{value:t}){e._assigning||Qo(e,t)}};function Qo(e,t){let n=e.multiple,r=p(t);if(!(n&&!r&&!h(t))){for(let i=0,a=e.options.length;iString(e)===String(o)):a.selected=Ce(t,o)>-1}else a.selected=t.has(o);else if(Se($o(a),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!n&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function $o(e){return`_value`in e?e._value:e.value}function es(e,t){let n=t?`_trueValue`:`_falseValue`;return n in e?e[n]:t}var ts=[`ctrl`,`shift`,`alt`,`meta`],ns={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>`button`in e&&e.button!==0,middle:e=>`button`in e&&e.button!==1,right:e=>`button`in e&&e.button!==2,exact:(e,t)=>ts.some(n=>e[`${n}Key`]&&!t.includes(n))},rs=(e,t)=>{if(!e)return e;let n=e._withMods||={},r=t.join(`.`);return n[r]||(n[r]=((n,...r)=>{for(let e=0;e{let n=e._withKeys||={},r=t.join(`.`);return n[r]||(n[r]=(n=>{if(!(`key`in n))return;let r=re(n.key);if(t.some(e=>e===r||is[e]===r))return e(n)}))},os=l({patchProp:Bo},Ka),ss;function cs(){return ss||=Pi(os)}var ls=((...e)=>{let t=cs().createApp(...e),{mount:n}=t;return t.mount=e=>{let r=ds(e);if(!r)return;let i=t._component;!_(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent=``);let a=n(r,!1,us(r));return r instanceof Element&&(r.removeAttribute(`v-cloak`),r.setAttribute(`data-v-app`,``)),a},t});function us(e){if(e instanceof SVGElement)return`svg`;if(typeof MathMLElement==`function`&&e instanceof MathMLElement)return`mathml`}function ds(e){return v(e)?document.querySelector(e):e}function fs(e,t){return function(){return e.apply(t,arguments)}}var{toString:ps}=Object.prototype,{getPrototypeOf:ms}=Object,{iterator:hs,toStringTag:gs}=Symbol,_s=(e=>t=>{let n=ps.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),vs=e=>(e=e.toLowerCase(),t=>_s(t)===e),ys=e=>t=>typeof t===e,{isArray:bs}=Array,xs=ys(`undefined`);function Ss(e){return e!==null&&!xs(e)&&e.constructor!==null&&!xs(e.constructor)&&Es(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var Cs=vs(`ArrayBuffer`);function ws(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&Cs(e.buffer),t}var Ts=ys(`string`),Es=ys(`function`),Ds=ys(`number`),Os=e=>typeof e==`object`&&!!e,ks=e=>e===!0||e===!1,As=e=>{if(_s(e)!==`object`)return!1;let t=ms(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(gs in e)&&!(hs in e)},js=e=>{if(!Os(e)||Ss(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Ms=vs(`Date`),Ns=vs(`File`),Ps=e=>!!(e&&e.uri!==void 0),Fs=e=>e&&e.getParts!==void 0,Is=vs(`Blob`),Ls=vs(`FileList`),Rs=e=>Os(e)&&Es(e.pipe);function zs(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var Bs=zs(),Vs=Bs.FormData===void 0?void 0:Bs.FormData,Hs=e=>{let t;return e&&(Vs&&e instanceof Vs||Es(e.append)&&((t=_s(e))===`formdata`||t===`object`&&Es(e.toString)&&e.toString()===`[object FormData]`))},Us=vs(`URLSearchParams`),[Ws,Gs,Ks,qs]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(vs),Js=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function Ys(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),bs(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var Zs=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,Qs=e=>!xs(e)&&e!==Zs;function $s(){let{caseless:e,skipUndefined:t}=Qs(this)&&this||{},n={},r=(r,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=e&&Xs(n,i)||i;As(n[a])&&As(r)?n[a]=$s(n[a],r):As(r)?n[a]=$s({},r):bs(r)?n[a]=r.slice():(!t||!xs(r))&&(n[a]=r)};for(let e=0,t=arguments.length;e(Ys(t,(t,r)=>{n&&Es(t)?Object.defineProperty(e,r,{value:fs(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),tc=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),nc=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,`constructor`,{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,`super`,{value:t.prototype}),n&&Object.assign(e.prototype,n)},rc=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-- >0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&ms(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},ic=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},ac=e=>{if(!e)return null;if(bs(e))return e;let t=e.length;if(!Ds(t))return null;let n=Array(t);for(;t-- >0;)n[t]=e[t];return n},oc=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&ms(Uint8Array)),sc=(e,t)=>{let n=(e&&e[hs]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},cc=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},lc=vs(`HTMLFormElement`),uc=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),dc=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),fc=vs(`RegExp`),pc=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};Ys(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},mc=e=>{pc(e,(t,n)=>{if(Es(e)&&[`arguments`,`caller`,`callee`].indexOf(n)!==-1)return!1;let r=e[n];if(Es(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},hc=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return bs(e)?r(e):r(String(e).split(t)),n},gc=()=>{},_c=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function vc(e){return!!(e&&Es(e.append)&&e[gs]===`FormData`&&e[hs])}var yc=e=>{let t=Array(10),n=(e,r)=>{if(Os(e)){if(t.indexOf(e)>=0)return;if(Ss(e))return e;if(!(`toJSON`in e)){t[r]=e;let i=bs(e)?[]:{};return Ys(e,(e,t)=>{let a=n(e,r+1);!xs(a)&&(i[t]=a)}),t[r]=void 0,i}}return e};return n(e,0)},bc=vs(`AsyncFunction`),xc=e=>e&&(Os(e)||Es(e))&&Es(e.then)&&Es(e.catch),Sc=((e,t)=>e?setImmediate:t?((e,t)=>(Zs.addEventListener(`message`,({source:n,data:r})=>{n===Zs&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),Zs.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,Es(Zs.postMessage)),q={isArray:bs,isArrayBuffer:Cs,isBuffer:Ss,isFormData:Hs,isArrayBufferView:ws,isString:Ts,isNumber:Ds,isBoolean:ks,isObject:Os,isPlainObject:As,isEmptyObject:js,isReadableStream:Ws,isRequest:Gs,isResponse:Ks,isHeaders:qs,isUndefined:xs,isDate:Ms,isFile:Ns,isReactNativeBlob:Ps,isReactNative:Fs,isBlob:Is,isRegExp:fc,isFunction:Es,isStream:Rs,isURLSearchParams:Us,isTypedArray:oc,isFileList:Ls,forEach:Ys,merge:$s,extend:ec,trim:Js,stripBOM:tc,inherits:nc,toFlatObject:rc,kindOf:_s,kindOfTest:vs,endsWith:ic,toArray:ac,forEachEntry:sc,matchAll:cc,isHTMLForm:lc,hasOwnProperty:dc,hasOwnProp:dc,reduceDescriptors:pc,freezeMethods:mc,toObjectSet:hc,toCamelCase:uc,noop:gc,toFiniteNumber:_c,findKey:Xs,global:Zs,isContextDefined:Qs,isSpecCompliantForm:vc,toJSONObject:yc,isAsyncFn:bc,isThenable:xc,setImmediate:Sc,asap:typeof queueMicrotask<`u`?queueMicrotask.bind(Zs):typeof process<`u`&&process.nextTick||Sc,isIterable:e=>e!=null&&Es(e[hs])},J=class e extends Error{static from(t,n,r,i,a,o){let s=new e(t.message,n||t.code,r,i,a);return s.cause=t,s.name=t.name,t.status!=null&&s.status==null&&(s.status=t.status),o&&Object.assign(s,o),s}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,`message`,{value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:q.toJSONObject(this.config),code:this.code,status:this.status}}};J.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,J.ERR_BAD_OPTION=`ERR_BAD_OPTION`,J.ECONNABORTED=`ECONNABORTED`,J.ETIMEDOUT=`ETIMEDOUT`,J.ERR_NETWORK=`ERR_NETWORK`,J.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,J.ERR_DEPRECATED=`ERR_DEPRECATED`,J.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,J.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,J.ERR_CANCELED=`ERR_CANCELED`,J.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,J.ERR_INVALID_URL=`ERR_INVALID_URL`;function Cc(e){return q.isPlainObject(e)||q.isArray(e)}function wc(e){return q.endsWith(e,`[]`)?e.slice(0,-2):e}function Tc(e,t,n){return e?e.concat(t).map(function(e,t){return e=wc(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function Ec(e){return q.isArray(e)&&!e.some(Cc)}var Dc=q.toFlatObject(q,{},null,function(e){return/^is[A-Z]/.test(e)});function Oc(e,t,n){if(!q.isObject(e))throw TypeError(`target must be an object`);t||=new FormData,n=q.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(e,t){return!q.isUndefined(t[e])});let r=n.metaTokens,i=n.visitor||l,a=n.dots,o=n.indexes,s=(n.Blob||typeof Blob<`u`&&Blob)&&q.isSpecCompliantForm(t);if(!q.isFunction(i))throw TypeError(`visitor must be a function`);function c(e){if(e===null)return``;if(q.isDate(e))return e.toISOString();if(q.isBoolean(e))return e.toString();if(!s&&q.isBlob(e))throw new J(`Blob is not supported. Use a Buffer instead.`);return q.isArrayBuffer(e)||q.isTypedArray(e)?s&&typeof Blob==`function`?new Blob([e]):Buffer.from(e):e}function l(e,n,i){let s=e;if(q.isReactNative(t)&&q.isReactNativeBlob(e))return t.append(Tc(i,n,a),c(e)),!1;if(e&&!i&&typeof e==`object`){if(q.endsWith(n,`{}`))n=r?n:n.slice(0,-2),e=JSON.stringify(e);else if(q.isArray(e)&&Ec(e)||(q.isFileList(e)||q.endsWith(n,`[]`))&&(s=q.toArray(e)))return n=wc(n),s.forEach(function(e,r){!(q.isUndefined(e)||e===null)&&t.append(o===!0?Tc([n],r,a):o===null?n:n+`[]`,c(e))}),!1}return Cc(e)?!0:(t.append(Tc(i,n,a),c(e)),!1)}let u=[],d=Object.assign(Dc,{defaultVisitor:l,convertValue:c,isVisitable:Cc});function f(e,n){if(!q.isUndefined(e)){if(u.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));u.push(e),q.forEach(e,function(e,r){(!(q.isUndefined(e)||e===null)&&i.call(t,e,q.isString(r)?r.trim():r,n,d))===!0&&f(e,n?n.concat(r):[r])}),u.pop()}}if(!q.isObject(e))throw TypeError(`data must be an object`);return f(e),t}function kc(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`,"%00":`\0`};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(e){return t[e]})}function Ac(e,t){this._pairs=[],e&&Oc(e,this,t)}var jc=Ac.prototype;jc.append=function(e,t){this._pairs.push([e,t])},jc.toString=function(e){let t=e?function(t){return e.call(this,t,kc)}:kc;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Mc(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function Nc(e,t,n){if(!t)return e;let r=n&&n.encode||Mc,i=q.isFunction(n)?{serialize:n}:n,a=i&&i.serialize,o;if(o=a?a(t,i):q.isURLSearchParams(t)?t.toString():new Ac(t,i).toString(r),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var Pc=class{constructor(){this.handlers=[]}use(e,t,n){return this.handlers.push({fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null}),this.handlers.length-1}eject(e){this.handlers[e]&&(this.handlers[e]=null)}clear(){this.handlers&&=[]}forEach(e){q.forEach(this.handlers,function(t){t!==null&&e(t)})}},Fc={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},Ic={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Ac,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},Lc=t({hasBrowserEnv:()=>Rc,hasStandardBrowserEnv:()=>Bc,hasStandardBrowserWebWorkerEnv:()=>Vc,navigator:()=>zc,origin:()=>Hc}),Rc=typeof window<`u`&&typeof document<`u`,zc=typeof navigator==`object`&&navigator||void 0,Bc=Rc&&(!zc||[`ReactNative`,`NativeScript`,`NS`].indexOf(zc.product)<0),Vc=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Hc=Rc&&window.location.href||`http://localhost`,Y={...Lc,...Ic};function Uc(e,t){return Oc(e,new Y.classes.URLSearchParams,{visitor:function(e,t,n,r){return Y.isNode&&q.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}function Wc(e){return q.matchAll(/\w+|\[(\w*)]/g,e).map(e=>e[0]===`[]`?``:e[1]||e[0])}function Gc(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&q.isArray(r)?r.length:a,s?(q.hasOwnProp(r,a)?r[a]=[r[a],n]:r[a]=n,!o):((!r[a]||!q.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&q.isArray(r[a])&&(r[a]=Gc(r[a])),!o)}if(q.isFormData(e)&&q.isFunction(e.entries)){let n={};return q.forEachEntry(e,(e,r)=>{t(Wc(e),r,n,0)}),n}return null}function qc(e,t,n){if(q.isString(e))try{return(t||JSON.parse)(e),q.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Jc={transitional:Fc,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=q.isObject(e);if(i&&q.isHTMLForm(e)&&(e=new FormData(e)),q.isFormData(e))return r?JSON.stringify(Kc(e)):e;if(q.isArrayBuffer(e)||q.isBuffer(e)||q.isStream(e)||q.isFile(e)||q.isBlob(e)||q.isReadableStream(e))return e;if(q.isArrayBufferView(e))return e.buffer;if(q.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return Uc(e,this.formSerializer).toString();if((a=q.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let t=this.env&&this.env.FormData;return Oc(a?{"files[]":e}:e,t&&new t,this.formSerializer)}}return i||r?(t.setContentType(`application/json`,!1),qc(e)):e}],transformResponse:[function(e){let t=this.transitional||Jc.transitional,n=t&&t.forcedJSONParsing,r=this.responseType===`json`;if(q.isResponse(e)||q.isReadableStream(e))return e;if(e&&q.isString(e)&&(n&&!this.responseType||r)){let n=!(t&&t.silentJSONParsing)&&r;try{return JSON.parse(e,this.parseReviver)}catch(e){if(n)throw e.name===`SyntaxError`?J.from(e,J.ERR_BAD_RESPONSE,this,null,this.response):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Y.classes.FormData,Blob:Y.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};q.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`],e=>{Jc.headers[e]={}});var Yc=q.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),Xc=e=>{let t={},n,r,i;return e&&e.split(` +`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim(),!(!n||t[n]&&Yc[n])&&(n===`set-cookie`?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+`, `+r:r)}),t},Zc=Symbol(`internals`);function Qc(e){return e&&String(e).trim().toLowerCase()}function $c(e){return e===!1||e==null?e:q.isArray(e)?e.map($c):String(e)}function el(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var tl=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function nl(e,t,n,r,i){if(q.isFunction(r))return r.call(this,t,n);if(i&&(t=n),q.isString(t)){if(q.isString(r))return t.indexOf(r)!==-1;if(q.isRegExp(r))return r.test(t)}}function rl(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function il(e,t){let n=q.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var al=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=Qc(t);if(!i)throw Error(`header name must be a non-empty string`);let a=q.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=$c(e))}let a=(e,t)=>q.forEach(e,(e,n)=>i(e,n,t));if(q.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(q.isString(e)&&(e=e.trim())&&!tl(e))a(Xc(e),t);else if(q.isObject(e)&&q.isIterable(e)){let n={},r,i;for(let t of e){if(!q.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);n[i=t[0]]=(r=n[i])?q.isArray(r)?[...r,t[1]]:[r,t[1]]:t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=Qc(e),e){let n=q.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return el(e);if(q.isFunction(t))return t.call(this,e,n);if(q.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=Qc(e),e){let n=q.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||nl(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=Qc(e),e){let i=q.findKey(n,e);i&&(!t||nl(n,n[i],i,t))&&(delete n[i],r=!0)}}return q.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||nl(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return q.forEach(this,(r,i)=>{let a=q.findKey(n,i);if(a){t[a]=$c(r),delete t[i];return}let o=e?rl(i):String(i).trim();o!==i&&delete t[i],t[o]=$c(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return q.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&q.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` +`)}getSetCookie(){return this.get(`set-cookie`)||[]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[Zc]=this[Zc]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=Qc(e);t[r]||(il(n,e),t[r]=!0)}return q.isArray(e)?e.forEach(r):r(e),this}};al.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),q.reduceDescriptors(al.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),q.freezeMethods(al);function ol(e,t){let n=this||Jc,r=t||n,i=al.from(r.headers),a=r.data;return q.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function sl(e){return!!(e&&e.__CANCEL__)}var cl=class extends J{constructor(e,t,n){super(e??`canceled`,J.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function ll(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new J(`Request failed with status code `+n.status,[J.ERR_BAD_REQUEST,J.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function ul(e){let t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||``}function dl(e,t){e||=10;let n=Array(e),r=Array(e),i=0,a=0,o;return t=t===void 0?1e3:t,function(s){let c=Date.now(),l=r[a];o||=c,n[i]=s,r[i]=c;let u=a,d=0;for(;u!==i;)d+=n[u++],u%=e;if(i=(i+1)%e,i===a&&(a=(a+1)%e),c-o{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i)]}var pl=(e,t,n=3)=>{let r=0,i=dl(50,250);return fl(n=>{let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=a-r,c=i(s),l=a<=o;r=a,e({loaded:a,total:o,progress:o?a/o:void 0,bytes:s,rate:c||void 0,estimated:c&&o&&l?(o-a)/c:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},ml=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},hl=e=>(...t)=>q.asap(()=>e(...t)),gl=Y.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Y.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Y.origin),Y.navigator&&/(msie|trident)/i.test(Y.navigator.userAgent)):()=>!0,_l=Y.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];q.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),q.isString(r)&&s.push(`path=${r}`),q.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),q.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.match(RegExp(`(?:^|; )`+e+`=([^;]*)`));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,``,Date.now()-864e5,`/`)}}:{write(){},read(){return null},remove(){}};function vl(e){return typeof e==`string`?/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e):!1}function yl(e,t){return t?e.replace(/\/?\/$/,``)+`/`+t.replace(/^\/+/,``):e}function bl(e,t,n){let r=!vl(t);return e&&(r||n==0)?yl(e,t):t}var xl=e=>e instanceof al?{...e}:e;function Sl(e,t){t||={};let n={};function r(e,t,n,r){return q.isPlainObject(e)&&q.isPlainObject(t)?q.merge.call({caseless:r},e,t):q.isPlainObject(t)?q.merge({},t):q.isArray(t)?t.slice():t}function i(e,t,n,i){if(!q.isUndefined(t))return r(e,t,n,i);if(!q.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!q.isUndefined(t))return r(void 0,t)}function o(e,t){if(!q.isUndefined(t))return r(void 0,t);if(!q.isUndefined(e))return r(void 0,e)}function s(n,i,a){if(a in t)return r(n,i);if(a in e)return r(void 0,n)}let c={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,responseEncoding:o,validateStatus:s,headers:(e,t,n)=>i(xl(e),xl(t),n,!0)};return q.forEach(Object.keys({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=q.hasOwnProp(c,r)?c[r]:i,o=a(e[r],t[r],r);q.isUndefined(o)&&a!==s||(n[r]=o)}),n}var Cl=e=>{let t=Sl({},e),{data:n,withXSRFToken:r,xsrfHeaderName:i,xsrfCookieName:a,headers:o,auth:s}=t;if(t.headers=o=al.from(o),t.url=Nc(bl(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),s&&o.set(`Authorization`,`Basic `+btoa((s.username||``)+`:`+(s.password?unescape(encodeURIComponent(s.password)):``))),q.isFormData(n)){if(Y.hasStandardBrowserEnv||Y.hasStandardBrowserWebWorkerEnv)o.setContentType(void 0);else if(q.isFunction(n.getHeaders)){let e=n.getHeaders(),t=[`content-type`,`content-length`];Object.entries(e).forEach(([e,n])=>{t.includes(e.toLowerCase())&&o.set(e,n)})}}if(Y.hasStandardBrowserEnv&&(r&&q.isFunction(r)&&(r=r(t)),r||r!==!1&&gl(t.url))){let e=i&&a&&_l.read(a);e&&o.set(i,e)}return t},wl=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Cl(e),i=r.data,a=al.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p;function m(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let h=new XMLHttpRequest;h.open(r.method.toUpperCase(),r.url,!0),h.timeout=r.timeout;function g(){if(!h)return;let r=al.from(`getAllResponseHeaders`in h&&h.getAllResponseHeaders());ll(function(e){t(e),m()},function(e){n(e),m()},{data:!o||o===`text`||o===`json`?h.responseText:h.response,status:h.status,statusText:h.statusText,headers:r,config:e,request:h}),h=null}`onloadend`in h?h.onloadend=g:h.onreadystatechange=function(){!h||h.readyState!==4||h.status===0&&!(h.responseURL&&h.responseURL.indexOf(`file:`)===0)||setTimeout(g)},h.onabort=function(){h&&=(n(new J(`Request aborted`,J.ECONNABORTED,e,h)),null)},h.onerror=function(t){let r=new J(t&&t.message?t.message:`Network Error`,J.ERR_NETWORK,e,h);r.event=t||null,n(r),h=null},h.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Fc;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new J(t,i.clarifyTimeoutError?J.ETIMEDOUT:J.ECONNABORTED,e,h)),h=null},i===void 0&&a.setContentType(null),`setRequestHeader`in h&&q.forEach(a.toJSON(),function(e,t){h.setRequestHeader(t,e)}),q.isUndefined(r.withCredentials)||(h.withCredentials=!!r.withCredentials),o&&o!==`json`&&(h.responseType=r.responseType),c&&([d,p]=pl(c,!0),h.addEventListener(`progress`,d)),s&&h.upload&&([u,f]=pl(s),h.upload.addEventListener(`progress`,u),h.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{h&&=(n(!t||t.type?new cl(null,e,h):t),h.abort(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let _=ul(r.url);if(_&&Y.protocols.indexOf(_)===-1){n(new J(`Unsupported protocol `+_+`:`,J.ERR_BAD_REQUEST,e));return}h.send(i||null)})},Tl=(e,t)=>{let{length:n}=e=e?e.filter(Boolean):[];if(t||n){let n=new AbortController,r,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof J?t:new cl(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new J(`timeout of ${t}ms exceeded`,J.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>e.addEventListener(`abort`,i));let{signal:s}=n;return s.unsubscribe=()=>q.asap(o),s}},El=function*(e,t){let n=e.byteLength;if(!t||n{let i=Dl(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Al=64*1024,{isFunction:jl}=q,Ml=(({Request:e,Response:t})=>({Request:e,Response:t}))(q.global),{ReadableStream:Nl,TextEncoder:Pl}=q.global,Fl=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Il=e=>{e=q.merge.call({skipUndefined:!0},Ml,e);let{fetch:t,Request:n,Response:r}=e,i=t?jl(t):typeof fetch==`function`,a=jl(n),o=jl(r);if(!i)return!1;let s=i&&jl(Nl),c=i&&(typeof Pl==`function`?(e=>t=>e.encode(t))(new Pl):async e=>new Uint8Array(await new n(e).arrayBuffer())),l=a&&s&&Fl(()=>{let e=!1,t=new n(Y.origin,{body:new Nl,method:`POST`,get duplex(){return e=!0,`half`}}).headers.has(`Content-Type`);return e&&!t}),u=o&&s&&Fl(()=>q.isReadableStream(new r(``).body)),d={stream:u&&(e=>e.body)};i&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!d[e]&&(d[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new J(`Response type '${e}' is not supported`,J.ERR_NOT_SUPPORT,n)})});let f=async e=>{if(e==null)return 0;if(q.isBlob(e))return e.size;if(q.isSpecCompliantForm(e))return(await new n(Y.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(q.isArrayBufferView(e)||q.isArrayBuffer(e))return e.byteLength;if(q.isURLSearchParams(e)&&(e+=``),q.isString(e))return(await c(e)).byteLength},p=async(e,t)=>q.toFiniteNumber(e.getContentLength())??f(t);return async e=>{let{url:i,method:o,data:s,signal:c,cancelToken:f,timeout:m,onDownloadProgress:h,onUploadProgress:g,responseType:_,headers:v,withCredentials:y=`same-origin`,fetchOptions:b}=Cl(e),x=t||fetch;_=_?(_+``).toLowerCase():`text`;let S=Tl([c,f&&f.toAbortSignal()],m),C=null,w=S&&S.unsubscribe&&(()=>{S.unsubscribe()}),T;try{if(g&&l&&o!==`get`&&o!==`head`&&(T=await p(v,s))!==0){let e=new n(i,{method:`POST`,body:s,duplex:`half`}),t;if(q.isFormData(s)&&(t=e.headers.get(`content-type`))&&v.setContentType(t),e.body){let[t,n]=ml(T,pl(hl(g)));s=kl(e.body,Al,t,n)}}q.isString(y)||(y=y?`include`:`omit`);let t=a&&`credentials`in n.prototype,c={...b,signal:S,method:o.toUpperCase(),headers:v.normalize().toJSON(),body:s,duplex:`half`,credentials:t?y:void 0};C=a&&new n(i,c);let f=await(a?x(C,b):x(i,c)),m=u&&(_===`stream`||_===`response`);if(u&&(h||m&&w)){let e={};[`status`,`statusText`,`headers`].forEach(t=>{e[t]=f[t]});let t=q.toFiniteNumber(f.headers.get(`content-length`)),[n,i]=h&&ml(t,pl(hl(h),!0))||[];f=new r(kl(f.body,Al,n,()=>{i&&i(),w&&w()}),e)}_||=`text`;let E=await d[q.findKey(d,_)||`text`](f,e);return!m&&w&&w(),await new Promise((t,n)=>{ll(t,n,{data:E,headers:al.from(f.headers),status:f.status,statusText:f.statusText,config:e,request:C})})}catch(t){throw w&&w(),t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)?Object.assign(new J(`Network Error`,J.ERR_NETWORK,e,C,t&&t.response),{cause:t.cause||t}):J.from(t,t&&t.code,e,C,t&&t.response)}}},Ll=new Map,Rl=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Ll;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:Il(t)),l=c;return c};Rl();var zl={http:null,xhr:wl,fetch:{get:Rl}};q.forEach(zl,(e,t)=>{if(e){try{Object.defineProperty(e,`name`,{value:t})}catch{}Object.defineProperty(e,`adapterName`,{value:t})}});var Bl=e=>`- ${e}`,Vl=e=>q.isFunction(e)||e===null||e===!1;function Hl(e,t){e=q.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new J(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : +`+e.map(Bl).join(` +`):` `+Bl(e[0]):`as no adapter specified`),`ERR_NOT_SUPPORT`)}return i}var Ul={getAdapter:Hl,adapters:zl};function Wl(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new cl(null,e)}function Gl(e){return Wl(e),e.headers=al.from(e.headers),e.data=ol.call(e,e.transformRequest),[`post`,`put`,`patch`].indexOf(e.method)!==-1&&e.headers.setContentType(`application/x-www-form-urlencoded`,!1),Ul.getAdapter(e.adapter||Jc.adapter,e)(e).then(function(t){return Wl(e),t.data=ol.call(e,e.transformResponse,t),t.headers=al.from(t.headers),t},function(t){return sl(t)||(Wl(e),t&&t.response&&(t.response.data=ol.call(e,e.transformResponse,t.response),t.response.headers=al.from(t.response.headers))),Promise.reject(t)})}var Kl=`1.13.6`,ql={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{ql[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var Jl={};ql.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Kl+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new J(r(i,` has been removed`+(t?` in `+t:``)),J.ERR_DEPRECATED);return t&&!Jl[i]&&(Jl[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),e?e(n,i,a):!0}},ql.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function Yl(e,t,n){if(typeof e!=`object`)throw new J(`options must be an object`,J.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-- >0;){let a=r[i],o=t[a];if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new J(`option `+a+` must be `+n,J.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new J(`Unknown option `+a,J.ERR_BAD_OPTION)}}var Xl={assertOptions:Yl,validators:ql},Zl=Xl.validators,Ql=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Pc,response:new Pc}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error){let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack?t.stack.replace(/^.+\n/,``):``;try{e.stack?n&&!String(e.stack).endsWith(n.replace(/^.+\n.+\n/,``))&&(e.stack+=` +`+n):e.stack=n}catch{}}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Sl(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&Xl.assertOptions(n,{silentJSONParsing:Zl.transitional(Zl.boolean),forcedJSONParsing:Zl.transitional(Zl.boolean),clarifyTimeoutError:Zl.transitional(Zl.boolean),legacyInterceptorReqResOrdering:Zl.transitional(Zl.boolean)},!1),r!=null&&(q.isFunction(r)?t.paramsSerializer={serialize:r}:Xl.assertOptions(r,{encode:Zl.function,serialize:Zl.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),Xl.assertOptions(t,{baseUrl:Zl.spelling(`baseURL`),withXsrfToken:Zl.spelling(`withXSRFToken`)},!0),t.method=(t.method||this.defaults.method||`get`).toLowerCase();let a=i&&q.merge(i.common,i[t.method]);i&&q.forEach([`delete`,`get`,`head`,`post`,`put`,`patch`,`common`],e=>{delete i[e]}),t.headers=al.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Fc;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[Gl.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);u{if(!n._listeners)return;let t=n._listeners.length;for(;t-- >0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new cl(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function eu(e){return function(t){return e.apply(null,t)}}function tu(e){return q.isObject(e)&&e.isAxiosError===!0}var nu={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(nu).forEach(([e,t])=>{nu[t]=e});function ru(e){let t=new Ql(e),n=fs(Ql.prototype.request,t);return q.extend(n,Ql.prototype,t,{allOwnKeys:!0}),q.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return ru(Sl(e,t))},n}var X=ru(Jc);X.Axios=Ql,X.CanceledError=cl,X.CancelToken=$l,X.isCancel=sl,X.VERSION=Kl,X.toFormData=Oc,X.AxiosError=J,X.Cancel=X.CanceledError,X.all=function(e){return Promise.all(e)},X.spread=eu,X.isAxiosError=tu,X.mergeConfig=Sl,X.AxiosHeaders=al,X.formToJSON=e=>Kc(q.isHTMLForm(e)?new FormData(e):e),X.getAdapter=Ul.getAdapter,X.HttpStatusCode=nu,X.default=X;var iu=`Hua.Todo_cloud_sync_settings`,au=`Hua.Todo_cloud_sync_session`,Z=class{static loadSettings(){try{let e=localStorage.getItem(iu);if(!e)return{serverUrl:``,enabled:!1};let t=JSON.parse(e);return{serverUrl:typeof t.serverUrl==`string`?t.serverUrl:``,enabled:typeof t.enabled==`boolean`?t.enabled:!1}}catch{return{serverUrl:``,enabled:!1}}}static saveSettings(e){localStorage.setItem(iu,JSON.stringify(e))}static clearSettings(){localStorage.removeItem(iu)}static loadSession(){try{let e=sessionStorage.getItem(au);if(!e)return null;let t=JSON.parse(e);return!t.accessToken||typeof t.accessToken!=`string`?null:{accessToken:t.accessToken,expiresAtUtc:typeof t.expiresAtUtc==`string`?t.expiresAtUtc:``,userId:typeof t.userId==`string`?t.userId:``,role:typeof t.role==`string`?t.role:``,permissions:Array.isArray(t.permissions)?t.permissions.filter(e=>typeof e==`string`):[]}}catch{return null}}static saveSession(e){sessionStorage.setItem(au,JSON.stringify(e))}static clearSession(){sessionStorage.removeItem(au)}},Q=X.create({baseURL:`/api`,timeout:1e4,headers:{"Content-Type":`application/json`}});Q.interceptors.request.use(e=>{let t=Z.loadSession();return t?.accessToken&&(e.headers=e.headers??{},e.headers.Authorization=`Bearer ${t.accessToken}`),e});var ou=(e,t=`error`)=>{window.showToast?window.showToast(e,t):window.alert(e)};Q.interceptors.response.use(e=>{let t=e.data;if(console.log(`API Response:`,e.config.url,t),t&&typeof t==`object`&&(t.success===!1||t.Success===!1)){let e=t.errors||t.Errors,n=t.message||t.Message,r=e&&Array.isArray(e)&&e.length>0?e.join(` +`):n||`操作失败`;return console.error(`Business Error:`,r),ou(r,`error`),Promise.reject(Error(r))}return e},e=>{console.error(`API Error:`,e);let t=`网络错误,请稍后再试`;if(e.response&&e.response.data){let n=e.response.data,r=n.errors||n.Errors,i=n.message||n.Message;r&&Array.isArray(r)&&r.length>0?t=r.join(` +`):i&&(t=i)}else e.message&&(t=e.message);return ou(t,`error`),Promise.reject(e)});var su=e=>{if(e===0||e===1||e===2)return e;if(typeof e==`number`&&Number.isFinite(e))return e<=0?0:e===1?1:2;if(typeof e==`string`){let t=e.trim();if(t===`0`||t===`1`||t===`2`)return Number(t);let n=t.toLowerCase();if(n===`low`)return 0;if(n===`medium`||n===`mid`)return 1;if(n===`high`)return 2}return 1},cu=e=>typeof e==`string`&&e.trim()?e:e instanceof Date&&Number.isFinite(e.getTime())?e.toISOString():typeof e==`number`&&Number.isFinite(e)?new Date(e).toISOString():new Date().toISOString(),lu=e=>{let t={...e??{}};return t.id=typeof t.id==`number`?String(t.id):t.id??``,t.code=typeof t.code==`string`&&t.code.trim()?t.code:void 0,t.title=typeof t.title==`string`?t.title:String(t.title??``),t.priority=su(t.priority),t.isCompleted=typeof t.isCompleted==`boolean`?t.isCompleted:!!t.isCompleted,t.createdAt=cu(t.createdAt),t.updatedAt=cu(t.updatedAt),t.parentTaskId===null||t.parentTaskId===0?t.parentTaskId=void 0:typeof t.parentTaskId==`number`&&(t.parentTaskId=String(t.parentTaskId)),Array.isArray(t.subTasks)?t.subTasks=t.subTasks.map(lu):t.subTasks=[],t},uu=e=>Array.isArray(e)?e.map(lu):[],du=`Hua.Todo_tasks`,fu=`Hua.Todo_sync_status`,$=class{static saveTasks(e){try{localStorage.setItem(du,JSON.stringify(uu(e)))}catch(e){console.error(`Failed to save tasks to localStorage:`,e)}}static loadTasks(){try{let e=localStorage.getItem(du);return e?uu(JSON.parse(e)):[]}catch(e){return console.error(`Failed to load tasks from localStorage:`,e),[]}}static clearTasks(){try{localStorage.removeItem(du)}catch(e){console.error(`Failed to clear tasks from localStorage:`,e)}}static saveSyncStatus(e){try{localStorage.setItem(fu,JSON.stringify(e))}catch(e){console.error(`Failed to save sync status:`,e)}}static loadSyncStatus(){try{let e=localStorage.getItem(fu);return e?JSON.parse(e):{lastSyncTime:0,isOnline:!0,pendingChanges:0}}catch(e){return console.error(`Failed to load sync status:`,e),{lastSyncTime:0,isOnline:!0,pendingChanges:0}}}static isOnline(){return navigator.onLine}static ALLOW_PERSIST_KEY=`Hua.Todo_allow_persist`;static setAllowPersist(e){try{let t=this.getAllowPersist();localStorage.setItem(this.ALLOW_PERSIST_KEY,String(e)),t&&!e&&(localStorage.removeItem(du),localStorage.removeItem(fu),console.log(`allowPersist 已禁止:所有持久化任务数据已清空`))}catch(e){console.error(`Failed to save allowPersist to localStorage:`,e)}}static getAllowPersist(){try{return localStorage.getItem(this.ALLOW_PERSIST_KEY)!==`false`}catch{return!0}}},pu={async getTasks(e){if(!$.isOnline()){let t=$.loadTasks();return{success:!0,data:e===void 0?t:t.filter(t=>t.isCompleted===e),message:`从本地加载任务`}}let t=e===!0?`/task/completed`:e===!1?`/task/active`:`/task`,n=await Q.get(t),r;if(r=Array.isArray(n.data)?{success:!0,data:n.data,message:`获取任务成功`}:n.data,r.success&&r.data){r.data=uu(r.data),$.saveTasks(r.data);let e=$.loadSyncStatus();e.lastSyncTime=Date.now(),e.isOnline=!0,e.pendingChanges=0,$.saveSyncStatus(e)}return r},async getTask(e){if(!$.isOnline()){let t=$.loadTasks().find(t=>t.id===e);return{success:!0,data:t||null,message:t?`从本地加载任务`:`任务不存在`}}let t=await Q.get(`/task/${e}`),n;return n=t.data&&`id`in t.data?{success:!0,data:t.data,message:`获取任务成功`}:t.data,n.success&&n.data&&(n.data=lu(n.data)),n},async createTask(e){let t={id:crypto.randomUUID(),title:e.title,priority:e.priority,isCompleted:!1,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),parentTaskId:e.parentTaskId,taskType:e.taskType??0,description:e.description,subTasks:[]};if(!$.isOnline()){let e=$.loadTasks();e.unshift(t),$.saveTasks(e);let n=$.loadSyncStatus();return n.pendingChanges++,$.saveSyncStatus(n),{success:!0,data:t,message:`离线创建任务成功`}}let n=await Q.post(`/task`,e),r;if(r=n.data&&`id`in n.data?{success:!0,data:n.data,message:`创建任务成功`}:n.data,r.success&&r.data){r.data=lu(r.data);let e=$.loadTasks(),n=e.findIndex(e=>e.id===t.id);n===-1?e.unshift(r.data):e[n]=r.data,$.saveTasks(e)}return r},async updateTask(e,t){let n=t;if(!$.isOnline()){let n=$.loadTasks(),r=n.findIndex(t=>t.id===e);if(r!==-1){t.title&&(n[r].title=t.title),t.priority!==void 0&&(n[r].priority=t.priority),t.description!==void 0&&(n[r].description=t.description),t.taskType!==void 0&&(n[r].taskType=t.taskType),n[r].updatedAt=new Date().toISOString(),$.saveTasks(n);let e=$.loadSyncStatus();return e.pendingChanges++,$.saveSyncStatus(e),{success:!0,data:n[r],message:`离线更新任务成功`}}return{success:!1,message:`任务不存在`}}let r=await Q.put(`/task`,n),i;if(i=r.data&&`id`in r.data?{success:!0,data:r.data,message:`更新任务成功`}:r.data,i.success&&i.data){i.data=lu(i.data);let t=$.loadTasks(),n=t.findIndex(t=>t.id===e);n!==-1&&(t[n]=i.data,$.saveTasks(t))}return i},async toggleComplete(e){if(!$.isOnline()){let t=$.loadTasks(),n=t.findIndex(t=>t.id===e);if(n!==-1){t[n].isCompleted=!t[n].isCompleted,t[n].updatedAt=new Date().toISOString(),$.saveTasks(t);let e=$.loadSyncStatus();return e.pendingChanges++,$.saveSyncStatus(e),{success:!0,data:t[n],message:`离线切换任务状态成功`}}return{success:!1,message:`任务不存在`}}let t=await Q.patch(`/task/${e}/toggle`),n;if(n=t.data&&`id`in t.data?{success:!0,data:t.data,message:`切换任务状态成功`}:t.data,n.success&&n.data){n.data=lu(n.data);let t=$.loadTasks(),r=t.findIndex(t=>t.id===e);r!==-1&&(t[r]=n.data,$.saveTasks(t))}return n},async deleteTask(e){if(!$.isOnline()){let t=$.loadTasks(),n=t.findIndex(t=>t.id===e);if(n!==-1){t.splice(n,1),$.saveTasks(t);let e=$.loadSyncStatus();return e.pendingChanges++,$.saveSyncStatus(e),{success:!0,message:`离线删除任务成功`}}return{success:!1,message:`任务不存在`}}let t=await Q.delete(`/task/${e}`),n;if(n=t.data&&typeof t.data==`object`&&`success`in t.data?t.data:{success:!0,message:`删除任务成功`},n.success){let t=$.loadTasks(),n=t.findIndex(t=>t.id===e);n!==-1&&(t.splice(n,1),$.saveTasks(t))}return n},async syncToServer(){if(!$.isOnline())return{success:!1,message:`当前离线,无法同步`};let e=$.loadTasks(),t=$.loadSyncStatus();if(t.pendingChanges===0)return{success:!0,data:e,message:`没有待同步的更改`};try{let e=await Q.get(`/task`),n;return n=Array.isArray(e.data)?{success:!0,data:e.data,message:`获取任务成功`}:e.data,n.success&&n.data?(n.data=uu(n.data),$.saveTasks(n.data),t.lastSyncTime=Date.now(),t.isOnline=!0,t.pendingChanges=0,$.saveSyncStatus(t),{success:!0,data:n.data,message:`同步成功`}):{success:!1,message:`同步失败`}}catch(e){return console.error(`Sync failed:`,e),{success:!1,message:`同步失败`}}}};async function mu(e,t,n,r){return(await Q.post(`/meeting/transcribeAudio`,{taskId:e,audioBase64:t,format:n,audioDuration:r})).data}async function hu(e,t){await Q.post(`/meeting/saveNotes`,{taskId:e,notes:t})}async function gu(e){return(await Q.post(`/meeting/breakdown`,{taskId:e})).data}async function _u(e,t){return(await Q.post(`/meeting/confirmBreakdown`,{taskId:e,subTasks:t})).data}var vu={async upload(e,t,n,r){let i=await Q.post(`/attachment/upload`,{taskId:e,fileName:t,base64Content:n,contentType:r});return i.data&&`success`in i.data?i.data:{success:!0,data:i.data,message:`上传成功`}},async addLink(e,t,n){let r=await Q.post(`/attachment/addLink`,{taskId:e,url:t,fileName:n});return r.data&&`success`in r.data?r.data:{success:!0,data:r.data,message:`链接已添加`}},async list(e){let t=await Q.get(`/attachment?taskId=${e}`);return Array.isArray(t.data)?{success:!0,data:t.data,message:`获取成功`}:t.data},async remove(e){let t=await Q.delete(`/attachment?id=${e}`);return t.data&&`success`in t.data?t.data:{success:!0,message:`已删除`}},async open(e){let t=await Q.post(`/attachment/open?id=${e}`);return t.data&&`success`in t.data?t.data:{success:!0,data:t.data,message:`已打开`}},async download(e){let t=await Q.get(`/attachment?taskId=0&id=${e}`);return t.data&&`success`in t.data?t.data:{success:!0,data:t.data,message:`获取成功`}}};function yu(){let e=F([]),t=F(!1),n=F(``);async function r(r){t.value=!0,n.value=``;try{let t=await vu.list(r);t.success&&t.data&&(e.value=t.data.map(e=>({...e,isMissing:!1})))}catch(e){n.value=e?.message||`加载附件失败`,console.error(`loadAttachments failed:`,e)}finally{t.value=!1}}async function i(r,i){if(i.size>50*1024*1024)return n.value=`文件大小不能超过 50MB`,!1;if(e.value.length>=20)return n.value=`每个待办项最多 20 个附件`,!1;t.value=!0,n.value=``;try{let t=await bu(i),a=await vu.upload(r,i.name,t,i.type);return a.success&&a.data?(e.value.push({...a.data,isMissing:!1}),!0):(n.value=a.message||`上传失败`,!1)}catch(e){return n.value=e?.message||`上传失败`,console.error(`uploadFile failed:`,e),!1}finally{t.value=!1}}async function a(r,i,a){if(e.value.length>=20)return n.value=`每个待办项最多 20 个附件`,!1;t.value=!0,n.value=``;try{let t=await vu.addLink(r,i,a);return t.success&&t.data?(e.value.push({...t.data,isMissing:!1}),!0):(n.value=t.message||`添加链接失败`,!1)}catch(e){return n.value=e?.message||`添加链接失败`,console.error(`addLinkAttachment failed:`,e),!1}finally{t.value=!1}}async function o(r){t.value=!0,n.value=``;try{let t=await vu.remove(r);return t.success?(e.value=e.value.filter(e=>e.id!==r),!0):(n.value=t.message||`删除失败`,!1)}catch(e){return n.value=e?.message||`删除失败`,console.error(`removeAttachment failed:`,e),!1}finally{t.value=!1}}async function s(e){try{let t=await vu.open(e);return t.success?!0:(n.value=t.message||`打开失败`,!1)}catch(e){return n.value=e?.message||`打开失败`,console.error(`openAttachment failed:`,e),!1}}function c(t){if(t.attachments){e.value=t.attachments.map(e=>({...e,isMissing:!1}));return}t.attachmentCount!==void 0&&t.attachmentCount>0?r(t.id):e.value=[]}return{attachments:e,isLoading:t,error:n,loadAttachments:r,uploadFile:i,addLinkAttachment:a,removeAttachment:o,openAttachment:s,initFromTask:c}}function bu(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{let e=r.result;t(e.split(`,`)[1]||e)},r.onerror=()=>n(Error(`读取文件失败`)),r.readAsDataURL(e)})}var xu={class:`attachment-section`},Su={class:`attachment-header`},Cu={class:`attachment-title`},wu={class:`attachment-actions`},Tu={key:0,class:`attachment-empty`},Eu={key:1,class:`attachment-list`},Du={class:`file-icon`},Ou={class:`file-info`},ku=[`title`],Au={key:0,class:`file-size`},ju={key:1,class:`missing-tag`},Mu={class:`file-actions`},Nu=[`onClick`],Pu=[`onClick`],Fu=ir({__name:`AttachmentList`,props:{items:{}},emits:[`upload`,`addLink`,`open`,`remove`],setup(e){function t(e){return e?e.startsWith(`image/`)?`🖼`:e.startsWith(`audio/`)||e.includes(`wav`)||e.includes(`webm`)?`🎵`:e.startsWith(`video/`)?`🎬`:e.includes(`pdf`)?`📕`:e.includes(`word`)||e.includes(`document`)?`📝`:e.includes(`sheet`)||e.includes(`excel`)?`📊`:e.includes(`zip`)||e.includes(`rar`)||e.includes(`tar`)?`📦`:`📄`:`📄`}function n(e){return e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}return(r,i)=>(V(),H(`div`,xu,[U(`div`,Su,[U(`span`,Cu,`📎 附件 (`+A(e.items.length)+`)`,1),U(`div`,wu,[U(`button`,{class:`btn-attach`,onClick:i[0]||=e=>r.$emit(`upload`),title:`上传文件`},[...i[2]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12`})],-1),ca(` 上传 `,-1)]]),U(`button`,{class:`btn-link`,onClick:i[1]||=e=>r.$emit(`addLink`),title:`添加链接`},[...i[3]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71`}),U(`path`,{d:`M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71`})],-1),ca(` 链接 `,-1)]])])]),e.items.length===0?(V(),H(`div`,Tu,` 暂无附件 `)):(V(),H(`div`,Eu,[(V(!0),H(z,null,Nr(e.items,e=>(V(),H(`div`,{key:e.id,class:_e([`attachment-item`,{"is-missing":e.isMissing}])},[U(`span`,Du,A(e.attachmentType===1?`🔗`:t(e.contentType)),1),U(`div`,Ou,[U(`span`,{class:`file-name`,title:e.fileName},A(e.fileName),9,ku),e.fileSize>0?(V(),H(`span`,Au,A(n(e.fileSize)),1)):G(``,!0),e.isMissing?(V(),H(`span`,ju,`⚠ 文件丢失`)):G(``,!0)]),U(`div`,Mu,[e.isMissing?G(``,!0):(V(),H(`button`,{key:0,class:`btn-file btn-open`,onClick:t=>r.$emit(`open`,e.id),title:`打开`},[...i[4]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3`})],-1)]],8,Nu)),U(`button`,{class:`btn-file btn-delete`,onClick:t=>r.$emit(`remove`,e.id),title:`删除`},[...i[5]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),U(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,Pu)])],2))),128))]))]))}}),Iu=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},Lu=Iu(Fu,[[`__scopeId`,`data-v-5b5fc0e2`]]),Ru={class:`link-dialog`},zu={class:`link-header`},Bu={class:`link-body`},Vu={class:`form-group`},Hu={class:`form-group`},Uu={class:`link-footer`},Wu=[`disabled`],Gu=Iu(ir({__name:`LinkInputDialog`,props:{visible:{type:Boolean}},emits:[`close`,`confirmed`],setup(e,{emit:t}){let n=t,r=F(``),i=F(``);function a(){let e=r.value.trim();if(!e)return;let t=e;/^https?:\/\//i.test(t)||(t=`https://`+t),n(`confirmed`,t,i.value.trim()||t),r.value=``,i.value=``,n(`close`)}return(t,n)=>e.visible?(V(),H(`div`,{key:0,class:`link-overlay`,onClick:n[4]||=rs(e=>t.$emit(`close`),[`self`])},[U(`div`,Ru,[U(`div`,zu,[n[5]||=U(`h4`,null,`添加外部链接`,-1),U(`button`,{class:`btn-close`,onClick:n[0]||=e=>t.$emit(`close`)},`×`)]),U(`div`,Bu,[U(`div`,Vu,[n[6]||=U(`label`,{class:`form-label`},`链接地址`,-1),L(U(`input`,{"onUpdate:modelValue":n[1]||=e=>r.value=e,type:`url`,placeholder:`https://example.com/doc`,class:`form-input`,onKeyup:as(a,[`enter`])},null,544),[[Jo,r.value]])]),U(`div`,Hu,[n[7]||=U(`label`,{class:`form-label`},`显示名称(可选)`,-1),L(U(`input`,{"onUpdate:modelValue":n[2]||=e=>i.value=e,type:`text`,placeholder:`留空则显示链接地址`,class:`form-input`,onKeyup:as(a,[`enter`])},null,544),[[Jo,i.value]])])]),U(`div`,Uu,[U(`button`,{class:`btn-cancel`,onClick:n[3]||=e=>t.$emit(`close`)},`取消`),U(`button`,{class:`btn-confirm`,disabled:!r.value.trim(),onClick:a},` 添加链接 `,8,Wu)])])])):G(``,!0)}}),[[`__scopeId`,`data-v-5d086492`]]),Ku={class:`dialog-content`},qu={class:`dialog-body`},Ju={class:`form-group`},Yu={class:`form-group`},Xu={class:`form-group`},Zu={class:`char-count`},Qu={key:0,class:`attach-error`},$u={class:`dialog-footer`},ed=[`disabled`],td=Iu(ir({__name:`TaskEditDialog`,props:{visible:{type:Boolean},task:{}},emits:[`update:visible`,`saved`],setup(e,{emit:t}){let n=e,r=t,i=F(``),a=F(1),o=F(``),s=F(!1),c=F(null),l=yu();Ln(()=>l.attachments.value.length,e=>{n.task&&(n.task.attachmentCount=e,n.task.attachments=l.attachments.value.map(e=>({id:e.id,taskId:e.taskId,fileName:e.fileName,filePath:e.filePath,fileSize:e.fileSize,contentType:e.contentType,attachmentType:e.attachmentType,createdAt:e.createdAt})))}),Ln(()=>n.task,e=>{e&&(i.value=e.title,a.value=e.priority,o.value=e.description||``,l.initFromTask(e))},{immediate:!0});function u(){c.value?.click()}async function d(e){let t=e.target,r=t.files?.[0];!r||!n.task||(await l.uploadFile(n.task.id,r)&&h(`附件上传成功`,`success`),t.value=``)}async function f(e){await l.openAttachment(e)}async function p(e){confirm(`确定要删除此附件吗?`)&&await l.removeAttachment(e)&&h(`附件已删除`,`success`)}async function m(e,t){n.task&&await l.addLinkAttachment(n.task.id,e,t)&&h(`链接已添加`,`success`)}function h(e,t=`error`){window.showToast?window.showToast(e,t):t===`error`?window.alert(e):console.log(e)}let g=async()=>{if(!n.task||!i.value.trim()){alert(`请输入任务标题`);return}try{let e=await pu.updateTask(n.task.id,{id:n.task.id,title:i.value.trim(),priority:a.value,description:o.value});e.success&&e.data&&(n.task.attachments&&(e.data.attachments=n.task.attachments,e.data.attachmentCount=n.task.attachmentCount),r(`saved`,e.data),r(`update:visible`,!1))}catch(e){console.error(`Failed to update task:`,e),alert(`更新任务失败`)}},_=()=>{r(`update:visible`,!1)};return(t,n)=>e.visible?(V(),H(`div`,{key:0,class:`dialog-overlay`,onClick:rs(_,[`self`])},[U(`div`,Ku,[U(`div`,{class:`dialog-header`},[n[5]||=U(`h2`,{class:`dialog-title`},`编辑待办项`,-1),U(`button`,{onClick:_,class:`btn-close`},`×`)]),U(`div`,qu,[U(`div`,Ju,[n[6]||=U(`label`,{class:`form-label`},`标题`,-1),L(U(`input`,{"onUpdate:modelValue":n[0]||=e=>i.value=e,type:`text`,placeholder:`输入任务标题...`,class:`form-input`,onKeyup:as(g,[`enter`])},null,544),[[Jo,i.value]])]),U(`div`,Yu,[n[8]||=U(`label`,{class:`form-label`},`优先级`,-1),L(U(`select`,{"onUpdate:modelValue":n[1]||=e=>a.value=e,class:`form-select`},[...n[7]||=[U(`option`,{value:``,disabled:``},`请选择优先级`,-1),U(`option`,{value:0},`低`,-1),U(`option`,{value:1},`中`,-1),U(`option`,{value:2},`高`,-1)]],512),[[Zo,a.value]])]),U(`div`,Xu,[n[9]||=U(`label`,{class:`form-label`},`描述`,-1),L(U(`textarea`,{"onUpdate:modelValue":n[2]||=e=>o.value=e,placeholder:`添加备注、步骤说明...`,class:`form-textarea`,maxlength:`5000`},null,512),[[Jo,o.value]]),U(`span`,Zu,A(o.value.length)+`/5000`,1)]),W(Lu,{items:Zt(l).attachments.value,onUpload:u,onAddLink:n[3]||=e=>s.value=!0,onOpen:f,onRemove:p},null,8,[`items`]),Zt(l).error?(V(),H(`div`,Qu,A(Zt(l).error),1)):G(``,!0),U(`input`,{ref_key:`fileInput`,ref:c,type:`file`,class:`hidden-file-input`,onChange:d},null,544)]),U(`div`,$u,[U(`button`,{onClick:_,class:`btn-cancel`},`取消`),U(`button`,{onClick:g,class:`btn-save`,disabled:Zt(l).isLoading.value},A(Zt(l).isLoading.value?`保存中...`:`保存`),9,ed)])]),W(Gu,{visible:s.value,onClose:n[4]||=e=>s.value=!1,onConfirmed:m},null,8,[`visible`])])):G(``,!0)}}),[[`__scopeId`,`data-v-7854be41`]]);function nd(){let e=F(!1),t=F(!1),n=F(0),r=F(null),i=F(null),a=F(null),o=F(typeof MediaRecorder<`u`),s=null,c=null,l=[];function u(){for(let e of[`audio/webm`,`audio/webm;codecs=opus`,`audio/mp4`,`audio/ogg;codecs=opus`])if(MediaRecorder.isTypeSupported(e))return e;return`audio/webm`}function d(){let e=u();return e.includes(`mp4`)?`mp4`:e.includes(`ogg`)?`ogg`:`webm`}async function f(){if(a.value=null,!o.value){a.value=`当前浏览器不支持录音,请使用文字输入`;return}try{let t=await navigator.mediaDevices.getUserMedia({audio:!0}),n=u();s=new MediaRecorder(t,{mimeType:n}),l=[],s.ondataavailable=e=>{e.data.size>0&&l.push(e.data)},s.onstop=()=>{t.getTracks().forEach(e=>e.stop());let e=s?.mimeType||n;r.value=new Blob(l,{type:e}),i.value&&URL.revokeObjectURL(i.value),i.value=URL.createObjectURL(r.value)},s.onerror=()=>{a.value=`录音过程中发生错误`,_(),e.value=!1},s.start(),e.value=!0,g()}catch(e){e.name===`NotAllowedError`||e.name===`PermissionDeniedError`?a.value=`无法访问麦克风,请使用文字输入`:a.value=`无法启动录音:`+(e.message||`未知错误`)}}function p(){!s||s.state===`inactive`||(_(),s.stop(),e.value=!1)}function m(){_(),s&&s.state!==`inactive`&&s.stop(),e.value=!1,t.value=!1,n.value=0,i.value&&URL.revokeObjectURL(i.value),r.value=null,i.value=null,a.value=null,l=[]}function h(){return r.value}function g(){_(),n.value=0,c=setInterval(()=>{n.value++,n.value>=7200&&p()},1e3)}function _(){c!==null&&(clearInterval(c),c=null)}return Cr(()=>{_(),s&&s.state!==`inactive`&&s.stop(),i.value&&URL.revokeObjectURL(i.value)}),{isRecording:e,isPaused:t,duration:n,audioBlob:r,audioUrl:i,error:a,isSupported:o,getFormat:d,startRecording:f,stopRecording:p,resetRecording:m,getAudioBlob:h}}var rd={class:`audio-recorder`},id={key:0,class:`error-message`},ad={key:1,class:`unsupported-message`},od={key:2},sd={key:0,class:`ready-state`},cd={key:1,class:`recording-state`},ld={class:`recording-header`},ud={class:`timer`},dd={class:`waveform`},fd={key:2,class:`stopped-state`},pd={class:`stopped-info`},md={class:`stopped-actions`},hd=[`disabled`],gd={key:3,class:`transcribing-state`},_d=Iu(ir({__name:`AudioRecorder`,props:{taskId:{}},emits:[`transcribed`,`error`],setup(e,{emit:t}){let n=e,r=t,{isRecording:i,duration:a,audioBlob:o,error:s,isSupported:c,getFormat:l,startRecording:u,stopRecording:d,resetRecording:f}=nd(),p=F(!1),m=F(null),h=Ia(()=>m.value||s.value);Ln(s,e=>{e&&r(`error`,e)});let g=Ia(()=>{let e=a.value,t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=e%60,i=e=>e.toString().padStart(2,`0`);return t>0?`${i(t)}:${i(n)}:${i(r)}`:`${i(n)}:${i(r)}`});async function _(){m.value=null,await u()}function v(){d()}function y(){f(),m.value=null}async function b(){if(o.value){p.value=!0,m.value=null;try{let e=await o.value.arrayBuffer(),t=new Uint8Array(e),i=``;for(let e=0;e(V(),H(`div`,rd,[h.value?(V(),H(`div`,id,A(h.value),1)):G(``,!0),Zt(c)?(V(),H(`div`,od,[!Zt(i)&&!Zt(o)&&!p.value?(V(),H(`div`,sd,[U(`button`,{class:`record-btn`,onClick:_},`🎤 开始录音`)])):Zt(i)?(V(),H(`div`,cd,[U(`div`,ld,[t[0]||=U(`span`,{class:`recording-indicator`},`● 录制中...`,-1),U(`span`,ud,A(g.value),1)]),U(`div`,dd,[(V(),H(z,null,Nr(12,e=>U(`div`,{key:e,class:`waveform-bar`,style:fe({animationDelay:`${e*.08}s`,height:`${20+Math.sin(Date.now()/200+e)*15}px`})},null,4)),64))]),U(`button`,{class:`stop-btn`,onClick:v},`⏹ 停止录音`)])):Zt(o)&&!p.value?(V(),H(`div`,fd,[U(`div`,pd,[U(`span`,null,`录音完成 (`+A(g.value)+`)`,1)]),U(`div`,md,[U(`button`,{class:`submit-btn`,disabled:p.value,onClick:b},A(p.value?`转写中...`:`提交转写`),9,hd),U(`button`,{class:`reset-btn`,onClick:y},`重新录制`)])])):G(``,!0),p.value?(V(),H(`div`,gd,[...t[1]||=[U(`span`,{class:`transcribing-text`},`转写中,请稍候...`,-1)]])):G(``,!0)])):(V(),H(`div`,ad,` 当前浏览器不支持录音,请使用文字输入 `))]))}}),[[`__scopeId`,`data-v-3824a835`]]),vd={class:`dialog`},yd={class:`dialog-header`},bd={key:0,class:`loading-state`},xd={key:1,class:`error-state`},Sd={class:`error-text`},Cd={key:2,class:`suggestions-body`},wd={class:`suggestions-list`},Td={class:`item-row`},Ed={class:`checkbox-label`},Dd=[`onUpdate:modelValue`],Od=[`onUpdate:modelValue`,`onBlur`,`onKeyup`],kd=[`onDblclick`],Ad=[`onUpdate:modelValue`],jd=[`onClick`],Md={key:0,class:`reason`},Nd={key:1,class:`reason manual-tag`},Pd={class:`dialog-footer`},Fd={class:`selected-count`},Id={class:`footer-actions`},Ld=[`disabled`],Rd=[`disabled`],zd=Iu(ir({__name:`MeetingBreakdownDialog`,props:{task:{},visible:{type:Boolean}},emits:[`close`,`created`],setup(e,{emit:t}){let n=e,r=t,i=F(!1),a=F(!1),o=F(``),s=F([]),c=F([]),l=Ia(()=>c.value.filter(e=>e.checked).length);async function u(){i.value=!0,o.value=``,s.value=[],c.value=[];try{let e=(await gu(n.task.id)).suggestions||[];if(e.length===0){o.value=`未从会议内容中提取到待办项,请确认纪要内容是否充分。`;return}s.value=e,c.value=e.map(e=>({title:e.title,priority:e.priority,checked:!0,reason:e.reason,isManual:!1,editing:!1}))}catch(e){o.value=`AI 拆分失败:${e?.message||String(e)}`}finally{i.value=!1}}function d(){c.value.push({title:`新待办项`,priority:1,checked:!0,reason:void 0,isManual:!0,editing:!0})}function f(e){c.value.splice(e,1)}function p(e){e.editing=!0}async function m(){let e=c.value.filter(e=>e.checked).map(e=>({title:e.title,priority:e.priority}));if(e.length!==0){a.value=!0;try{h(`已成功创建 ${(await _u(n.task.id,e)).createdCount??e.length} 个待办项`,`success`),r(`created`),r(`close`)}catch(e){h(`创建失败:${e?.message||String(e)}`,`error`)}finally{a.value=!1}}}function h(e,t=`error`){window.showToast?window.showToast(e,t):window.alert(e)}return Ln(()=>n.visible,e=>{e&&u()}),(t,n)=>e.visible?(V(),H(`div`,{key:0,class:`overlay`,onClick:n[2]||=rs(e=>r(`close`),[`self`])},[U(`div`,vd,[U(`div`,yd,[U(`h3`,null,`会议任务拆分 — `+A(e.task.title),1),U(`button`,{class:`close-button`,onClick:n[0]||=e=>r(`close`)},`✕`)]),i.value?(V(),H(`div`,bd,[...n[3]||=[U(`span`,{class:`spinner`},null,-1),U(`p`,null,`AI 正在分析会议内容,生成待办项建议...`,-1)]])):o.value?(V(),H(`div`,xd,[U(`p`,Sd,A(o.value),1),U(`button`,{class:`btn btn-secondary`,onClick:u},`🔄 重试`)])):(V(),H(`div`,Cd,[n[5]||=U(`p`,{class:`hint`},` AI 根据会议内容生成了以下待办项建议。请勾选需要创建的条目,可双击标题编辑。 `,-1),U(`div`,wd,[(V(!0),H(z,null,Nr(c.value,(e,t)=>(V(),H(`div`,{key:t,class:`suggestion-item`},[U(`div`,Td,[U(`label`,Ed,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":t=>e.checked=t},null,8,Dd),[[Yo,e.checked]]),e.editing?L((V(),H(`input`,{key:0,ref_for:!0,ref:`titleInputs`,"onUpdate:modelValue":t=>e.title=t,class:`title-input`,onBlur:t=>e.editing=!1,onKeyup:as(t=>e.editing=!1,[`enter`])},null,40,Od)),[[Jo,e.title]]):(V(),H(`span`,{key:1,class:`title-text`,onDblclick:t=>p(e)},A(e.title),41,kd))]),L(U(`select`,{"onUpdate:modelValue":t=>e.priority=t,class:`priority-select`},[...n[4]||=[U(`option`,{value:2},`🔴 高`,-1),U(`option`,{value:1},`🟡 中`,-1),U(`option`,{value:0},`🟢 低`,-1)]],8,Ad),[[Zo,e.priority]]),U(`button`,{class:`remove-btn`,onClick:e=>f(t),title:`移除此条目`},`✕`,8,jd)]),e.reason?(V(),H(`div`,Md,`📝 `+A(e.reason),1)):G(``,!0),e.isManual?(V(),H(`div`,Nd,`手动添加`)):G(``,!0)]))),128))]),U(`button`,{class:`add-btn`,onClick:d},`+ 手动添加待办项`)])),U(`div`,Pd,[U(`span`,Fd,`已选 `+A(l.value)+` 项`,1),U(`div`,Id,[U(`button`,{class:`btn btn-secondary`,onClick:n[1]||=e=>r(`close`),disabled:a.value},`取消`,8,Ld),U(`button`,{class:`btn btn-primary`,disabled:l.value===0||a.value,onClick:m},A(a.value?`创建中...`:`✅ 确认创建 (${l.value})`),9,Rd)])])])])):G(``,!0)}}),[[`__scopeId`,`data-v-fd688f75`]]),Bd={key:0,class:`meeting-badge`,title:`会议`},Vd=[`checked`],Hd=[`title`],Ud={class:`expand-icon`},Wd={class:`task-info`},Gd={class:`task-title`},Kd={key:0,class:`task-desc-preview`},qd={class:`task-meta`},Jd={class:`task-id task-id--compact`},Yd={class:`task-priority task-priority--compact`},Xd={key:0,class:`task-attach-badge`},Zd={class:`task-created`},Qd={key:1,class:`meeting-actions`},$d=[`title`],ef=[`disabled`],tf={key:2,class:`notes-editor`},nf={class:`notes-actions`},rf={key:0,class:`audio-info`},af=[`disabled`],of={key:4,class:`add-subtask-form`},sf={key:5,class:`subtasks`},cf=Iu(ir({__name:`TaskItem`,props:{task:{}},emits:[`updated`,`deleted`,`subtask-created`],setup(e,{emit:t}){let n=e,r=t,i=F(!1),a=F(!1),o=F(!1),s=F(!1),c=F(!1),l=F(!1),u=F(``),d=F(!1),f=F(``),p=F(1),m=F(null);Ln(c,e=>{e&&(u.value=n.task.meetingNotes||``)});let h=async()=>{a.value=!0,await yn(),m.value&&m.value.focus()},g=Ia(()=>{switch(n.task.priority){case 0:return`低`;case 2:return`高`;default:return`中`}}),_=Ia(()=>g.value),v=e=>{let t=new Date(e);if(!Number.isFinite(t.getTime()))return``;let n=e=>String(e).padStart(2,`0`);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`},y=Ia(()=>v(n.task.createdAt)),b=()=>{i.value=!i.value},x=async()=>{try{let e=await pu.toggleComplete(n.task.id);e.success&&e.data&&r(`updated`,e.data)}catch(e){console.error(`Failed to toggle task:`,e)}},S=async()=>{try{(await pu.deleteTask(n.task.id)).success&&r(`deleted`,n.task.id)}catch(e){console.error(`Failed to delete task:`,e)}},C=async()=>{if(!f.value.trim()){alert(`请输入子任务标题`);return}try{let e=await pu.createTask({title:f.value.trim(),priority:p.value,parentTaskId:n.task.id});e.success&&e.data&&(r(`subtask-created`,e.data),a.value=!1,f.value=``,p.value=1)}catch(e){console.error(`Failed to create subtask:`,e)}},w=e=>{if(n.task.subTasks){let t=n.task.subTasks.findIndex(t=>t.id===e.id);t!==-1&&(n.task.subTasks[t]=e,r(`updated`,n.task))}},T=e=>{n.task.subTasks&&(n.task.subTasks=n.task.subTasks.filter(t=>t.id!==e),r(`updated`,n.task))},E=()=>{o.value=!0};function ee(){s.value=!s.value}function D(e,t){u.value||=n.task.meetingNotes||``,e&&(u.value=u.value?u.value+` + +`+e:e),n.task.audioDuration=t,s.value=!1}function te(){l.value=!0}function O(){r(`updated`,n.task)}async function ne(){if(n.task){d.value=!0;try{await hu(n.task.id,u.value),n.task.meetingNotes=u.value,c.value=!1,ie(`纪要已保存`,`success`)}catch(e){ie(`保存纪要失败`,`error`),console.error(`saveNotes failed:`,e)}finally{d.value=!1}}}function re(e){let t=Math.floor(e/3600),n=Math.floor(e%3600/60),r=Math.floor(e%60);return t>0?`${t}小时${n}分${r}秒`:n>0?`${n}分${r}秒`:`${r}秒`}function ie(e,t=`error`){window.showToast&&window.showToast(e,t)}let ae=e=>{r(`updated`,e)};return(t,n)=>{let r=kr(`TaskItem`,!0);return V(),H(`div`,{class:_e([`task-item`,{completed:e.task.isCompleted,"priority-low":e.task.priority===0,"priority-medium":e.task.priority===1,"priority-high":e.task.priority===2}])},[e.task.taskType===1?(V(),H(`span`,Bd,`🎙`)):G(``,!0),U(`div`,{class:`task-content`,onDblclick:E},[U(`input`,{type:`checkbox`,checked:e.task.isCompleted,onChange:x,class:`task-checkbox`},null,40,Vd),e.task.subTasks&&e.task.subTasks.length>0?(V(),H(`button`,{key:0,class:`task-expand`,onClick:rs(b,[`stop`]),title:i.value?`收起`:`展开`},[U(`span`,Ud,A(i.value?`▼`:`▶`),1)],8,Hd)):G(``,!0),U(`button`,{onClick:h,class:`btn-add-subtask-icon`,title:`添加子任务`},[...n[8]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`},[U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z`})],-1)]]),U(`div`,Wd,[U(`span`,Gd,A(e.task.title),1),e.task.description?(V(),H(`span`,Kd,A(e.task.description),1)):G(``,!0),U(`div`,qd,[U(`span`,Jd,`#`+A(e.task.code||e.task.id),1),U(`span`,Yd,A(_.value),1),(e.task.attachmentCount??0)>0?(V(),H(`span`,Xd,`📎 `+A(e.task.attachmentCount),1)):G(``,!0),U(`span`,Zd,`创建`+A(y.value),1)])]),U(`div`,{class:`task-actions`},[U(`button`,{onClick:E,class:`btn-edit-icon`,title:`编辑`},[...n[9]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M12 20h9`}),U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5Z`})],-1)]]),U(`button`,{onClick:S,class:`btn-delete-icon`,title:`删除`},[...n[10]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{"stroke-linecap":`round`,"stroke-linejoin":`round`,d:`M6 18L18 6M6 6l12 12`})],-1)]])])],32),e.task.taskType===1?(V(),H(`div`,Qd,[U(`button`,{class:`btn-meeting`,onClick:rs(ee,[`stop`]),title:s.value?`关闭录音`:`录音`},A(s.value?`⏹`:`🎤`),9,$d),U(`button`,{class:`btn-meeting`,onClick:n[0]||=rs(e=>c.value=!c.value,[`stop`]),title:`纪要`},` 📝 `),U(`button`,{class:`btn-meeting`,onClick:rs(te,[`stop`]),title:`AI 拆分`,disabled:!e.task.meetingNotes},` 🤖 `,8,ef)])):G(``,!0),c.value&&e.task.taskType===1?(V(),H(`div`,tf,[L(U(`textarea`,{"onUpdate:modelValue":n[1]||=e=>u.value=e,class:`notes-textarea`,placeholder:`输入或粘贴会议纪要...`,rows:`4`},null,512),[[Jo,u.value]]),U(`div`,nf,[e.task.audioDuration?(V(),H(`span`,rf,`录音时长:`+A(re(e.task.audioDuration)),1)):G(``,!0),U(`button`,{class:`btn-save-notes`,onClick:ne,disabled:d.value},A(d.value?`保存中...`:`💾 保存纪要`),9,af)])])):G(``,!0),s.value&&e.task.taskType===1?(V(),ea(_d,{key:3,"task-id":e.task.id,onTranscribed:D},null,8,[`task-id`])):G(``,!0),a.value?(V(),H(`div`,of,[L(U(`input`,{ref_key:`subTaskInput`,ref:m,"onUpdate:modelValue":n[2]||=e=>f.value=e,type:`text`,placeholder:`输入子任务标题...`,onKeyup:as(C,[`enter`]),class:`subtask-input`},null,544),[[Jo,f.value]]),L(U(`select`,{"onUpdate:modelValue":n[3]||=e=>p.value=e,class:`subtask-select`},[...n[11]||=[U(`option`,{value:0},`低`,-1),U(`option`,{value:1},`中`,-1),U(`option`,{value:2},`高`,-1)]],512),[[Zo,p.value]]),U(`button`,{onClick:C,class:`btn-create-subtask`,title:`创建子任务`},[...n[12]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`},[U(`line`,{x1:`12`,y1:`5`,x2:`12`,y2:`19`}),U(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`})],-1)]]),U(`button`,{onClick:n[4]||=e=>a.value=!1,class:`btn-cancel`,title:`取消`},[...n[13]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M18 6L6 18M6 6l12 12`})],-1)]])])):G(``,!0),i.value&&e.task.subTasks&&e.task.subTasks.length>0?(V(),H(`div`,sf,[(V(!0),H(z,null,Nr(e.task.subTasks,e=>(V(),ea(r,{key:e.id,task:e,onUpdated:w,onDeleted:T,onSubtaskCreated:n[5]||=e=>t.$emit(`subtask-created`,e)},null,8,[`task`]))),128))])):G(``,!0),W(td,{visible:o.value,"onUpdate:visible":n[6]||=e=>o.value=e,task:e.task,onSaved:ae},null,8,[`visible`,`task`]),W(zd,{visible:l.value,task:e.task,onClose:n[7]||=e=>l.value=!1,onCreated:O},null,8,[`visible`,`task`])],2)}}}),[[`__scopeId`,`data-v-4cce1be2`]]),lf=(e,t=`error`)=>{window.showToast?window.showToast(e,t):window.alert(e)},uf=0,df=()=>{let e=Date.now();e-uf<1500||(uf=e,window.dispatchEvent(new CustomEvent(`openCloudSyncSettings`)))},ff=X.create({timeout:1e4,headers:{"Content-Type":`application/json`}});ff.interceptors.request.use(e=>{let t=Z.loadSession();return t?.accessToken&&(e.headers=e.headers??{},e.headers.Authorization=`Bearer ${t.accessToken}`),e}),ff.interceptors.response.use(e=>e,e=>{let t=`网络错误,请稍后再试`,n=e?.response?.status,r=e?.response?.data,i=r?.code??r?.Code;if(n===403&&i===`FORBIDDEN`)return lf(`云同步权限不足:请确认安全策略中已启用 AllowSync`,`error`),Promise.reject(e);if(n===401||n===403)return n===401?(Z.clearSession(),df(),lf(`云同步会话已过期,请重新登录`,`error`)):lf(`云同步请求被拒绝(${i||n})`,`error`),Promise.reject(e);if(e?.response?.data){let n=e.response.data;if(typeof n==`string`&&n.trim())t=n;else if(n&&typeof n==`object`){let e=n.message??n.Message,r=n.errors??n.Errors;Array.isArray(r)&&r.length>0?t=r.join(` +`):typeof e==`string`&&e.trim()&&(t=e)}}else typeof e?.message==`string`&&e.message.trim()&&(t=e.message);return lf(t,`error`),Promise.reject(e)});function pf(e){return!!(e&&typeof e==`object`&&`success`in e&&`data`in e)}function mf(e){if(pf(e)){if(!e.success)throw Error(e.errors?.join(` +`)||e.message||`云同步请求失败`);return e.data}return e}var hf=e=>{let t=new Map;for(let n of e)t.set(n.id,{id:n.id,title:n.title??``,priority:n.priority??1,isCompleted:!!n.isCompleted,code:n.code??``,parentTaskId:n.parentTaskId??void 0,createdAt:n.creationTime,updatedAt:n.lastModificationTime??n.creationTime,creationTime:n.creationTime,creatorId:n.creatorId,lastModificationTime:n.lastModificationTime,lastModifierId:n.lastModifierId,isDeleted:n.isDeleted,deletionTime:n.deletionTime,deleterId:n.deleterId,subTasks:[]});let n=[];for(let e of t.values()){let r=e.parentTaskId;r!=null&&t.has(r)?t.get(r).subTasks.push(e):n.push(e)}return n},gf=e=>{let t=[],n=new Set,r=e=>{for(let i of e){let e=_f(i.id)?i.id:null;e!=null&&n.has(e)||(e!=null&&n.add(e),t.push({id:e,title:i.title,priority:i.priority,isCompleted:!!i.isCompleted,code:i.code??``,parentTaskId:_f(i.parentTaskId??``)?i.parentTaskId:null,lastModificationTime:i.lastModificationTime||new Date().toISOString()}),i.subTasks&&i.subTasks.length>0&&r(i.subTasks))}};return r(e),t};function _f(e){return/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e)}var vf={async login(e){let t=mf((await ff.post(`/api/auth/login`,e)).data);if(!t)throw Error(`登录失败:服务端返回空响应`);return Z.saveSession({accessToken:t.accessToken,expiresAtUtc:t.expiresAtUtc,userId:t.userId,role:t.role,permissions:Array.isArray(t.permissions)?t.permissions:[]}),t},async logout(){try{await ff.post(`/api/auth/logout`)}catch{}Z.clearSession()},async getTasks(){let e=mf((await ff.get(`/api/tasks/`)).data);return hf(Array.isArray(e)?e:[])},async syncTasks(e,t=[]){let n={upserts:gf(e),deletes:t},r=mf((await ff.post(`/api/tasks`,n)).data);return{tasks:hf(Array.isArray(r?.tasks)?r.tasks:[]),serverTimeUtc:r?.serverTimeUtc}},async getSecurityPolicy(){return mf((await ff.get(`/api/security/policy`)).data)},async probeServerUrl(e){return mf((await ff.post(`/api/cloud-sync/probe`,{targetUrl:e})).data)}},yf=Rt({session:null,policy:{allowPersist:!0,allowSync:!0}}),bf={async executeCommand(e){return(await Q.post(`/voice/command`,{text:e})).data},async confirmCommand(e,t,n){return(await Q.post(`/voice/command/confirm`,{intent:e,targetId:t,newTitle:n})).data},async getBreakdown(e){return(await Q.post(`/voice/ai-breakdown`,{taskId:e})).data},async confirmBreakdown(e,t){return{success:!0,data:(await Q.post(`/voice/ai-breakdown/confirm`,{parentTaskId:e,subTasks:t})).data,message:`创建成功`}}};function xf(){let e=F(!1),t=F(``),n=F(``),r=F(!1),i=F(null),a=Ia(()=>!!window.SpeechRecognition||!!window.webkitSpeechRecognition),o=null;async function s(s){if(!a.value){n.value=`当前浏览器不支持语音识别`;return}if(!e.value){o=new(window.SpeechRecognition||window.webkitSpeechRecognition),o.lang=`zh-CN`,o.interimResults=!1,o.maxAlternatives=1,o.continuous=!1,o.onresult=async e=>{let a=e.results[0][0].transcript;if(t.value=a,a.trim()){r.value=!0,n.value=``;try{let e=await bf.executeCommand(a);i.value=e,s?.(e),u(e.result.message)}catch(e){n.value=e?.message||`语音指令执行失败`,u(`指令执行失败`)}finally{r.value=!1}}},o.onerror=t=>{n.value=`语音识别错误: ${t.error}`,e.value=!1},o.onend=()=>{e.value=!1};try{await o.start(),e.value=!0}catch(t){n.value=t?.message||`启动语音识别失败`,e.value=!1}}}function c(){o&&o.stop(),e.value=!1}async function l(e,t,a){r.value=!0,n.value=``;try{let n=await bf.confirmCommand(e,t,a);return i.value=n,u(n.result.message),n}catch(e){return n.value=e?.message||`确认执行失败`,null}finally{r.value=!1}}function u(e){if(!(!e||!(`speechSynthesis`in window)))try{let t=new SpeechSynthesisUtterance(e);t.lang=`zh-CN`,t.rate=1,window.speechSynthesis.speak(t)}catch{}}return{isListening:e,isExecuting:r,recognizedText:t,error:n,lastResult:i,isSupported:a,startListening:s,stopListening:c,confirmAmbiguity:l}}var Sf={class:`task-list`},Cf={class:`page-header`},wf={class:`header-controls`},Tf={class:`sort-group`},Ef=[`title`],Df={key:0,class:`sort-order-icon`,viewBox:`0 0 24 24`,fill:`currentColor`},Of={key:1,class:`sort-order-icon`,viewBox:`0 0 24 24`,fill:`currentColor`},kf={class:`checkbox-wrapper`},Af=[`title`],jf={key:0,width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},Mf={key:1,class:`voice-pulse`},Nf={class:`task-info`},Pf={class:`info-item`},Ff={key:0,class:`info-item info-memory`},If={key:1,class:`info-item`},Lf=[`title`],Rf={class:`info-item info-user`},zf=[`disabled`,`title`],Bf={key:0,class:`voice-indicator`},Vf={key:0,class:`voice-dot`},Hf={class:`voice-text`},Uf={key:1,class:`voice-error`},Wf={key:2,class:`voice-candidates`},Gf=[`onClick`],Kf={class:`task-form`},qf={key:0,class:`empty`},Jf={key:1,class:`tasks-shell`},Yf={class:`tasks-scroll`},Xf=Iu(ir({__name:`TaskList`,setup(e){let t=F(``),n=F(1),r=F(0),i=F([]),a=F(!1),o=F(`createdAt`),s=F(`desc`),c=F($.isOnline()),l=F(0),u=F(0),d=Ia(()=>$.getAllowPersist()&&yf.policy.allowPersist),f=F(!1),p=F(``),m=F(``),h=Ia(()=>{let e=m.value;if(!e)return``;try{return new URL(e).hostname}catch{return e.length>20?e.slice(0,20)+`...`:e}}),g=F(!1);function _(){let e=Z.loadSession();f.value=!!e?.accessToken,e?(p.value=e.role===`admin`?`👑 ${e.userId?.slice(0,8)||``}`:`👤 ${e.userId?.slice(0,8)||``}`,yf.session=e):(p.value=``,yf.session=null),m.value=Z.loadSettings().serverUrl}async function v(){g.value||await ge()}async function y(){try{await vf.logout(),Z.clearSession(),_(),yf.policy={allowPersist:!0,allowSync:!0},$.setAllowPersist(!0),b(`已登出云同步`,`success`)}catch(e){console.error(`Logout failed:`,e),b(`登出失败`,`error`)}}function b(e,t=`error`){window.showToast&&window.showToast(e,t)}let x=()=>{s.value=s.value===`asc`?`desc`:`asc`},S=()=>{c.value=$.isOnline()},C=e=>{let t=new Date(e),n=new Date().getTime()-t.getTime(),r=Math.floor(n/6e4);return r<1?`刚刚`:r<60?`${r}分钟前`:r<1440?`${Math.floor(r/60)}小时前`:`${Math.floor(r/1440)}天前`},w=Ia(()=>{let e=i.value.filter(e=>!e.parentTaskId);return a.value?T(e):T(E(e,!1))}),T=e=>{let t={2:3,1:2,0:1};return[...e].sort((e,n)=>{let r=0;switch(o.value){case`priority`:r=t[n.priority]-t[e.priority],r===0&&(r=new Date(n.createdAt).getTime()-new Date(e.createdAt).getTime());break;case`completedAt`:{let t=e.isCompleted?new Date(e.updatedAt).getTime():0;r=(n.isCompleted?new Date(n.updatedAt).getTime():0)-t,r===0&&(r=new Date(n.createdAt).getTime()-new Date(e.createdAt).getTime());break}default:r=new Date(n.createdAt).getTime()-new Date(e.createdAt).getTime();break}return s.value===`asc`?-r:r}).map(e=>e.subTasks&&e.subTasks.length>0?{...e,subTasks:T(e.subTasks)}:e)},E=(e,t)=>e.map(e=>({...e})).filter(e=>{if(e.isCompleted===t)return e.subTasks&&e.subTasks.length>0&&(e.subTasks=E(e.subTasks,t)),!0;if(e.subTasks&&e.subTasks.length>0){let n=E(e.subTasks,t);if(n.length>0)return e.subTasks=n,!0}return!1}),ee=async()=>{try{let e=await pu.getTasks();if(e.success&&e.data){i.value=e.data;let t=$.loadSyncStatus();l.value=t.lastSyncTime,u.value=t.pendingChanges}}catch(e){console.error(`Failed to load tasks:`,e),alert(`加载任务失败`)}},D=async()=>{if(!t.value.trim()){alert(`请输入任务标题`);return}try{let e=await pu.createTask({title:t.value.trim(),priority:n.value,taskType:r.value});e.success&&e.data&&(i.value.unshift(e.data),t.value=``,n.value=1,r.value=0)}catch(e){console.error(`Failed to create task:`,e),alert(`创建任务失败`)}},te=e=>{O(i.value,e)},O=(e,t)=>{for(let n=0;n0&&O(e[n].subTasks,t))return!0}return!1},ne=e=>{re(i.value,e)},re=(e,t)=>{for(let n=0;n0&&re(e[n].subTasks,t))return!0}return!1},ie=e=>{ae(i.value,e)},ae=(e,t)=>{for(let n=0;n0&&ae(e[n].subTasks,t))return!0}return!1},k=()=>{let e=new CustomEvent(`openHotKeySettings`,{detail:{modifiers:`Alt`,key:`X`,isEnabled:!0}});window.dispatchEvent(e)},oe=e=>{if(!d.value&&i.value.length>0)return e.preventDefault(),e.returnValue=`数据未同步且本地禁止落盘,退出将导致数据丢失。确定退出吗?`,e.returnValue},se=()=>{window.dispatchEvent(new CustomEvent(`openCloudSyncSettings`))},ce=xf(),le=F(!1),ue=F(``),de=F(``),fe=F([]),pe=``;async function me(){if(le.value){ce.stopListening(),le.value=!1;return}le.value=!0,de.value=``,ue.value=``,fe.value=[],await ce.startListening(e=>{le.value=!1;let t=e.result;t.ambiguity&&t.candidates?.length>0?(ue.value=t.message||`找到多个匹配项`,fe.value=t.candidates,pe=e.intent):t.success?(ue.value=t.message,ee(),setTimeout(()=>{ue.value=``},3e3)):ue.value=t.message||`指令未能识别`,ce.error.value&&(de.value=ce.error.value)})}async function he(e,t){if(!pe)return;let n=await ce.confirmAmbiguity(pe,e);n?.result.success?(ue.value=n.result.message,ee()):ue.value=n?.result.message||`执行失败`,fe.value=[],pe=``,setTimeout(()=>{ue.value=``},3e3)}let ge=async()=>{if(!Z.loadSession()?.accessToken){b(`请先登录云同步`,`error`);return}g.value=!0;try{let{tasks:e}=await vf.syncTasks(i.value,[]);i.value=e,l.value=Date.now(),u.value=0,$.saveSyncStatus({lastSyncTime:l.value,isOnline:!0,pendingChanges:0}),b(`云同步完成`,`success`)}catch(e){console.error(`Cloud sync failed:`,e),b(`同步失败:${e?.response?.data?.message||e?.message||`同步失败`}`,`error`)}finally{g.value=!1}},ve=e=>{let t=e.detail;_(),t?.enabled&&t?.isLoggedIn&&ye()},ye=async()=>{try{i.value=await vf.getTasks(),l.value=Date.now(),$.saveSyncStatus({lastSyncTime:l.value,isOnline:!0,pendingChanges:0})}catch(e){console.error(`Pull cloud tasks failed:`,e)}};return yr(()=>{ee(),S(),_(),window.addEventListener(`online`,S),window.addEventListener(`offline`,S),window.addEventListener(`cloudSyncTasksRequested`,ge),window.addEventListener(`cloudSyncStateChanged`,ve),window.addEventListener(`beforeunload`,oe)}),Cr(()=>{window.removeEventListener(`online`,S),window.removeEventListener(`offline`,S),window.removeEventListener(`cloudSyncTasksRequested`,ge),window.removeEventListener(`cloudSyncStateChanged`,ve),window.removeEventListener(`beforeunload`,oe)}),(e,i)=>(V(),H(`div`,Sf,[U(`div`,Cf,[U(`div`,wf,[U(`div`,Tf,[L(U(`select`,{"onUpdate:modelValue":i[0]||=e=>o.value=e,class:`sort-select`},[...i[5]||=[U(`option`,{value:`createdAt`},`创建时间`,-1),U(`option`,{value:`completedAt`},`完成时间`,-1),U(`option`,{value:`priority`},`优先级`,-1)]],512),[[Zo,o.value]]),U(`button`,{type:`button`,class:`sort-select sort-order-btn`,onClick:x,title:s.value===`desc`?`切换为升序`:`切换为降序`},[s.value===`desc`?(V(),H(`svg`,Df,[...i[6]||=[U(`path`,{d:`M11 4h2v11h3l-4 4-4-4h3V4z`},null,-1)]])):(V(),H(`svg`,Of,[...i[7]||=[U(`path`,{d:`M11 20h2V9h3l-4-4-4 4h3v11z`},null,-1)]]))],8,Ef),U(`label`,kf,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":i[1]||=e=>a.value=e,class:`checkbox-input`},null,512),[[Yo,a.value]]),i[8]||=U(`span`,{class:`checkbox-label`},`已完成`,-1)]),U(`button`,{class:`hotkey-settings-btn`,onClick:k,title:`设置快捷键`},[...i[9]||=[U(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`},[U(`path`,{d:`M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3z`})],-1),U(`span`,null,`快捷键`,-1)]]),U(`button`,{class:`hotkey-settings-btn voice-btn`,onClick:me,title:le.value?`停止语音`:`语音输入`},[le.value?(V(),H(`span`,Mf,`⏺`)):(V(),H(`svg`,jf,[...i[10]||=[U(`path`,{d:`M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z`},null,-1),U(`path`,{d:`M19 10v2a7 7 0 0 1-14 0v-2`},null,-1),U(`line`,{x1:`12`,y1:`19`,x2:`12`,y2:`23`},null,-1),U(`line`,{x1:`8`,y1:`23`,x2:`16`,y2:`23`},null,-1)]]))],8,Af)])]),U(`div`,Nf,[U(`span`,Pf,`计数:`+A(w.value.length),1),d.value?G(``,!0):(V(),H(`span`,Ff,`⚡ 内存模式`)),l.value>0?(V(),H(`span`,If,[U(`span`,null,`更新:`+A(C(l.value)),1)])):G(``,!0),f.value?(V(),H(z,{key:3},[m.value?(V(),H(`span`,{key:0,class:`info-item`,title:m.value},`☁ `+A(h.value),9,Lf)):G(``,!0),U(`span`,Rf,A(p.value),1),U(`button`,{class:_e([`info-item info-sync-btn`,{syncing:g.value}]),disabled:g.value,onClick:v,title:g.value?`同步中...`:`同步到云端`},A(g.value?`⏳`:`🔄`),11,zf),U(`button`,{class:`info-item info-settings-btn`,onClick:se,title:`云同步设置`},` ⚙ `),U(`button`,{class:`info-item info-logout-btn`,onClick:y,title:`登出云同步`},` 登出 `)],64)):(V(),H(`button`,{key:2,class:`info-item info-login-btn`,onClick:se,title:`登录云同步`},` 🔑 登录 `))]),le.value||ue.value?(V(),H(`div`,Bf,[le.value?(V(),H(`span`,Vf)):G(``,!0),U(`span`,Hf,A(le.value?`正在聆听...`:ue.value),1),de.value?(V(),H(`span`,Uf,A(de.value),1)):G(``,!0),fe.value.length>0?(V(),H(`div`,Wf,[i[11]||=U(`span`,{class:`candidates-hint`},`找到多个匹配,请选择:`,-1),(V(!0),H(z,null,Nr(fe.value,e=>(V(),H(`button`,{key:e.id,class:`candidate-btn`,onClick:t=>he(e.id,e.title)},A(e.title),9,Gf))),128))])):G(``,!0)])):G(``,!0)]),U(`div`,Kf,[L(U(`input`,{"onUpdate:modelValue":i[2]||=e=>t.value=e,type:`text`,placeholder:`输入新任务...`,onKeyup:as(D,[`enter`]),class:`task-input`},null,544),[[Jo,t.value]]),L(U(`select`,{"onUpdate:modelValue":i[3]||=e=>n.value=e,class:`task-select`},[...i[12]||=[U(`option`,{value:0},`低`,-1),U(`option`,{value:1},`中`,-1),U(`option`,{value:2},`高`,-1)]],512),[[Zo,n.value]]),L(U(`select`,{"onUpdate:modelValue":i[4]||=e=>r.value=e,class:`task-select`},[...i[13]||=[U(`option`,{value:0},`📋 普通`,-1),U(`option`,{value:1},`🎙 会议`,-1)]],512),[[Zo,r.value]]),U(`button`,{onClick:D,class:`btn-add`},[...i[14]||=[U(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`},[U(`line`,{x1:`12`,y1:`5`,x2:`12`,y2:`19`}),U(`line`,{x1:`5`,y1:`12`,x2:`19`,y2:`12`})],-1)]])]),w.value.length===0?(V(),H(`div`,qf,`暂无任务`)):(V(),H(`div`,Jf,[U(`div`,Yf,[(V(!0),H(z,null,Nr(w.value,e=>(V(),ea(cf,{key:e.id,task:e,onUpdated:te,onDeleted:ne,onSubtaskCreated:ie},null,8,[`task`]))),128))])]))]))}}),[[`__scopeId`,`data-v-0b9b025b`]]),Zf={class:`hotkey-settings-dialog`},Qf={class:`dialog-body`},$f={class:`form-group`},ep={class:`form-group`},tp=[`value`],np={class:`form-group`},rp={class:`checkbox-label`},ip={class:`preview`},ap={class:`hotkey-preview`},op=Iu(ir({__name:`HotKeySettingsDialog`,setup(e){let t=F(!1),n=Rt({modifiers:`Alt`,key:`X`,isEnabled:!0}),r=`ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`.split(``);yr(()=>{window.addEventListener(`openHotKeySettings`,i)});function i(e){let r=e.detail;n.modifiers=r.modifiers,n.key=r.key,n.isEnabled=r.isEnabled,t.value=!0}function a(){t.value=!1}function o(){return n.isEnabled?`${n.modifiers.split(`,`).map(e=>e.trim()).join(` + `)} + ${n.key}`:`未启用`}function s(){let e=new CustomEvent(`hotKeyConfigChanged`,{detail:{modifiers:n.modifiers,key:n.key,isEnabled:n.isEnabled}});window.dispatchEvent(e)}function c(){n.modifiers=`Alt`,n.key=`X`,n.isEnabled=!0,s()}function l(){let e={modifiers:n.modifiers,key:n.key,isEnabled:n.isEnabled};window.chrome?.webview?.postMessage&&window.chrome.webview.postMessage(`HOTKEY_CONFIG:${JSON.stringify(e)}`),a()}return(e,i)=>t.value?(V(),H(`div`,{key:0,class:`hotkey-settings-overlay`,onClick:rs(a,[`self`])},[U(`div`,Zf,[U(`div`,{class:`dialog-header`},[i[3]||=U(`h2`,null,`⌨️ 设置快捷键`,-1),U(`button`,{class:`close-button`,onClick:a},`×`)]),U(`div`,Qf,[U(`div`,$f,[i[5]||=U(`label`,null,`修饰键:`,-1),L(U(`select`,{"onUpdate:modelValue":i[0]||=e=>n.modifiers=e,onChange:s},[...i[4]||=[la(``,6)]],544),[[Zo,n.modifiers]])]),U(`div`,ep,[i[6]||=U(`label`,null,`主键:`,-1),L(U(`select`,{"onUpdate:modelValue":i[1]||=e=>n.key=e,onChange:s},[(V(),H(z,null,Nr(r,e=>U(`option`,{key:e,value:e},A(e),9,tp)),64))],544),[[Zo,n.key]])]),U(`div`,np,[U(`label`,rp,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":i[2]||=e=>n.isEnabled=e,onChange:s},null,544),[[Yo,n.isEnabled]]),i[7]||=U(`span`,null,`启用快捷键`,-1)])]),U(`div`,ip,[i[8]||=U(`strong`,null,`当前快捷键:`,-1),U(`span`,ap,A(o()),1)])]),U(`div`,{class:`dialog-footer`},[U(`button`,{class:`btn btn-secondary`,onClick:c},`重置默认`),U(`button`,{class:`btn btn-primary`,onClick:l},`保存`)])])])):G(``,!0)}}),[[`__scopeId`,`data-v-ae88d5d6`]]),sp={class:`dialog`},cp={class:`dialog-body`},lp={class:`section`},up={class:`checkbox-row`},dp={class:`section`},fp={class:`form-row`},pp=[`disabled`],mp={key:0,class:`hint`},hp={class:`mono`},gp={class:`probe-title`},_p={class:`probe-desc`},vp={class:`section`},yp={key:0,class:`logged-in`},bp={class:`hint`},xp={class:`mono`},Sp={class:`sync-status`},Cp={class:`sync-status-item`},wp={class:`sync-status-item`},Tp={class:`mono`},Ep={class:`sync-status-item`},Dp={key:0,class:`policy-info`},Op={class:`policy-item`},kp={class:`policy-item`},Ap={class:`button-row`},jp=[`disabled`],Mp={key:0},Np={key:1},Pp={key:1,class:`login-form`},Fp={class:`form-row`},Ip=[`disabled`],Lp=Iu(ir({__name:`CloudSyncSettingsDialog`,setup(e){let t=F(!1),n=F(!1),r=F(``),i=F(``),a=F(!1),o=F(null),s=F(``),c=F(``),l=F(!1),u=F(null),d=F({lastSyncTime:0,isOnline:!1,pendingChanges:0}),f=F(!1),p=Ia(()=>n.value&&v.value&&u.value?.allowSync!==!1),m=async()=>{b();try{let e=(await X.get(`/api/cloudSyncProxySettings`)).data?.data?.serverUrl??``;i.value=e,r.value=e||`http://localhost:5173`}catch{let e=Z.loadSettings();i.value=e.serverUrl,r.value=e.serverUrl||`http://localhost:5173`}n.value=Z.loadSettings().enabled!==!1,g(),v.value&&h()},h=async()=>{if(!Z.loadSession()?.accessToken)return u.value=null,yf.policy={allowPersist:!0,allowSync:!0},$.setAllowPersist(!0),!1;try{let e=await vf.getSecurityPolicy();return u.value=e,yf.policy=e,$.setAllowPersist(e.allowPersist),!0}catch(e){return X.isAxiosError(e)&&e.response?.status===401?(Z.clearSession(),b(),u.value=null,yf.policy={allowPersist:!0,allowSync:!0},$.setAllowPersist(!0),w(),!1):(console.error(`Failed to fetch security policy:`,e),!1)}},g=()=>{d.value=$.loadSyncStatus()},_=e=>{if(!e)return`从未同步`;let t=Date.now()-e;if(t<6e4)return`刚刚`;if(t<36e5)return`${Math.floor(t/6e4)} 分钟前`;if(t<864e5)return`${Math.floor(t/36e5)} 小时前`;let n=new Date(e),r=new Date;return n.toDateString()===r.toDateString()?`今天 ${n.toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`})}`:n.toLocaleString(`zh-CN`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`})},v=F(!1),y=F(``),b=()=>{let e=Z.loadSession();v.value=!!e?.accessToken,y.value=e?`${e.role||`User`}@${e.userId?e.userId.slice(0,8):``}`:``},x=Ia(()=>!n.value||!i.value||!s.value.trim()||!c.value?!1:!l.value),S=(e,t=`error`)=>{window.showToast?window.showToast(e,t):window.alert(e)},C=e=>{let t=e.trim();if(!t)return{ok:!1,message:`请输入服务端地址`};let n;try{n=new URL(t)}catch{return{ok:!1,message:`地址格式不正确,请包含协议(http:// 或 https://)`}}if(n.protocol!==`http:`&&n.protocol!==`https:`)return{ok:!1,message:`仅支持 http:// 或 https:// 地址`};if(!n.hostname)return{ok:!1,message:`地址缺少主机名(host)`};let r=n.origin,i=n.pathname&&n.pathname!==`/`?`已忽略地址中的路径部分,仅保留 origin。`:void 0;return{ok:!0,normalized:r.replace(/\/+$/,``),warn:i}},w=()=>{let e=Z.loadSettings(),t=Z.loadSession(),n=new CustomEvent(`cloudSyncStateChanged`,{detail:{enabled:e.enabled,serverUrl:i.value||e.serverUrl,isLoggedIn:!!t?.accessToken}});window.dispatchEvent(n)},T=()=>{if(n.value&&!i.value){n.value=!1,S(`启用云同步前请先配置并保存服务端地址`,`error`);return}Z.saveSettings({serverUrl:i.value,enabled:n.value}),w()},E=async()=>{let e=C(r.value);if(!e.ok||!e.normalized){S(e.message||`地址不合法`,`error`);return}i.value=e.normalized;try{await X.post(`/api/cloudSyncProxySettings`,{serverUrl:i.value})}catch{Z.saveSettings({serverUrl:i.value,enabled:n.value})}e.warn?S(e.warn,`success`):S(`地址已保存`,`success`),a.value=!0;try{let e=await vf.probeServerUrl(i.value);o.value={type:e.type,title:e.title,desc:e.description}}catch{o.value={type:`error`,title:`探测失败`,desc:`无法连接到服务端,请检查地址是否正确`}}finally{a.value=!1}w()},ee=async()=>{if(x.value){l.value=!0;try{if(await vf.login({userName:s.value.trim(),password:c.value}),b(),!await h()){S(`登录失败:无法获取安全策略,请检查服务端认证配置`,`error`);return}S(`登录成功`,`success`),w()}finally{l.value=!1}}},D=async()=>{await vf.logout(),b(),u.value=null,yf.policy={allowPersist:!0,allowSync:!0},$.setAllowPersist(!0),S(`已登出`,`success`),w()},te=async()=>{if(!(!p.value||f.value)){f.value=!0;try{g();let e=new CustomEvent(`cloudSyncTasksRequested`);window.dispatchEvent(e),setTimeout(()=>g(),500)}finally{setTimeout(()=>{f.value=!1},1500)}}};function O(){m(),o.value=null,v.value||(s.value||=`admin`,c.value||=`123456`),t.value=!0}function ne(){t.value=!1}return yr(()=>{window.addEventListener(`openCloudSyncSettings`,O),g(),v.value&&h()}),(e,m)=>t.value?(V(),H(`div`,{key:0,class:`overlay`,onClick:rs(ne,[`self`])},[U(`div`,sp,[U(`div`,{class:`dialog-header`},[m[4]||=U(`h2`,null,`☁️ 云同步设置`,-1),U(`button`,{class:`close-button`,onClick:ne},`×`)]),U(`div`,cp,[U(`div`,lp,[m[6]||=U(`div`,{class:`section-title`},`同步开关`,-1),U(`label`,up,[L(U(`input`,{type:`checkbox`,"onUpdate:modelValue":m[0]||=e=>n.value=e,onChange:T},null,544),[[Yo,n.value]]),m[5]||=U(`span`,null,`启用云同步(支持双向同步)`,-1)]),m[7]||=U(`div`,{class:`hint`},` 启用后,任务将与云端同步,支持双向同步功能。 `,-1)]),U(`div`,dp,[m[9]||=U(`div`,{class:`section-title`},`服务端地址`,-1),U(`div`,fp,[L(U(`input`,{"onUpdate:modelValue":m[1]||=e=>r.value=e,class:`text-input`,type:`text`,placeholder:`例如:https://example.com`,autocomplete:`off`},null,512),[[Jo,r.value]]),U(`button`,{class:`btn btn-secondary`,disabled:a.value,onClick:E},A(a.value?`探测中...`:`保存并探测`),9,pp)]),i.value?(V(),H(`div`,mp,[m[8]||=ca(` 已保存:`,-1),U(`span`,hp,A(i.value),1)])):G(``,!0),o.value?(V(),H(`div`,{key:1,class:_e([`probe`,o.value.type])},[U(`div`,gp,A(o.value.title),1),U(`div`,_p,A(o.value.desc),1)],2)):G(``,!0)]),U(`div`,vp,[m[17]||=U(`div`,{class:`section-title`},`登录`,-1),v.value?(V(),H(`div`,yp,[U(`div`,bp,[m[10]||=ca(` 已登录:`,-1),U(`span`,xp,A(y.value),1)]),U(`div`,Sp,[U(`div`,Cp,[m[11]||=U(`span`,{class:`label`},`连接状态:`,-1),U(`span`,{class:_e(d.value.isOnline?`success`:`offline`)},A(d.value.isOnline?`在线`:`离线`),3)]),U(`div`,wp,[m[12]||=U(`span`,{class:`label`},`上次同步:`,-1),U(`span`,Tp,A(d.value.lastSyncTime>0?_(d.value.lastSyncTime):`从未同步`),1)]),U(`div`,Ep,[m[13]||=U(`span`,{class:`label`},`待同步:`,-1),U(`span`,{class:_e(d.value.pendingChanges>0?`warn`:``)},A(d.value.pendingChanges)+` 项变更 `,3)])]),u.value?(V(),H(`div`,Dp,[U(`div`,Op,[m[14]||=U(`span`,{class:`label`},`落盘策略:`,-1),U(`span`,{class:_e(u.value.allowPersist?`success`:`warn`)},A(u.value.allowPersist?`允许(本地持久化)`:`禁止(仅限内存)`),3)]),U(`div`,kp,[m[15]||=U(`span`,{class:`label`},`同步权限:`,-1),U(`span`,{class:_e(u.value.allowSync?`success`:`warn`)},A(u.value.allowSync?`正常`:`已禁用`),3)])])):G(``,!0),U(`div`,Ap,[U(`button`,{class:`btn btn-secondary`,onClick:D},`登出`),U(`button`,{class:`btn btn-outline`,disabled:!p.value||f.value,onClick:te},[f.value?(V(),H(`span`,Mp,`同步中...`)):(V(),H(`span`,Np,`立即同步`))],8,jp)])])):(V(),H(`div`,Pp,[U(`div`,Fp,[L(U(`input`,{"onUpdate:modelValue":m[2]||=e=>s.value=e,class:`text-input`,type:`text`,placeholder:`用户名`,autocomplete:`username`},null,512),[[Jo,s.value]]),L(U(`input`,{"onUpdate:modelValue":m[3]||=e=>c.value=e,class:`text-input`,type:`password`,placeholder:`密码`,autocomplete:`current-password`},null,512),[[Jo,c.value]]),U(`button`,{class:`btn btn-primary`,disabled:!x.value,onClick:ee},A(l.value?`登录中...`:`登录`),9,Ip)]),m[16]||=U(`div`,{class:`hint`},` 说明:服务端使用 Bearer 会话(AccessToken=SessionId)。会话默认仅保存在本次运行(sessionStorage)。 `,-1)]))])]),U(`div`,{class:`dialog-footer`},[U(`button`,{class:`btn btn-secondary`,onClick:ne},`关闭`)])])])):G(``,!0)}}),[[`__scopeId`,`data-v-ab8b3536`]]),Rp={class:`app`},zp={class:`toast-content`},Bp={class:`toast-icon`},Vp={class:`toast-message`};ls(ir({__name:`App`,setup(e){let t=F(``),n=F(`error`),r=F(!1),i=(e,i=`error`)=>{t.value=e,n.value=i,r.value=!0,setTimeout(()=>{r.value=!1},5e3)};return yr(()=>{window.showToast=i}),(e,i)=>(V(),H(`div`,Rp,[W(Xf),W(op),W(Lp),W(Qa,{name:`toast`},{default:jn(()=>[r.value?(V(),H(`div`,{key:0,class:_e([`toast`,n.value])},[U(`div`,zp,[U(`span`,Bp,A(n.value===`error`?`❌`:`✅`),1),U(`span`,Vp,A(t.value),1)]),U(`button`,{onClick:i[0]||=e=>r.value=!1,class:`toast-close`},`×`)],2)):G(``,!0)]),_:1})]))}})).mount(`#app`); \ No newline at end of file diff --git a/src/Hua.Todo.Host/wwwroot/spa/favicon.svg b/src/Hua.Todo.Host/wwwroot/spa/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/src/Hua.Todo.Host/wwwroot/spa/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Hua.Todo.Host/wwwroot/spa/icons.svg b/src/Hua.Todo.Host/wwwroot/spa/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/src/Hua.Todo.Host/wwwroot/spa/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Hua.Todo.Host/wwwroot/spa/index.html b/src/Hua.Todo.Host/wwwroot/spa/index.html new file mode 100644 index 0000000..8eda88a --- /dev/null +++ b/src/Hua.Todo.Host/wwwroot/spa/index.html @@ -0,0 +1,14 @@ + + + + + + + Hua.Todo-web + + + + +
+ + diff --git a/src/Hua.Todo.HttpApi.AspNetCore/CloudSync/Services/CloudSyncServerService.cs b/src/Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs similarity index 85% rename from src/Hua.Todo.HttpApi.AspNetCore/CloudSync/Services/CloudSyncServerService.cs rename to src/Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs index f727c2d..f60b639 100644 --- a/src/Hua.Todo.HttpApi.AspNetCore/CloudSync/Services/CloudSyncServerService.cs +++ b/src/Hua.Todo.HttpApi.AspNetCore/CloudSync/CloudSyncServiceCollectionExtensions.cs @@ -7,16 +7,21 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; -namespace Hua.Todo.HttpApi.AspNetCore.CloudSync.Services; +namespace Hua.Todo.HttpApi.AspNetCore.CloudSync; /// -/// 云同步服务端配置(静态 DI 注册)。 +/// 云同步服务端依赖注入扩展方法。 +/// 与 、 +/// 同属 +/// HttpApi.AspNetCore 层的 DI/中间件扩展,Host 专用,MAUI/Avalonia 不调用。 /// -public static class CloudSyncServerService +public static class CloudSyncServiceCollectionExtensions { /// /// 注册云同步服务端能力:认证 / RBAC / 任务同步 / 安全策略 / 管理后台。 /// + /// 宿主 DI 容器。 + /// DI 容器(链式调用)。 public static IServiceCollection AddCloudSyncServer(this IServiceCollection services) { services.AddHttpContextAccessor(); @@ -61,4 +66,4 @@ public static class CloudSyncServerService return services; } -} \ No newline at end of file +} diff --git a/src/Hua.Todo.HttpApi.AspNetCore/DynamicApi/DynamicApiMiddleware.cs b/src/Hua.Todo.HttpApi.AspNetCore/DynamicApi/DynamicApiMiddleware.cs index 521609e..fef9b07 100644 --- a/src/Hua.Todo.HttpApi.AspNetCore/DynamicApi/DynamicApiMiddleware.cs +++ b/src/Hua.Todo.HttpApi.AspNetCore/DynamicApi/DynamicApiMiddleware.cs @@ -146,7 +146,12 @@ public class DynamicApiMiddleware { if (!(context.User.Identity?.IsAuthenticated ?? false)) { - await WriteErrorResponse(context, 401, "未登录或会话已过期"); + // Host 双角色统一部署:区分"浏览器页面跳转"和"API/Fetch 调用"两种 401 + // - Accept: text/html(用户在地址栏直接访问 /api/* 路径) → 302 跳转到 "/", + // Host 提供前端 SPA,SPA 会判断未登录 → 显示登录弹窗(CloudSyncSettingsDialog)。 + // 这样"没权限跳转到后端登录页"就等价于"跳回 Web 端根路径 → 前端接管登录 UI"。 + // - 其他情况(axios/fetch/swagger/mcp) → 返回 401 JSON 错误,前端拦截器自行处理。 + await WriteUnauthorizedResponse(context, message: "未登录或会话已过期"); return; } @@ -154,7 +159,8 @@ public class DynamicApiMiddleware { if (!context.User.HasPermission(perm)) { - await WriteErrorResponse(context, 403, "权限不足"); + // 403 同样区分:浏览器页面跳转时回到 "/"(前端判断后提示"权限不足") + await WriteForbiddenResponse(context, message: "权限不足"); return; } } @@ -668,7 +674,26 @@ public class DynamicApiMiddleware } else { - errors.Add($"数据库操作失败: {innerMessage}"); + // 提取受影响的实体及 FK 字段,帮助定位具体是哪个约束失败 + var entryDetails = new List(); + foreach (var entry in dbEx.Entries) + { + var entityName = entry.Entity.GetType().Name; + var fkValues = entry.Properties + .Where(p => p.Metadata.Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase) + && p.CurrentValue != null) + .Select(p => $"{p.Metadata.Name}={p.CurrentValue}") + .ToList(); + if (fkValues.Count > 0) + entryDetails.Add($"{entityName}({string.Join(", ", fkValues)})"); + else + entryDetails.Add(entityName); + } + + var detailSuffix = entryDetails.Count > 0 + ? $"(涉及:{string.Join("; ", entryDetails)})" + : ""; + errors.Add($"数据库操作失败: {innerMessage}{detailSuffix}"); } } else @@ -750,4 +775,64 @@ public class DynamicApiMiddleware await context.Response.WriteAsync(json); } + + /// + /// 401 未登录响应。 + /// 浏览器直接访问 /api/*(Accept: text/html)→ 302 跳回 Web 端根路径 /,由 SPA 显示登录弹窗; + /// 其他情况(axios/fetch/swagger/mcp) → 保持原有 JSON 错误。 + /// + private static async Task WriteUnauthorizedResponse(HttpContext context, string message) + { + if (IsBrowserHtmlRequest(context)) + { + context.Response.StatusCode = StatusCodes.Status302Found; + context.Response.Headers.Location = "/"; + await Task.CompletedTask; + return; + } + + await WriteErrorResponse(context, StatusCodes.Status401Unauthorized, message); + } + + /// + /// 403 权限不足响应。 + /// 规则与 401 相同:浏览器 HTML 请求 302 跳 /,其他返回 JSON。 + /// + private static async Task WriteForbiddenResponse(HttpContext context, string message) + { + if (IsBrowserHtmlRequest(context)) + { + context.Response.StatusCode = StatusCodes.Status302Found; + context.Response.Headers.Location = "/"; + await Task.CompletedTask; + return; + } + + await WriteErrorResponse(context, StatusCodes.Status403Forbidden, message); + } + + /// + /// 判断当前请求是否是"浏览器直接发起的页面导航" + /// (GET + Accept 优先包含 text/html,且不是 XHR/Fetch)。 + /// + private static bool IsBrowserHtmlRequest(HttpContext context) + { + if (!HttpMethods.IsGet(context.Request.Method)) + return false; + + var accept = context.Request.Headers["Accept"].ToString(); + if (string.IsNullOrEmpty(accept) || + !accept.Contains("text/html", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // Fetch/XHR/axios 通常带 X-Requested-With 或 无 Accept=text/html; + // 额外保险:不是 Ajax 请求 + var requestedWith = context.Request.Headers["X-Requested-With"].ToString(); + if ("XMLHttpRequest".Equals(requestedWith, StringComparison.OrdinalIgnoreCase)) + return false; + + return true; + } } diff --git a/src/Hua.Todo.Web/package-lock.json b/src/Hua.Todo.Web/package-lock.json index 27eca56..9bdb7a0 100644 --- a/src/Hua.Todo.Web/package-lock.json +++ b/src/Hua.Todo.Web/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "Hua.Todo-web", "version": "0.0.0", + "license": "AGPL-3.0", "dependencies": { "axios": "^1.13.6", "pinia": "^3.0.4", diff --git a/src/Hua.Todo.Web/src/api/client.ts b/src/Hua.Todo.Web/src/api/client.ts index 8176014..3537edf 100644 --- a/src/Hua.Todo.Web/src/api/client.ts +++ b/src/Hua.Todo.Web/src/api/client.ts @@ -1,4 +1,5 @@ import axios from 'axios'; +import CloudSyncStorage from '../services/cloudSyncStorage'; declare global { interface Window { @@ -16,6 +17,17 @@ const apiClient = axios.create({ }, }); +// Web Host 模式下注入 Bearer Token(与 cloudClient 共用 sessionStorage 同一会话) +// 嵌入式(MAUI/Avalonia)模式下 loadSession() 返回 null,此拦截器为无操作 +apiClient.interceptors.request.use((config) => { + const session = CloudSyncStorage.loadSession(); + if (session?.accessToken) { + config.headers = config.headers ?? {}; + (config.headers as any).Authorization = `Bearer ${session.accessToken}`; + } + return config; +}); + const showNotification = (message: string, type: 'error' | 'success' = 'error') => { if ((window as any).showToast) { (window as any).showToast(message, type); diff --git a/src/Hua.Todo.Web/vite.config.ts b/src/Hua.Todo.Web/vite.config.ts index e7636f4..13078cc 100644 --- a/src/Hua.Todo.Web/vite.config.ts +++ b/src/Hua.Todo.Web/vite.config.ts @@ -15,9 +15,11 @@ export default defineConfig({ port: 5174, proxy: { '/api': { + // 代理到 MAUI/Avalonia 嵌入式 WebServer(5057);用 VITE_API_TARGET 可覆盖 target: process.env.VITE_API_TARGET || 'http://localhost:5057', changeOrigin: true, secure: false, + followRedirects: true, }, }, }, diff --git a/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncAuthSecurityPolicyTests.cs b/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncAuthSecurityPolicyTests.cs index 6e88502..37069a2 100644 --- a/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncAuthSecurityPolicyTests.cs +++ b/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncAuthSecurityPolicyTests.cs @@ -6,7 +6,7 @@ using Hua.Todo.Application.Common; using Hua.Todo.Application.Repositories; using Hua.Todo.Application.Services.CloudSync.Models; using Hua.Todo.Core.Entities; -using Hua.Todo.HttpApi.AspNetCore.CloudSync.Services; +using Hua.Todo.HttpApi.AspNetCore.CloudSync; using Hua.Todo.HttpApi.AspNetCore.DynamicApi; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; diff --git a/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncEndpointRegistrationTests.cs b/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncEndpointRegistrationTests.cs index df460ca..75f8ba0 100644 --- a/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncEndpointRegistrationTests.cs +++ b/test/Hua.Todo.Host.Tests/CloudSync/CloudSyncEndpointRegistrationTests.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Hua.Todo.Application.Common; using Hua.Todo.Application.Repositories; using Hua.Todo.Application.Services.CloudSync.Services; -using Hua.Todo.HttpApi.AspNetCore.CloudSync.Services; +using Hua.Todo.HttpApi.AspNetCore.CloudSync; using Xunit; namespace Hua.Todo.Host.Tests.CloudSync; diff --git a/test/Hua.Todo.Host.Tests/CloudSync/HostBootstrapAndAuthRedirectTests.cs b/test/Hua.Todo.Host.Tests/CloudSync/HostBootstrapAndAuthRedirectTests.cs new file mode 100644 index 0000000..415172b --- /dev/null +++ b/test/Hua.Todo.Host.Tests/CloudSync/HostBootstrapAndAuthRedirectTests.cs @@ -0,0 +1,245 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using Hua.Todo.Application.Common; +using Hua.Todo.Application.Repositories; +using Hua.Todo.Application.Services.CloudSync.Models; +using Hua.Todo.HttpApi.AspNetCore.CloudSync; +using Hua.Todo.HttpApi.AspNetCore.DynamicApi; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Hua.Todo.Host.Tests.CloudSync; + +/// +/// 2026-08-08 用户反馈修复的 ATDD 验收测试: +/// 1. [根路径 404] 前端未构建(wwwroot/spa 无 index.html)时,访问 / 和 /settings 不 404, +/// 返回 MinimalBootstrapPageMiddleware 的最小引导页(含中文错误提示 + 构建命令) +/// 2. [无权限跳转登录] 浏览器 Accept: text/html 直访需要权限的 /api/*,DynamicApi 401/403 +/// 应 302 跳 "/"(Host 双角色统一部署:前端 SPA 加载后显示登录弹窗 CloudSyncSettingsDialog) +/// 3. [不破坏现有 API] Accept: application/json 的 axios/fetch 调用需要权限的 /api/*, +/// 返回原有的 401/403 JSON(不 302,否则前端拦截器无法判断) +/// +public sealed class HostBootstrapAndAuthRedirectTests : IDisposable +{ + private readonly SqliteConnection _connection; + private readonly string _baseDir; + private readonly string _wwwrootDir; + private readonly HttpClient _client; + private readonly WebApplication _app; + + public HostBootstrapAndAuthRedirectTests() + { + _baseDir = AppContext.BaseDirectory; + _wwwrootDir = Path.Combine(_baseDir, "wwwroot"); + Directory.CreateDirectory(_wwwrootDir); + + // 模拟"前端未构建"场景:删掉 wwwroot/spa/index.html(如果存在) + var idx = Path.Combine(_wwwrootDir, "spa", "index.html"); + if (File.Exists(idx)) File.Delete(idx); + + _connection = new SqliteConnection("Data Source=HostBootstrapAuthRedirectTests;Mode=Memory;Cache=Shared"); + _connection.Open(); + + _app = BuildHost(environmentName: "Development"); + _client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false }) + { + BaseAddress = new Uri(_app.Urls.First()), + }; + } + + private WebApplication BuildHost(string environmentName) + { + var builder = WebApplication.CreateBuilder(); + builder.Environment.EnvironmentName = environmentName; + builder.WebHost.UseUrls("http://127.0.0.1:0"); + + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + builder.Services.AddApplicationServices("Data Source=HostBootstrapAuthRedirectTests;Mode=Memory;Cache=Shared"); + builder.Services.AddDbContext(options => options.UseSqlite(_connection)); + builder.Services.AddCloudSyncServer(); + + var app = builder.Build(); + + using (var scope = app.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + } + + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + // 与 Program.cs 完全对齐:MinimalBootstrapPageMiddleware 放在 UseDefaultFiles/UseStaticFiles 之前 + // (注意:本测试类中 Program.cs 里的内部类型 MinimalBootstrapPageMiddleware 不可见, + // 这里用等价实现的内联委托替代,保持相同判断规则 + 相同 HTML 特征字) + app.Use(async (context, next) => + { + await next(); + + // 相同判断规则: + var indexHtmlPath = Path.Combine(app.Environment.WebRootPath, "spa", "index.html"); + if (File.Exists(indexHtmlPath)) return; + if (context.Response.StatusCode != StatusCodes.Status404NotFound) return; + var method = context.Request.Method; + if (!HttpMethods.IsGet(method) && !HttpMethods.IsHead(method)) return; + var path = context.Request.Path.Value ?? string.Empty; + if (path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("/swagger", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("/mcp", StringComparison.OrdinalIgnoreCase) || + path.StartsWith("/admin/", StringComparison.OrdinalIgnoreCase)) + return; + var accept = context.Request.Headers["Accept"].ToString(); + if (string.IsNullOrEmpty(accept) || + !accept.Contains("text/html", StringComparison.OrdinalIgnoreCase)) + return; + + context.Response.StatusCode = StatusCodes.Status200OK; + context.Response.ContentType = "text/html; charset=utf-8"; + // 特征字:用于测试断言,不需要完整样式 + await context.Response.WriteAsync( + "Hua.Todo - 前端尚未构建" + + "前端尚未构建" + + "dotnet build src/Hua.Todo.Host"); + }); + + app.UseDefaultFiles(); + app.UseStaticFiles(); + + // SPA 静态资源映射(与 Program.cs 对齐) + var spaRoot = Path.Combine(app.Environment.WebRootPath, "spa"); + if (Directory.Exists(spaRoot)) + { + app.UseDefaultFiles(new DefaultFilesOptions { FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(spaRoot) }); + app.UseStaticFiles(new StaticFileOptions { FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(spaRoot) }); + } + + app.UseAuthentication(); + app.UseAuthorization(); + app.UseDynamicApi(); + + app.MapFallbackToFile("spa/index.html"); + + app.StartAsync().GetAwaiter().GetResult(); + return app; + } + + [Fact] + public async Task Root_Get_NoIndexHtml_AcceptHtml_Returns200BootstrapPage_Not404() + { + // Given: wwwroot/spa/index.html 不存在(构造函数中已删除) + // When : 浏览器 GET / Accept: text/html + // Then : 200 + text/html + body 含「前端尚未构建」特征字 + using var req = new HttpRequestMessage(HttpMethod.Get, "/"); + req.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + using var response = await _client.SendAsync(req); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("text/html", + response.Content.Headers.ContentType?.MediaType ?? string.Empty, + StringComparison.OrdinalIgnoreCase); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("前端尚未构建", body, StringComparison.Ordinal); + Assert.Contains("dotnet build src/Hua.Todo.Host", body, StringComparison.Ordinal); + } + + [Fact] + public async Task SpaRouteSettings_Get_NoIndexHtml_AcceptHtml_Returns200BootstrapPage_Not404() + { + // Given: 同上 + // When : 浏览器 GET /settings Accept: text/html + // Then : 同上 200 + 引导页(SPA 路由不 404) + using var req = new HttpRequestMessage(HttpMethod.Get, "/settings"); + req.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + using var response = await _client.SendAsync(req); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("前端尚未构建", body, StringComparison.Ordinal); + } + + [Fact] + public async Task DynamicApi_RequirePermission_Get_BrowserAcceptHtml_401_Returns302RedirectToRoot() + { + // Given: 未登录 + 明确标注 [RequirePermission] 的 /api/security/policy + // (ISecurityPolicyService 配置:[DynamicApiRoute("security")] + [HttpGet("policy")] + // + [RequirePermission(CloudPermissions.PolicyRead)]) + // When : 浏览器 GET /api/security/policy Accept: text/html + // Then : 302 + Location = "/"(Host 提供前端 SPA,前端判断未登录 → CloudSyncSettingsDialog) + using var req = new HttpRequestMessage(HttpMethod.Get, "/api/security/policy"); + req.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,application/json;q=0.8,*/*;q=0.7"); + using var response = await _client.SendAsync(req); + + Assert.Equal(HttpStatusCode.Found, response.StatusCode); + Assert.Equal("/", response.Headers.Location?.OriginalString); + } + + [Fact] + public async Task DynamicApi_RequirePermission_Get_AcceptJson_401_Returns401Json_NotRedirect() + { + // Given: 同上,Accept: application/json(axios/fetch 模式) + // When : GET /api/security/policy Accept: application/json + // Then : 401 + application/json + body.success=false(不破坏现有前端拦截器逻辑) + using var req = new HttpRequestMessage(HttpMethod.Get, "/api/security/policy"); + req.Headers.Accept.ParseAdd("application/json, text/plain, */*"); + using var response = await _client.SendAsync(req); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.StartsWith("application/json", + response.Content.Headers.ContentType?.MediaType ?? string.Empty, + StringComparison.OrdinalIgnoreCase); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("\"success\":false", body, StringComparison.OrdinalIgnoreCase); + Assert.Contains("未登录", body, StringComparison.Ordinal); + } + + [Fact] + public async Task DynamicApi_RequirePermission_Put_BrowserAcceptHtml_401_Still401Json_NotRedirect() + { + // Given: 未登录 PUT /api/security/policy(RequirePermission 要求 PolicyWrite) + // Rule : IsBrowserHtmlRequest 只对 GET/HEAD 生效 → PUT/POST/PATCH/DELETE → 永远返回 JSON 401/403 + // When : PUT /api/security/policy,Accept: text/html(模拟不常见场景:浏览器 PUT 提交) + // Then : 401 JSON(不 302,因为不是 GET/HEAD) + using var req = new HttpRequestMessage(HttpMethod.Put, "/api/security/policy"); + req.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + req.Content = JsonContent.Create(new { AllowPersist = true, AllowSync = true }); + using var response = await _client.SendAsync(req); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.StartsWith("application/json", + response.Content.Headers.ContentType?.MediaType ?? string.Empty, + StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public async Task Api_Probe_Post_NoAuth_Anonymous_StillWorks_NotRedirectOr401() + { + // 回归:不影响已有的匿名 DynamicApi 端点(/probe 本来就 AllowAnonymous) + using var response = await _client.PostAsJsonAsync("/api/cloud-sync/probe", + new ProbeRequest { TargetUrl = "http://localhost:5173" }); + + Assert.NotEqual(HttpStatusCode.Unauthorized, response.StatusCode); + Assert.NotEqual(HttpStatusCode.Redirect, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("isReachable", body, StringComparison.OrdinalIgnoreCase); + } + + public void Dispose() + { + _client.Dispose(); + try { _app.StopAsync().GetAwaiter().GetResult(); } catch { /* 忽略 */ } + try { _app.DisposeAsync().GetAwaiter().GetResult(); } catch { /* 忽略 */ } + _connection.Dispose(); + } +} diff --git a/test/Hua.Todo.Host.Tests/CloudSync/HostHttpPipelineAnonymousEndpointTests.cs b/test/Hua.Todo.Host.Tests/CloudSync/HostHttpPipelineAnonymousEndpointTests.cs new file mode 100644 index 0000000..3c1b4b0 --- /dev/null +++ b/test/Hua.Todo.Host.Tests/CloudSync/HostHttpPipelineAnonymousEndpointTests.cs @@ -0,0 +1,155 @@ +using System.Net; +using System.Net.Http.Json; +using Hua.Todo.Application.Common; +using Hua.Todo.Application.Repositories; +using Hua.Todo.Application.Services.CloudSync.Models; +using Hua.Todo.HttpApi.AspNetCore.CloudSync; +using Hua.Todo.HttpApi.AspNetCore.DynamicApi; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Hua.Todo.Host.Tests.CloudSync; + +/// +/// Host HTTP 管道行为测试。 +/// 针对用户反馈的「前端云同步保存并探测报 Request failed with status code 307」。 +/// +/// 正确探测链路(嵌入式模式 MAUI / Avalonia): +/// 前端 POST /api/cloud-sync/probe {targetUrl} +/// → Vite proxy (target=http://localhost:5057) +/// → MAUI / Avalonia EmbeddedWebServer +/// → CloudSyncProxyMiddleware / CloudProbeService +/// → 后端 HttpClient 探测 {targetUrl}/api +/// 307 若出现,来自前端请求本身被某一层 HTTPS 重定向(Host 侧 UseHttpsRedirection), +/// 而非探测响应中的 HTTP 状态码。 +/// +/// 纯 Host 独立模式(VITE_API_TARGET 指向 Host)也必须保证匿名端点 HTTP 下不 307。 +/// 因此 Host Program.cs 在开发环境不启用 UseHttpsRedirection。 +/// +public sealed class HostHttpPipelineAnonymousEndpointTests : IDisposable +{ + private readonly SqliteConnection _connection; + private readonly HttpClient _clientDev; // 开发环境(修复后:不启用 UseHttpsRedirection) + private readonly HttpClient _clientProd; // 生产环境(显式启用 UseHttpsRedirection,仅回归验证) + private readonly WebApplication _appDev; + private readonly WebApplication _appProd; + + public HostHttpPipelineAnonymousEndpointTests() + { + _connection = new SqliteConnection("Data Source=HostHttpPipelineTests;Mode=Memory;Cache=Shared"); + _connection.Open(); + + _appDev = BuildHost(environmentName: "Development", enableHttpsRedirection: false); + _appProd = BuildHost(environmentName: "Production", enableHttpsRedirection: true); + + _clientDev = new HttpClient { BaseAddress = new Uri(_appDev.Urls.First()) }; + _clientProd = new HttpClient { BaseAddress = new Uri(_appProd.Urls.First()) }; + + // 自动跟随重定向会隐藏 307,手动关掉以捕获原始状态码 + _clientDev = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false }) + { BaseAddress = new Uri(_appDev.Urls.First()) }; + _clientProd = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false }) + { BaseAddress = new Uri(_appProd.Urls.First()) }; + } + + private WebApplication BuildHost(string environmentName, bool enableHttpsRedirection) + { + var builder = WebApplication.CreateBuilder(); + builder.Environment.EnvironmentName = environmentName; + builder.WebHost.UseUrls("http://127.0.0.1:0"); + + builder.Services.AddApplicationServices("Data Source=HostHttpPipelineTests;Mode=Memory;Cache=Shared"); + builder.Services.AddDbContext(options => options.UseSqlite(_connection)); + builder.Services.AddCloudSyncServer(); + + var app = builder.Build(); + using (var scope = app.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + } + + // 与修复后的 Program.cs 逻辑一致:仅非开发环境启用 HTTPS 重定向 + if (enableHttpsRedirection) + app.UseHttpsRedirection(); + + app.UseAuthentication(); + app.UseAuthorization(); + app.UseDynamicApi(); + app.StartAsync().GetAwaiter().GetResult(); + return app; + } + + // ===== 开发环境(修复后):匿名端点 HTTP 下不得重定向 ===== + + [Fact] + public async Task DevEnv_ProbeEndpoint_Http_NoRedirect() + { + var response = await _clientDev.PostAsJsonAsync("/api/cloud-sync/probe", + new ProbeRequest { TargetUrl = "http://localhost:5173" }); + + Assert.NotEqual(HttpStatusCode.TemporaryRedirect, response.StatusCode); + Assert.False(IsRedirect(response.StatusCode)); + } + + [Fact] + public async Task DevEnv_AuthLoginEndpoint_Http_NoRedirect() + { + var response = await _clientDev.PostAsJsonAsync("/api/auth/login", + new { userName = "admin", password = "x" }); + + Assert.NotEqual(HttpStatusCode.TemporaryRedirect, response.StatusCode); + Assert.False(IsRedirect(response.StatusCode)); + } + + [Fact] + public async Task DevEnv_AuthBootstrapEndpoint_Http_NoRedirect() + { + var response = await _clientDev.PostAsJsonAsync("/api/auth/bootstrap", + new { userName = "admin", password = "Admin@123" }); + + Assert.NotEqual(HttpStatusCode.TemporaryRedirect, response.StatusCode); + Assert.False(IsRedirect(response.StatusCode)); + } + + [Fact] + public async Task DevEnv_CloudSyncProxySettingsEndpoint_Http_NoRedirect() + { + var response = await _clientDev.GetAsync("/api/cloud-sync-proxy-settings"); + + Assert.NotEqual(HttpStatusCode.TemporaryRedirect, response.StatusCode); + Assert.False(IsRedirect(response.StatusCode)); + } + + // ===== 生产环境:启用 HTTPS 重定向,则 307(回归测试) ===== + + [Fact] + public async Task ProdEnv_ProbeEndpoint_Http_Returns307TemporaryRedirect() + { + var response = await _clientProd.PostAsJsonAsync("/api/cloud-sync/probe", + new ProbeRequest { TargetUrl = "http://localhost:5173" }); + + // 生产启用 HTTPS 重定向 → HTTP POST 应触发 307。部署时由反向代理处理,此测试仅保证行为可预测。 + Assert.Equal(HttpStatusCode.TemporaryRedirect, response.StatusCode); + } + + public void Dispose() + { + _clientDev.Dispose(); + _clientProd.Dispose(); + + foreach (var app in new[] { _appDev, _appProd }) + { + app.StopAsync().GetAwaiter().GetResult(); + app.DisposeAsync().GetAwaiter().GetResult(); + } + _connection.Dispose(); + } + + private static bool IsRedirect(HttpStatusCode code) => + (int)code is >= 300 and < 400; +} diff --git a/test/Hua.Todo.Host.Tests/CloudSync/WebRootStaticFileTests.cs b/test/Hua.Todo.Host.Tests/CloudSync/WebRootStaticFileTests.cs new file mode 100644 index 0000000..a1844cd --- /dev/null +++ b/test/Hua.Todo.Host.Tests/CloudSync/WebRootStaticFileTests.cs @@ -0,0 +1,189 @@ +using System.Net; +using System.Net.Http.Json; +using Hua.Todo.Application.Common; +using Hua.Todo.Application.Repositories; +using Hua.Todo.Application.Services.CloudSync.Models; +using Hua.Todo.HttpApi.AspNetCore.CloudSync; +using Hua.Todo.HttpApi.AspNetCore.DynamicApi; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Hua.Todo.Host.Tests.CloudSync; + +/// +/// 模式 B(Web 端)根路径托管 Hua.Todo.Web 静态页的验收测试。 +/// +/// 依据 v1.3.0 工单 06 的 6 条 Given-When-Then: +/// 1. 根路径 / 返回 200 + HTML(含 <div id="app">) +/// 2. SPA 内部路由 /settings fallback 到 index.html +/// 3. /api/* 动态 API 不被 fallback 覆盖 +/// 4. /swagger 不被 fallback 覆盖(中间件顺序保证) +/// +/// RED 阶段:当前 Program.cs 没有 UseDefaultFiles / UseStaticFiles / MapFallbackToFile, +/// 因此根路径断言会失败(404,body 非 HTML)。 +/// GREEN 阶段:按工单 06 §五 5.1 的 Program.cs 顺序补齐中间件后,全部通过。 +/// +public sealed class WebRootStaticFileTests : IDisposable +{ + private readonly SqliteConnection _connection; + private readonly string _webRootDir; + private readonly HttpClient _client; + private readonly WebApplication _app; + + /// + /// 写入到临时 wwwroot/index.html 的特征字符串,供断言用。 + /// 避免与实际 Hua.Todo.Web 构建耦合(测试不需要真的 npm build)。 + /// + private const string SpaMountPoint = "
SpaMount
"; + private const string SpaTitle = "Hua.Todo - ServedByHost"; + + public WebRootStaticFileTests() + { + // === 将临时 wwwroot/index.html 写入默认 WebRoot(无需改 WebRoot,避免 WebApplicationBuilder.WebHost.UseWebRoot 抛 NotSupportedException)=== + // WebApplicationBuilder 启动后默认 WebRoot = AppContext.BaseDirectory/wwwroot(即 bin/Debug/net10.0/wwwroot) + var baseDir = AppContext.BaseDirectory; + _webRootDir = Path.Combine(baseDir, "wwwroot"); + Directory.CreateDirectory(_webRootDir); + File.WriteAllText( + Path.Combine(_webRootDir, "index.html"), + $"{SpaTitle}{SpaMountPoint}"); + + // === 内存 SQLite(与既有集成测试同模式) === + _connection = new SqliteConnection("Data Source=WebRootStaticFileTests;Mode=Memory;Cache=Shared"); + _connection.Open(); + + _app = BuildHost(environmentName: "Development"); + _client = new HttpClient(new HttpClientHandler { AllowAutoRedirect = false }) + { + BaseAddress = new Uri(_app.Urls.First()), + }; + } + + private WebApplication BuildHost(string environmentName) + { + var builder = WebApplication.CreateBuilder(); + builder.Environment.EnvironmentName = environmentName; + builder.WebHost.UseUrls("http://127.0.0.1:0"); + + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + builder.Services.AddApplicationServices("Data Source=WebRootStaticFileTests;Mode=Memory;Cache=Shared"); + builder.Services.AddDbContext(options => options.UseSqlite(_connection)); + builder.Services.AddCloudSyncServer(); + + var app = builder.Build(); + + using (var scope = app.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.EnsureCreated(); + } + + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + // === GREEN:按 Program.cs 硬约束顺序启用静态文件中间件 + SPA fallback === + app.UseDefaultFiles(); + app.UseStaticFiles(); + + app.UseAuthentication(); + app.UseAuthorization(); + app.UseDynamicApi(); + + // SPA fallback(在 UseDynamicApi 之后,不覆盖 /api/*) + app.MapFallbackToFile("index.html"); + + app.StartAsync().GetAwaiter().GetResult(); + return app; + } + + [Fact] + public async Task Root_Get_ReturnsIndexHtml_WithAppMountDiv() + { + // Given: Host 启动且 WebRoot/wwwroot 有 index.html + // When : HTTP GET / + // Then : 200 + text/html + body 含
挂载点 + using var response = await _client.GetAsync("/"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.StartsWith("text/html", + response.Content.Headers.ContentType?.MediaType ?? string.Empty, + StringComparison.OrdinalIgnoreCase); + + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains(SpaMountPoint, body, StringComparison.Ordinal); + Assert.Contains(SpaTitle, body, StringComparison.Ordinal); + } + + [Fact] + public async Task SpaRoute_Settings_Get_FallsBackToIndexHtml_Not404() + { + // Given: 同上 + // When : HTTP GET /settings(前端内部路由) + // Then : 200 + body 是 index.html(注意:404 是 RED 时的现状,也是本测试在 RED 阶段失败的原因) + using var response = await _client.GetAsync("/settings"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains(SpaMountPoint, body, StringComparison.Ordinal); + } + + [Fact] + public async Task ApiDynamicApiRoute_PostProbe_NotOverriddenByFallback() + { + // Given: 同上 + // When : HTTP POST /api/cloud-sync/probe(匿名 DynamicApi 端点) + // Then : 返回 JSON(body 含 isReachable 字段),不是 index.html + // 不要求 isReachable=true,因为 test 里 Host 没监听 5173,必 false;也可能 503,但不是 HTML。 + using var response = await _client.PostAsJsonAsync("/api/cloud-sync/probe", + new ProbeRequest { TargetUrl = "http://localhost:5173" }); + + // 状态码不是 404 / 不返回 fallback 的 HTML: + Assert.NotEqual(HttpStatusCode.NotFound, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.DoesNotContain(SpaMountPoint, body, StringComparison.Ordinal); + + // 至少含 DynamicApi 的 JSON 特征:isReachable 字段 + Assert.Contains("isReachable", body, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public async Task SwaggerUi_Get_Returns200_NotOverriddenByFallback() + { + // Given: 开发环境,Swagger 启用 + // When : HTTP GET /swagger/index.html + // Then : 200,body 含 Swagger UI 的 "Swagger UI" 或 "swagger-ui",不是前端 index.html + using var response = await _client.GetAsync("/swagger/index.html"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var body = await response.Content.ReadAsStringAsync(); + Assert.Contains("swagger", body, StringComparison.OrdinalIgnoreCase); + Assert.DoesNotContain(SpaMountPoint, body, StringComparison.Ordinal); + } + + public void Dispose() + { + _client.Dispose(); + + try { _app.StopAsync().GetAwaiter().GetResult(); } catch { /* 忽略 */ } + try { _app.DisposeAsync().GetAwaiter().GetResult(); } catch { /* 忽略 */ } + + _connection.Dispose(); + // 清理临时写入的 index.html(只删我们自己写的,不删目录里的其他可能文件) + try + { + var idx = Path.Combine(_webRootDir, "index.html"); + if (File.Exists(idx)) File.Delete(idx); + } + catch { /* 忽略临时文件清理失败 */ } + } +} diff --git a/test/Hua.Todo.Host.Tests/DataSeeder/DataSeederTests.cs b/test/Hua.Todo.Host.Tests/DataSeeder/DataSeederTests.cs new file mode 100644 index 0000000..fb95903 --- /dev/null +++ b/test/Hua.Todo.Host.Tests/DataSeeder/DataSeederTests.cs @@ -0,0 +1,255 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Hua.Todo.Application.Repositories; +using Hua.Todo.Application.Services.DataSeeder; +using Hua.Todo.Core.Entities; +using Xunit; + +namespace Hua.Todo.Host.Tests.DataSeederTests; + +/// +/// 验收测试。 +/// 对应研发工单 v1.2.0 工单 10 的验收标准(Given-When-Then), +/// 详见 docs/project/研发工单-v1.2.0/10-数据库初始化默认管理员.md 第五章。 +/// +public sealed class DataSeederTests : IDisposable +{ + private readonly SqliteConnection _connection; + private readonly DbContextOptions _options; + private readonly IPasswordHasher _passwordHasher; + + public DataSeederTests() + { + _connection = new SqliteConnection("Data Source=:memory:"); + _connection.Open(); + + _options = new DbContextOptionsBuilder() + .UseSqlite(_connection) + .Options; + + _passwordHasher = new PasswordHasher(); + } + + public void Dispose() + { + _connection.Dispose(); + } + + /// + /// 创建并迁移数据库上下文(每个测试独立实例,避免数据互相污染)。 + /// + private TodoDbContext CreateContext() + { + var context = new TodoDbContext(_options); + context.Database.Migrate(); + return context; + } + + /// + /// 创建使用默认配置的 DataSeeder。 + /// + private static DataSeeder CreateSeeder(TodoDbContext db, IPasswordHasher hasher, AdminBootstrapOptions? options = null) + => new(db, hasher, options ?? new AdminBootstrapOptions()); + + // ========== 5.1 主流程 ========== + + /// + /// Given 全新空数据库(Migrate 完成,Users 表无任何云用户) + /// When 调用 IDataSeeder.SeedAsync() + /// Then Users 表新增一条记录,Role="admin",UserName 取配置值,MustChangePassword=true + /// And SecurityPolicies 表新增对应记录(AllowPersist=true, AllowSync=true) + /// And AuditLogs 表新增一条 EventType="SeedBootstrap" 记录 + /// + [Fact] + public async Task SeedAsync_EmptyDatabase_CreatesDefaultAdminWithPolicyAndAuditLog() + { + using var db = CreateContext(); + var options = new AdminBootstrapOptions { UserName = "admin", Password = "Admin@123", Enabled = true }; + var seeder = CreateSeeder(db, _passwordHasher, options); + + await seeder.SeedAsync(); + + var admin = await db.Users.SingleOrDefaultAsync(u => u.Role == "admin"); + Assert.NotNull(admin); + Assert.Equal("admin", admin!.UserName); + Assert.True(admin.MustChangePassword); + + var policy = await db.SecurityPolicies.SingleOrDefaultAsync(p => p.UserId == admin.Id); + Assert.NotNull(policy); + Assert.True(policy!.AllowPersist); + Assert.True(policy.AllowSync); + + var audit = await db.AuditLogs.SingleOrDefaultAsync(a => a.EventType == "SeedBootstrap"); + Assert.NotNull(audit); + Assert.True(audit!.IsSuccess); + } + + // ========== 5.2 幂等性 ========== + + /// + /// Given 数据库已存在 Role="admin" 的云用户 + /// When 调用 SeedAsync() + /// Then Users 表不新增记录 + /// And AuditLogs 表不新增 SeedBootstrap 记录 + /// + [Fact] + public async Task SeedAsync_ExistingCloudUser_DoesNotCreateDuplicate() + { + using var db = CreateContext(); + var existingAdmin = await SeedExistingAdminAsync(db, "admin"); + + var seeder = CreateSeeder(db, _passwordHasher); + await seeder.SeedAsync(); + + var admins = await db.Users.Where(u => u.Role == "admin").ToListAsync(); + Assert.Single(admins); + Assert.Equal(existingAdmin.Id, admins[0].Id); + + var seedAudits = await db.AuditLogs.Where(a => a.EventType == "SeedBootstrap").ToListAsync(); + Assert.Empty(seedAudits); + } + + // ========== 5.3 配置自定义账号 ========== + + /// + /// Given AdminBootstrapOptions.UserName="root" 且 Password="MyStrong#123" + /// When 调用 SeedAsync() + /// Then 创建的管理员 UserName="root" + /// And 使用该密码可通过 IPasswordHasher.VerifyHashedPassword 校验 + /// + [Fact] + public async Task SeedAsync_CustomOptions_UsesConfiguredCredentials() + { + using var db = CreateContext(); + var options = new AdminBootstrapOptions + { + Enabled = true, + UserName = "root", + Password = "MyStrong#123" + }; + var seeder = CreateSeeder(db, _passwordHasher, options); + + await seeder.SeedAsync(); + + var admin = await db.Users.SingleOrDefaultAsync(u => u.UserName == "root"); + Assert.NotNull(admin); + Assert.Equal("admin", admin!.Role); + + var verify = _passwordHasher.VerifyHashedPassword(admin, admin.PasswordHash, "MyStrong#123"); + Assert.Equal(PasswordVerificationResult.Success, verify); + } + + // ========== 5.4 配置禁用 ========== + + /// + /// Given AdminBootstrapOptions.Enabled=false + /// When 调用 SeedAsync() + /// Then 不执行任何种子逻辑,直接返回 + /// + [Fact] + public async Task SeedAsync_DisabledOption_DoesNothing() + { + using var db = CreateContext(); + var options = new AdminBootstrapOptions { Enabled = false }; + var seeder = CreateSeeder(db, _passwordHasher, options); + + await seeder.SeedAsync(); + + var userCount = await db.Users.CountAsync(); + Assert.Equal(0, userCount); + + var auditCount = await db.AuditLogs.CountAsync(); + Assert.Equal(0, auditCount); + } + + // ========== 5.5 密码哈希安全 ========== + + /// + /// Given 执行 SeedAsync() 成功 + /// When 查询 Users 表 + /// Then PasswordHash 不等于明文密码 + /// And PasswordHash 可通过 IPasswordHasher.VerifyHashedPassword(明文) 校验为 Success + /// + [Fact] + public async Task SeedAsync_PasswordHashed_NotStoredInPlaintext() + { + using var db = CreateContext(); + const string plainPassword = "Admin@123"; + var options = new AdminBootstrapOptions { Enabled = true, UserName = "admin", Password = plainPassword }; + var seeder = CreateSeeder(db, _passwordHasher, options); + + await seeder.SeedAsync(); + + var admin = await db.Users.SingleAsync(u => u.Role == "admin"); + + Assert.NotEqual(plainPassword, admin.PasswordHash); + Assert.NotEmpty(admin.PasswordHash); + + var verify = _passwordHasher.VerifyHashedPassword(admin, admin.PasswordHash, plainPassword); + Assert.Equal(PasswordVerificationResult.Success, verify); + } + + // ========== 5.6 既有 local 用户不影响初始化 ========== + + /// + /// Given Users 表仅存在 Role="local" 的本地用户(嵌入式遗留数据) + /// When 调用 SeedAsync() + /// Then 仍然创建默认管理员(因为幂等条件是 Role != "local" 的云用户) + /// + [Fact] + public async Task SeedAsync_OnlyLocalUserExists_StillCreatesAdmin() + { + using var db = CreateContext(); + await SeedLocalUserAsync(db, "local"); + + var seeder = CreateSeeder(db, _passwordHasher); + await seeder.SeedAsync(); + + var localUser = await db.Users.SingleAsync(u => u.Role == "local"); + var admin = await db.Users.SingleOrDefaultAsync(u => u.Role == "admin"); + + Assert.NotNull(admin); + Assert.NotEqual(localUser.Id, admin!.Id); + Assert.Equal(2, await db.Users.CountAsync()); + } + + // ========== 辅助方法 ========== + + /// + /// 写入一个已存在的云管理员(模拟"系统已初始化"场景)。 + /// + private async Task SeedExistingAdminAsync(TodoDbContext db, string userName) + { + var user = new UserEntity + { + Id = Guid.NewGuid(), + UserName = userName, + Role = "admin", + CreatedAtUtc = DateTime.UtcNow, + UpdatedAtUtc = DateTime.UtcNow + }; + user.PasswordHash = _passwordHasher.HashPassword(user, "Existing#123"); + db.Users.Add(user); + await db.SaveChangesAsync(); + return user; + } + + /// + /// 写入一个本地用户(Role="local",嵌入式模式遗留数据)。 + /// + private async Task SeedLocalUserAsync(TodoDbContext db, string userName) + { + var user = new UserEntity + { + Id = Guid.NewGuid(), + UserName = userName, + Role = "local", + CreatedAtUtc = DateTime.UtcNow, + UpdatedAtUtc = DateTime.UtcNow + }; + user.PasswordHash = _passwordHasher.HashPassword(user, "local-dummy"); + db.Users.Add(user); + await db.SaveChangesAsync(); + } +}