diff --git a/AGENTS.md b/AGENTS.md index d4cd43f..0b4ab92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,6 +17,7 @@ - 业务需求:`docs/需求拆解/` - **代销平台 API 契约:** `docs/项目框架设计/接口契约-代销平台API-v0.1.md`(canonical 路径 · 命名 · Agent 迁移表) +- **行情 Phase B(草案):** `docs/项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md` · `docs/项目框架设计/C-05-行情数据源选型对比.md` - 表结构 SQL:`docs/项目框架设计/表设计/` - 技术选型 / JWT:`docs/项目框架设计/技术选型和版本/` - Core 模拟底座:`docs/项目框架设计/Core模拟底座/` diff --git a/docs/memory/MEMORY.md b/docs/memory/MEMORY.md index d568720..5965e05 100644 --- a/docs/memory/MEMORY.md +++ b/docs/memory/MEMORY.md @@ -48,9 +48,11 @@ | `docs/项目框架设计/合并注意事项-风控模块并入main.md` | **AL-09 已执行(2026-09-08)** | 合并接线完成;接缝见《风控Agent模块边界与合并接缝标注.md》 | | `docs/项目管理/04-从需求到公共API开发方法.md` | **已定(2026-09-08)** | 需求→矩阵→存储→Service→API→Agent 主线;Phase A 清单 | | `docs/项目框架设计/接口契约-代销平台API-v0.1.md` | **已定(2026-09-08)** | 代销平台 REST canonical 路径、命名规范、Agent 迁移表 | +| `docs/项目框架设计/C-05-行情数据源选型对比.md` | **已定(2026-09-09)** | C-05 净值数据源分阶段选型;禁止爬虫/前端直连第三方 | +| `docs/项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md` | **草案(2026-09-09)** | `nav-snapshot` · `sync_market_nav` · 前端产品行情 Phase B | | `docs/项目框架设计/表设计/` | 已定 | Agent 共用 11 表 + agent 专用 SQL | | `docs/项目框架设计/Core模拟底座/` | 已定 | 无真实 Core 时的 L0 方案 | -| `web/` | **不存在** | 前端 React 待 init | +| `web/` | **P0 脚手架(2026-09-09)** | Vite+AntD+HashRouter;登录+Layout+路由占位;见 `web/README.md` | **本地 bootstrap(首次):** 完整步骤与前置说明见 `FLOW.md` §0(权威),速览: @@ -181,6 +183,8 @@ RBAC 联调账号:scripts/dev/rbac-seed-reference.md | `docs/项目框架设计/` | 表结构、JWT 手册、Core 模拟、技术版本 | | `docs/项目管理/04-从需求到公共API开发方法.md` | 搭公共底座/API 前;新人 onboarding | | `docs/项目框架设计/接口契约-代销平台API-v0.1.md` | 写/Review 代销平台 REST;Agent 合并对照 canonical 路径 | +| `docs/项目框架设计/C-05-行情数据源选型对比.md` | C-05 行情/净值数据源拍板与禁止项 | +| `docs/项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md` | Phase B `nav-snapshot` 与 sync 脚本契约(未实现) | 缺 `docs/memory/*` 文件:按 project-memory-kit 同名补回,**禁止空模板盖进度**。 diff --git a/docs/superpowers/specs/2026-09-09-frontend-p0-design.md b/docs/superpowers/specs/2026-09-09-frontend-p0-design.md new file mode 100644 index 0000000..3adc02b --- /dev/null +++ b/docs/superpowers/specs/2026-09-09-frontend-p0-design.md @@ -0,0 +1,416 @@ +# 前端 P0 设计 · 四角色工作台 + 平台壳 + 跨角色数据分析 Agent + +> 日期:2026-09-09 +> 状态:**待实现**(brainstorming 定稿) +> 技术栈:**React 19 + Vite 7 + TypeScript strict + Ant Design 5 + HashRouter + Vitest** +> 关联:`docs/memory/FRAMEWORK.md` · `docs/项目框架设计/接口契约-代销平台API-v0.1.md` · `02-JWT-RBAC鉴权手册.md` + +--- + +## 1. 目标与范围 + +### 1.1 P0 要交付什么 + +1. 初始化 `web/`,可 `npm run dev` 联调本机 FastAPI(`:8000`)。 +2. **四角色 Demo 工作台**(Ant Design ProLayout:侧栏 + 顶栏 + 内容区),平台读 API **尽量真接**。 +3. **Chat 真 SSE**:customer、risk 完整;advisor/analyst 共用组件,能力弱处加 Banner。 +4. **平台功能布局完善**:各角色菜单、按钮、空状态、403 页齐全;Agent 专属能力用占位/Mock,等队友接入。 +5. **产品行情(静态 · P0)**:共享页「净值一览」,真接 `GET /api/products` + `GET /api/products/{id}/nav`;页眉标明 **模拟静态净值,非实时行情**;动态行情 **Phase B** 接外部信息 API(见 §2.6)。 +6. **数据分析模块(新增拍板)**: + - **analyst 角色**:拥有完整「数据分析工作台」页面(问数 + Chat)。 + - **其他已登录角色**:也可进入 **数据分析 Agent**(Chat / 问数入口),但 **查表/明细级查询** 由后端鉴权;权限不足时 **403 + 审计**,前端展示统一错误,不静默降级。 + +### 1.2 不做什么(P0) + +- 不 Mock 整个平台 REST(避免与 v0.1 打架)。 +- 不做 simulate 交易写 UI(菜单可 Disabled + Tooltip)。 +- 不引入 shadcn(与 FRAMEWORK 已定 AntD 5 一致)。 +- 不在 P0 实现完整 NL2SQL Agent(分析页可先 Mock 表格 + 真 Chat 骨架)。 +- **不做** 实时看盘 / K 线 / 自选股 / 外部行情推送(留 Phase B)。 + +### 1.3 后端 Agent 接入现状(供前端预期管理) + +| Agent | 对话入口 | Tool / 业务能力 | +| --- | --- | --- | +| customer(客户财富) | ✅ `/api/chat` `/stream` | ✅ 持仓/流水/档案/KB;**无**独立客服产品 Agent | +| advisor(代理人) | ✅ | ✅ 同类 Core/KB Tool;业务 Agent 空壳 | +| analyst(数据分析) | ✅ 矩阵准入 | ❌ chat 内 **无** NL2SQL Tool(`match_intent` 不含 analyst) | +| risk(风控) | ✅ | ✅ 四只读风控 Tool + 事件线 | + +--- + +## 2. 信息架构与路由(§1 定稿 + 分析模块修订) + +### 2.1 顶层路由 + +```text +#/login +#/app # 登录后壳(ProLayout) + ├─ #/app/home # 角色差异化首页 + │ + ├─ #/app/market # 【共享】产品行情 · 静态净值一览(§2.6) + │ + ├─ #/app/analytics # 【共享】数据分析模块(见 §2.3) + │ ├─ .../query # 问数页(analyst 全量;其他角色受限/403) + │ └─ .../chat # 数据分析 Agent Chat(X-Agent-Type: analyst) + │ + ├─ #/app/customer/* # 客户专属(CUST 登录默认) + ├─ #/app/advisor/* # 理财师专属 + ├─ #/app/analyst/* # 分析员专属(可 redirect 到 /app/analytics) + └─ #/app/risk/* # 风控专属 +``` + +### 2.2 各角色侧栏(摘要) + +| 角色 | 主菜单 | 共享入口 | +| --- | --- | --- | +| customer | 首页 · 我的档案 · 持仓 · 流水 · 产品 · 适当性 · **客户 Chat** | **产品行情** · **数据分析** | +| advisor | 首页 · 名下客户 · 草稿(占位) · **顾问 Chat** | **产品行情** · **数据分析** | +| analyst | 首页 · **数据分析工作台**(= analytics 模块) | **产品行情** | +| risk_officer | 首页 · 预警台账 · AML(占位) · **风控 Chat** | **产品行情** · **数据分析** | + +侧栏中 **「数据分析」** 对所有已登录角色可见;analyst 登录时该项高亮且默认 landing 到 `/app/analytics/query`。 + +### 2.3 跨角色数据分析 Agent(用户拍板) + +**产品行为:** + +1. **入口**:任意角色侧栏均可进 `#/app/analytics/chat`;analyst 另有 `#/app/analytics/query` 完整问数页。 +2. **Chat**:统一 `X-Agent-Type: analyst`;会话与其他 Agent 线 **隔离**(同 actor 下 analyst 会话独立列表)。 +3. **问数页**: + - analyst:展示 SQL 输入/结果表/留痕说明(P0 结果区可 Mock,提交时若后端未就绪则 Banner)。 + - 其他角色:同一页面壳;发起 **明细级查表** 时若返回 `AUTH_403_SCOPE` / `AUTH_403_ROLE`,用 `ApiErrorResult` 展示手册错误码与 trace_id,**不**伪造数据。 +4. **权限提示(前端)**:进入 analytics 前读取 JWT `roles` / `permissions`(login 响应或 decode payload);无 `sql:execute:readonly` 时问数页 Primary 按钮 Disabled + Tooltip「需要数据分析权限」;仍可使用 Chat 做通用问答。 + +**与 JWT 手册对齐(意图):** + +| 角色 | 预期查数范围 | 前端提示 | +| --- | --- | --- | +| analyst | 目录内只读 SQL + 聚合 | 全功能 | +| compliance | 审计/聚合 | 明细按钮 Disabled 或 403 | +| advisor | 名下客户聚合(未来 Tool) | Banner + 403 友好文案 | +| risk_officer | 全量读预警相关;明细 SQL 可能 403 | 同上 | +| customer | 仅本人/聚合(未来) | 强提示「客户视图」 | + +### 2.4 后端接缝(实现前须知晓) + +当前 `AGENT_ACCESS_MATRIX`(`app/api/deps.py`)**仅允许** `analyst`、`compliance` 使用 `X-Agent-Type: analyst`。 +**advisor / risk_officer / customer** 现在调用 analyst Chat 会得到 **`AUTH_403_AGENT_MISMATCH`**。 + +**P0 前端按本设计先做 UI 与路由**;若要满足「其他角色也能进数据分析 Agent」,需 **统筹/后端小改**(扩大 analyst 矩阵准入 + Tool/SQL 网关按 permissions 挡明细)。建议单独 Issue: + +- 扩大 `analyst` 的 `roles` 白名单(至少 `advisor`, `risk_officer`, `customer` 等 staff/customer 组合按手册 §5.4 修订)。 +- NL2SQL / 查表 Tool 接入后,在 Tool 层用 `permissions` / `assert_customer_access` 挡明细。 + +前端不绕过鉴权;403 即产品行为。 + +### 2.5 Demo 登录账号 + +| 卡片 | actor_id | token_type | 默认 landing | +| --- | --- | --- | --- | +| 客户 | `CUST-9527` | customer | `#/app/customer/home` | +| 理财师 | `STAFF-10086` | staff | `#/app/advisor/customers` | +| 分析员 | `STAFF-20001` | staff | `#/app/analytics/query` | +| 风控专员 | `STAFF-30001` | staff | `#/app/risk/alerts` | + +### 2.6 产品行情 · 静态净值一览(用户拍板 2026-09-09) + +**定位:** P0 替代「看盘」的 **轻量 Demo**——展示代销产品 **最新一条模拟净值**,不是证券实时行情。 + +**路由:** `#/app/market`(**全角色共享**,侧栏「产品行情」) + +**数据来源(真接,不 Mock):** + +1. `GET /api/products?limit=50&offset=0` → 产品列表(名称、风险等级、产品类型等) +2. 对每个 `product_id` 调 `GET /api/products/{product_id}/nav` → 合并为表格行 + - P0 产品约 14 只,前端 `Promise.all` 并发可接受 + - v0.2 草案:`GET /api/products/nav-snapshot` 批量接口,减少 N+1(**非 P0 阻塞**;见 [接口契约 v0.2 行情扩展草案](../../项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md)) + +**表格列(Ant Design Table):** + +| 列 | 字段 | +| --- | --- | +| 产品代码 | `product_id` | +| 产品名称 | `product_name` | +| 最新净值 | `nav` | +| 日涨跌 | `daily_chg_pct`(正红负绿,`Typography.Text type="danger/success"`) | +| 净值日期 | `nav_date` | +| 操作 | 链接 `#/app/customer/products` 或弹 Drawer 产品详情(`GET /api/products/{id}`) | + +**页眉 Alert(固定 copy):** + +> 当前为 **Core 模拟库静态净值**(种子日期如 2026-09-04),**非实时行情**。实时/K 线/自选等能力计划在 **Phase B** 接入外部信息 API。 + +**交互:** + +- 支持按产品名搜索(前端 filter)、按 `daily_chg_pct` 排序 +- 刷新按钮:重新拉 products + nav +- 无 nav 的产品:行内 `-` + Tooltip「暂无净值数据」 + +**Phase B(不在 P0 实现,已登记文档):** + +```text +第三方 REST(咕咕/iTick/次方等择一 PoC · 见 C-05 选型对比) + ↓ scripts/sync/sync_market_nav.py(仅后端 cron/手动) + ↓ UPSERT core_product_nav + ↓ GET /api/products/nav-snapshot(Platform API · v0.2 草案) +web /app/market 改调 nav-snapshot;Alert 文案切「T+1 同步 · 非实时交易行情」 +``` + +详见:[C-05-行情数据源选型对比.md](../../项目框架设计/C-05-行情数据源选型对比.md) · [接口契约 v0.2 行情扩展草案](../../项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md) + +需求场景 **C-05 行情** 仍归 Agent/平台后续;P0 只交付静态页满足 Demo 与布局完整性。 + +--- + +## 3. 技术结构(§2 定稿) + +### 3.1 目录 + +```text +web/ +├── vite.config.ts # proxy /api → :8000 +├── src/ +│ ├── api/client.ts # Bearer、错误体四键、X-Trace-Id +│ ├── api/chatStream.ts # SSE OpenAI chunk 解析 +│ ├── stores/authStore.ts +│ ├── layouts/AppLayout.tsx +│ ├── routes/menus.tsx # 角色菜单 + 共享 analytics 项 +│ ├── pages/market/ # MarketQuotes.tsx 静态净值一览 +│ ├── pages/analytics/ # query.tsx + chat.tsx(共享模块) +│ ├── pages/{customer,advisor,risk}/... +│ ├── components/chat/ # ChatPanel 复用 +│ ├── components/AgentBanner.tsx +│ ├── components/PermissionGate.tsx # 按 permissions 禁用/提示 +│ └── mocks/analystQuery.ts # 问数 Mock(仅 analyst P0 演示) +``` + +### 3.2 API 调用约定 + +| 场景 | Header | +| --- | --- | +| 平台读 API | `Authorization: Bearer` | +| 各角色 Chat | + `X-Agent-Type: customer\|advisor\|risk` | +| 数据分析 Chat / sessions | + `X-Agent-Type: analyst`(固定) | + +### 3.3 Chat 组件 + +- `ChatPanel` 接收 prop `agentType`,内部所有 chat API 带对应 `X-Agent-Type`。 +- `/app/analytics/chat` 固定 `agentType="analyst"`。 +- SSE:首帧 `meta.session_id / trace_id / disclaimer`;错误帧 `STREAM_FAILED` / `PERSIST_FAILED`。 + +### 3.4 权限 UI + +- `PermissionGate`:`permissions.includes('sql:execute:readonly')` 等。 +- `authStore` 保存 login 返回的 `roles`;若后端 JWT payload 含 `permissions`,解析存入(dev token 已有)。 + +--- + +## 4. Mock / 真接策略(§3 定稿) + +| 页面 | 数据 | +| --- | --- | +| 客户/产品/持仓/流水/适当性/理财师客户列表 | ✅ 平台 API 真接 | +| **产品行情(静态)** | ✅ `GET /api/products` + 逐产品 `GET .../nav` 合并 | +| customer / risk Chat | ✅ SSE 真接 | +| advisor Chat | ✅ SSE + Banner | +| **analytics Chat** | ✅ SSE(`analyst`);非 analyst 角色依赖后端矩阵扩展 | +| **analytics 问数** | analyst:Mock 表格 + 留痕说明;其他角色:同壳,403 真展示 | +| 风控 alerts | ✅ `GET /api/risk/alerts` | +| 首页 KPI | 部分真汇总 + 「演示」角标 | + +--- + +## 5. 验收标准 + +1. 四 Demo 账号登录进入对应工作台;侧栏均有 **数据分析**、**产品行情** 入口。 +2. **产品行情** 页展示 ≥10 行净值数据,页眉 Alert 标明静态模拟;涨跌列可排序。 +3. `STAFF-20001` 可进问数 + Chat;其他角色可进 analytics Chat 路由(后端未扩矩阵前可见 403 页也算预期,文档注明)。 +4. 客户持仓页、风控预警列表有真数据;Chat 流式与 disclaimer 正常。 +5. 403/401 统一 `ApiErrorResult`,可复制 trace_id。 +6. `npm run build` 通过;Vitest 至少覆盖 `authStore`、SSE 解析、`mergeProductNavRows` 纯函数。 + +--- + +## 6. 实现顺序建议(供 writing-plans) + +1. `web/` init(Vite + AntD + HashRouter + proxy) +2. `authStore` + Login + AppLayout + 路由守卫 +3. 平台 API client + customer 只读页(一条链路跑通) +4. **共享 `/app/market` 静态产品行情页** +5. 共享 `ChatPanel` + customer/risk Chat +6. **`/app/analytics` 模块**(PermissionGate + query + chat) +7. advisor / risk 其余页 + 占位页 +8. Vitest + README(`web/README.md` 启动说明) + +--- + +## 7. 修订记录 + +| 日期 | 说明 | +| --- | --- | +| 2026-09-09 | 首版:AntD P0 壳 + 四角色 + 平台真接 | +| 2026-09-09 | 增补:数据分析模块跨角色可进;查表鉴权挡;后端矩阵扩展待办 | +| 2026-09-09 | **§8 视觉布局确认** + `web/` 脚手架:登录插画/Logo/Empty SVG · ProLayout 壳 · Demo 登录 | + +--- + +## 8. 视觉风格与布局(待用户确认 · 2026-09-09) + +### 8.1 整体风格定位 + +| 维度 | P0 拍板 | +| --- | --- | +| **气质** | **金融代销 / 内勤工作台**——稳重、信息密度适中,偏「银行 App + 运营后台」而非 C 端炫动 | +| **组件库** | **Ant Design 5** 默认 Design Token + 少量品牌色覆盖(**不**上 Ant Design Pro 付费版;用 `Layout` 自拼 ProLayout 结构) | +| **主题** | **浅色为主**(`algorithm: default`);P0 不做暗色切换 | +| **主色** | `#1677ff`(AntD 默认蓝,信任感);风控警示用 `#ff4d4f`,涨 `#cf1322` / 跌 `#389e0d`(A 股习惯:红涨绿跌) | +| **圆角** | 全局 `borderRadius: 6`(卡片、按钮统一) | +| **密度** | **`size: middle`** 默认;表格 `size="small"` 以容纳更多列 | +| **字体** | 系统栈:`-apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif`;数字列 **`font-variant-numeric: tabular-nums`** 对齐 | +| **图标** | `@ant-design/icons`;侧栏菜单项带 outline 图标(`UserOutlined`、`LineChartOutlined` 等) | + +**不做:** 大面积渐变、玻璃拟态、动效-heavy 首页;P0 不定制插画系统。 + +### 8.2 全局壳布局(登录后 `#/app/*`) + +```text +┌─────────────────────────────────────────────────────────────────┐ +│ Header 高 56px · 白底 · 底边 1px #f0f0f0 │ +│ [≡折叠] JinRong 智能管家 [角色Tag] [ActorId] [退出] │ +├──────────┬──────────────────────────────────────────────────────┤ +│ Sider │ Content 区 · 背景 #f5f5f5 · padding 24px │ +│ 宽 220px │ ┌─ PageShell ─────────────────────────────────────┐ │ +│ 可折叠 │ │ Breadcrumb + 标题 + 右侧操作区(刷新/导出占位) │ │ +│ 白底 │ ├─────────────────────────────────────────────────┤ │ +│ │ │ Card / Table / Chat 主体 │ │ +│ · 角色菜单│ └─────────────────────────────────────────────────┘ │ +│ · 共享菜单│ │ +│ 产品行情│ │ +│ 数据分析│ │ +└──────────┴──────────────────────────────────────────────────────┘ +``` + +| 区域 | 规格 | +| --- | --- | +| **Header** | 左:Logo 文案 + 侧栏折叠;右:`Tag` 显示当前角色(客户/理财师/分析/风控)、`Typography.Text` 显示 `actor_id`(可复制)、退出按钮 | +| **Sider** | 220px 展开 / 64px 折叠;菜单 **分组**:「工作台」「智能 Agent」「平台服务」;当前路由高亮;共享项(产品行情、数据分析)在「平台服务」组 | +| **Content** | 最大内容宽 **无硬 cap**(表格全宽);Chat 页除外(见 8.4) | +| **PageShell** | 统一:`Breadcrumb` → `Typography.Title level={4}` → 可选 `Alert`(静态净值/Agent 建设中)→ 子内容 | + +### 8.3 登录页 `#/login` + +```text +┌────────────────────────────────────────────┐ +│ 左 40% 品牌区(浅蓝 #e6f4ff 底) │ +│ JinRong 智能管家 · 四 Agent 演示平台 │ +│ │ +│ 右 60% 白底卡片 │ +│ 「选择演示账号登录」 │ +│ [ 客户 CUST-9527 ] 一键登录 │ +│ [ 理财师 STAFF-10086 ] │ +│ [ 分析员 STAFF-20001 ] │ +│ [ 风控 STAFF-30001 ] │ +│ 小字:Mock JWT · 仅供开发演示 │ +└────────────────────────────────────────────┘ +``` + +- 移动端 P0 **不优先适配**(桌面 Demo 为主);最小宽 1280px 设计。 + +### 8.4 页面类型模板 + +#### A. 数据列表页(持仓 / 流水 / 产品行情 / 预警 / 名下客户) + +```text +[ Alert 可选 ] +[ Card ] + Toolbar: [搜索 Input] [刷新 Button] [占位 Primary 禁用+Tooltip] + Table: bordered=false, pagination, 金额右对齐, 涨跌带色 +``` + +- **产品行情**:顶部 **黄色 Alert**「静态模拟净值…」;涨跌列红涨绿跌。 + +#### B. 详情 / 表单页(档案 / 适当性试算) + +- 左 **`Descriptions` bordered** 或右 **`Form` + `Card`**;提交 `Button type="primary"`;结果用 **`Result`** 或 **`Alert type="success/error"`** 展示阻断原因列表。 + +#### C. Chat 页(各 Agent 共用 `ChatPanel`) + +```text +┌─────────────────────────────────────────────────────────────┐ +│ [ AgentBanner 可选 · info 蓝条 ] │ +├───────────────┬─────────────────────────────────────────────┤ +│ SessionList │ MessageList(气泡) │ +│ 宽 260px │ flex 1 · 灰底 #fafafa │ +│ Card 内 │ user 右对齐蓝泡 / assistant 左对齐白泡 │ +│ 新建/关闭会话 │ 底部固定:免责声明小字(has_disclaimer) │ +├───────────────┴─────────────────────────────────────────────┤ +│ Input.TextArea + [发送] · SSE 流式逐字 │ +└─────────────────────────────────────────────────────────────┘ +``` + +- **免责声明**:customer/risk 在首帧 `meta.disclaimer` 下发展示 **`Alert type="warning"`** 折叠条,assistant 气泡底部也可重复小字(与后端落库一致)。 +- **流式**:assistant 气泡末尾闪烁光标 `▍`(P0 简易 CSS)。 + +#### D. 首页 `#/app/home`(按角色) + +| 角色 | 布局 | +| --- | --- | +| customer | 4 列 **`Statistic` Card**(持仓市值/产品数/会话数/占位)+ 快捷入口 **`Row` + `Button`** | +| advisor | 名下客户 **`Statistic`** + 待办占位 Card | +| analyst | 跳转数据分析 **`Card` 可点击** | +| risk | 待审预警数 **`Badge`** + 最近预警 **`List`** 摘要 | + +部分数字带 **「演示」`Tag`**(半 Mock KPI)。 + +#### E. 占位页(草稿箱 / AML / 未接入 Agent) + +- 统一 **`Empty`** + 说明文案 + **`Button type="link"`** 回首页;不用整页插画。 + +### 8.5 状态与反馈 + +| 场景 | 组件 | +| --- | --- | +| 加载 | 表格 **`Skeleton`** / 页面 **`Spin`** | +| 403/401 | **`Result status="403"`** + `error_code` + 可复制 trace_id | +| Agent 建设中 | **`Alert type="info" showIcon`**(`AgentBanner`) | +| 操作成功 | **`message.success`**(toast) | +| 流式失败 | Chat 区内 **`Alert type="error"`**(STREAM_FAILED) | + +### 8.6 ConfigProvider 初值(`web/src/theme.ts`) + +```ts +{ + token: { + colorPrimary: '#1677ff', + borderRadius: 6, + fontSize: 14, + }, + components: { + Layout: { headerBg: '#ffffff', siderBg: '#ffffff', bodyBg: '#f5f5f5' }, + Table: { headerBg: '#fafafa' }, + }, +} +``` + +### 8.7 与后续 Phase B 的兼容 + +- 外部行情接入后:**产品行情** 页仅增 Tab「静态净值 | 实时行情」,壳布局 **不变**。 +- 暗色主题:预留 `theme.ts` 导出,P0 不接线。 + +**请用户确认:** 以上风格(金融稳重 + AntD 默认蓝 + 红涨绿跌 + 侧栏工作台壳 + Chat 左会话右消息)是否 OK;若需「招行红」「深色 Header」等品牌色,指出主色 hex 即可调整。 + +### 8.8 装饰素材(用户拍板 · 2026-09-09) + +| 位置 | 素材 | 来源 / 许可 | +| --- | --- | --- | +| 登录页左栏 | `web/public/assets/illustrations/login-finance.svg` | 项目自绘矢量(主色 `#1677ff`,金融图表+盾牌意象) | +| 登录页 / Header 小标 | `web/public/assets/brand/logo-mark.svg` | 自绘「JR」圆角标 | +| Empty / 占位页 | `web/public/assets/illustrations/empty-data.svg` | 自绘(文件夹+图表) | +| Chat 空会话 | `web/public/assets/illustrations/empty-chat.svg` | 自绘(对话气泡) | +| 首页 Hero(可选) | 角色差异化 **Statistic Card** 为主,不铺大图 | — | + +**备选(后续可替换):** [unDraw](https://undraw.co/) Finance / Personal Finance / Financial Advisor 类 SVG(**免费商用、无需署名**;禁止打包再分发)。替换时保持主色 `#1677ff` 即可。 + +**规范:** 插图仅用于登录/Empty/403 等非数据区;**表格、行情、持仓页不用背景大图**,避免干扰读数。 + diff --git a/docs/项目框架设计/C-05-行情数据源选型对比.md b/docs/项目框架设计/C-05-行情数据源选型对比.md new file mode 100644 index 0000000..e80d783 --- /dev/null +++ b/docs/项目框架设计/C-05-行情数据源选型对比.md @@ -0,0 +1,158 @@ +# C-05 行情 / 净值数据源选型对比 + +> 场景 ID:**C-05**(客户查产品净值 · 「这只基金现在净值多少」) +> 用途:答辩 / 发群 / Phase B 外部同步拍板 +> 关联:[接口契约-代销平台API-v0.1.md](./接口契约-代销平台API-v0.1.md) · [接口契约-代销平台API-v0.2-行情扩展草案.md](./接口契约-代销平台API-v0.2-行情扩展草案.md) · [Core模拟底座/01-表结构与种子说明.md](./Core模拟底座/01-表结构与种子说明.md) · [需求拆解/用户故事/平台与行业背景.md](../需求拆解/用户故事/平台与行业背景.md) + +--- + +## 1. 需求边界(本项目) + +| 要 | 不要 | +| --- | --- | +| 代销 **场外公募/理财** 产品 **最新净值 + 日涨跌 + 净值日期** | 股票 **实时 tick / 分时 / K 线** | +| 数据进 **Core L0**(`core_product_nav`)→ Platform API → 前端/Agent | 前端/Agent **直连** 第三方行情 REST | +| 开发 Demo:**静态种子** 即可 | 毫秒级行情、自选股、全市场看盘终端 | +| 合规叙事:**持牌代销平台** 自有产品货架 | 爬虫蚂蚁/天天基金/雪球内部接口 | + +PRD / 风控 PRD 已明确:**不做**真实 TA/清算/毫秒级行情。 + +--- + +## 2. 一句话结论 + +> **国内头部互联网代销(蚂蚁、天天基金等)没有面向第三方的官方「看盘 API」。** +> **本项目 P0 用 Core 模拟库;Phase B 可选付费 REST 数据商做 T+1 同步进 Core;生产级走 Wind/Choice/iFinD 或 TA/登记披露对账。** + +--- + +## 3. 数据源对比总表 + +| 类别 | 代表 | 能否用于 C-05 净值 | 本项目阶段 | 合规/稳定 | 备注 | +| --- | --- | --- | --- | --- | --- | +| **Core 模拟种子** | `scripts/core/06-seed-nav.sql` | ✅ 完全匹配 | **P0 · 当前** | ✅ | `GET /api/products/{id}/nav` 已实现 | +| **互联网代销开放 API** | 蚂蚁开放平台、天天基金、雪球 | ❌ 无官方通用净值 API | **禁止** | ❌ | 内部 JS/爬虫非授权 | +| **页面爬虫 / 逆向** | fundgz、pysnowball | ⚠️ 能抓到但不稳定 | **禁止** | ❌ | 与持牌平台叙事冲突 | +| **第三方 REST 数据 API** | 咕咕数据、iTick、次方量化 | ✅ 场外基金净值 | **Phase B PoC** | 🟡 需合同+字段核对 | 经 `sync_market_nav` 落 Core | +| **金融终端数据商** | Wind、Choice、同花顺 iFinD | ✅ 全品种+历史 | **生产方案** | ✅ 机构签约 | Demo 阶段不必签 | +| **券商 OpenAPI** | 富途、长桥、华盛 | 🟡 偏场内/港美 | **不适用** | ✅ 开户即用 | 与场外公募代销主场景不对题 | +| **券商 QMT/PTrade** | 国内券商 | 🟡 本地 Python | **不适用** | ✅ 门槛高 | 量化交易,非净值展示 | +| **交易所 / 基金通 B2B** | 上证基金通 snapshot | 🟡 场内 ETF/LOF | **未来扩展** | ✅ 需机构接入 | 与当前 14 只种子产品不同线 | +| **登记中心 / TA 披露** | 理财登记、基金公司 TA | ✅ 权威净值来源 | **生产对账** | ✅ 批量日终 | 非实时 REST,文件/专线 | + +图例:**✅ 推荐** · **🟡 有条件** · **❌ 不推荐/禁止** + +--- + +## 4. 禁止清单(团队硬规则) + +以下 **不得** 进入 `scripts/sync/` 或生产代码: + +1. 天天基金 `fundgz.1234567.com.cn` 等非官方内部 URL +2. 雪球 Cookie / Token 逆向(pysnowball 等) +3. 任何 **未签约** 的页面 scraping +4. 前端或 Agent Tool **携带第三方 appkey 直连** 外网行情 +5. 将外部 API 响应 **伪造为实时 tick** 展示(监管与产品口径不允许) + +--- + +## 5. 分阶段推荐路径 + +| 阶段 | 数据源 | 接入方式 | 对外 API | +| --- | --- | --- | --- | +| **P0 开发/Demo** | Core 种子 | `reset.ps1` + `06-seed-nav.sql` | `GET /api/products`、`/nav` | +| **P0 前端** | 同上 | `web/` 产品行情页真调平台 API | 页眉:**静态模拟净值** | +| **Phase B 验证** | 咕咕 / iTick / 次方 等 **择一 PoC** | `scripts/sync/sync_market_nav.py` → UPSERT `core_product_nav` | 新增 `GET /api/products/nav-snapshot`(见 v0.2 草案) | +| **生产** | Wind / Choice / iFinD + TA 披露 | 日终批处理 + 对账 | 同上;`quote_mode=external_t1` | +| **Phase C(可选)** | 历史净值 / 场内 ETF 实时 | 历史表或交易所 B2B | `GET .../nav/history` 等待定 | + +--- + +## 6. Phase B PoC 第三方 REST 细对比(候选,非背书) + +> 以下为 **调研摘要**,签约前须自行核对最新文档、价格与授权范围。 + +| 维度 | 咕咕数据(示例) | iTick(示例) | 次方量化(示例) | +| --- | --- | --- | --- | +| **典型能力** | 公募基金净值 REST | 场内 ETF/LOF 报价 | 场内基金 spot | +| **与 C-05 匹配度** | 高(场外净值) | 中(偏场内) | 中 | +| **接入成本** | appkey + 按量 | API key | x-api-key | +| **入库方式** | sync 脚本 | sync 脚本 | sync 脚本 | +| **Demo 推荐度** | **优先试** | 有场内产品时试 | 备选 | + +**PoC 验收标准(4 条):** + +1. 能映射到种子库 `PROD-*`(或配置 `MARKET_NAV_PRODUCT_MAP`) +2. 返回含 `nav` + `nav_date`(或等价字段) +3. 日终 T+1 延迟可接受(页眉标明,不冒充实时) +4. 商业授权允许 **入库再展示** + +--- + +## 7. 生产级数据商(文档/答辩用) + +| 服务商 | 典型用途 | 门槛 | +| --- | --- | --- | +| **Wind Client API** | 全市场历史+实时 | 机构付费终端 | +| **东方财富 Choice EMQuantAPI** | 基金净值、基本面 | 机构试用/付费 | +| **同花顺 iFinD QuantAPI** | 多资产 REST/SDK | 机构账号 | + +**与第三方小 REST 的分工:** + +- PoC:**小 REST** 验证 sync 管道与 UI +- 生产:**数据商** 替换 adapter 实现,**Platform API 路径不变** + +--- + +## 8. 架构对齐(为什么不是「接代销 App API」) + +```text +┌─────────────────────────────────────────────────────────┐ +│ XX 科技持牌代销平台(本项目) │ +│ 卖的是自有货架 core_product · 净值权威在 core_product_nav │ +└───────────────────────────┬─────────────────────────────┘ + │ + P0: 种子 SQL │ Phase B+: sync_market_nav + ▼ + jinrong_core.core_product_nav + │ + ▼ + GET /api/products/nav-snapshot + │ + ┌─────────────┴─────────────┐ + ▼ ▼ + web 产品行情页 Agent / 其他 Agent +``` + +**行业常态:** 代销平台 **采购** 数据或接 **TA/登记披露**,而不是调用竞品 App 的接口。 + +--- + +## 9. 与场景 / 矩阵映射 + +| 场景 | 数据需求 | 当前状态 | 数据源 | +| --- | --- | --- | --- | +| **C-05** | 最新净值 | API v0.1 已有;Agent T-40 未做 | Core 种子 | +| **C-01** | 持仓市值 | `core_holding.market_value` 种子 | Core(非实时重算) | +| 前端「产品行情」 | 全产品净值表 | P0 设计已定 | Platform API | +| R-02 / 适当性 | 产品 R 等级 | Core | 与净值源独立 | + +--- + +## 10. 决策记录(建议拍板) + +| # | 决策 | 建议 | +| --- | --- | --- | +| 1 | P0 是否接外部 API | **否**,Core 种子 + 静态页眉 | +| 2 | Phase B 是否做 sync | **是**,选一个 REST PoC + `sync_market_nav.py` | +| 3 | 是否做实时看盘 | **否**(Phase C 再评估场内 ETF) | +| 4 | 生产数据商 | 文档登记 Wind/Choice/iFinD,实施时再签 | +| 5 | 禁止爬虫 | **是**,写入开发规范 | + +--- + +## 11. 修订记录 + +| 日期 | 说明 | +| --- | --- | +| 2026-09-09 | 首版:互联网代销/API 调研结论 + 分阶段选型 + Phase B PoC 对比 | diff --git a/docs/项目框架设计/Core模拟底座/00-方案总览.md b/docs/项目框架设计/Core模拟底座/00-方案总览.md index 554deb9..4947619 100644 --- a/docs/项目框架设计/Core模拟底座/00-方案总览.md +++ b/docs/项目框架设计/Core模拟底座/00-方案总览.md @@ -114,6 +114,8 @@ docs/项目框架设计/Core模拟底座/ | `core_cash_flow` | 资金进出 | C-01 | | `core_product_nav` | 最新净值/日涨跌 | C-05 | +P0 净值来自种子 SQL;Phase B 可选经 `sync_market_nav` 从第三方 REST **T+1 同步**进本表(见 [C-05-行情数据源选型对比.md](../C-05-行情数据源选型对比.md))。 + **P1 可加:** `core_industry`、`core_dividend`、`core_aml_list`(R-03 名单) ### 4.1 关键字段约定 diff --git a/docs/项目框架设计/Core模拟底座/01-表结构与种子说明.md b/docs/项目框架设计/Core模拟底座/01-表结构与种子说明.md index 298f860..97e8a14 100644 --- a/docs/项目框架设计/Core模拟底座/01-表结构与种子说明.md +++ b/docs/项目框架设计/Core模拟底座/01-表结构与种子说明.md @@ -196,6 +196,8 @@ NEO4J_PASSWORD=... - [Neo4j 模型](../表设计/04-neo4j-model.md) - [画像 L1/L2/L3](../表设计/06-用户画像L1-L3设计.md) - [适当性 log 契约](../表设计/07-risk_suitability_log说明.md) +- [C-05 行情数据源选型对比](../C-05-行情数据源选型对比.md)(Phase B 外部 sync 拍板) +- [接口契约 v0.2 行情扩展草案](../接口契约-代销平台API-v0.2-行情扩展草案.md)(`nav-snapshot` · `sync_market_nav`) - 客户资料:`docs/来自客户的项目资料/金融/` diff --git a/docs/项目框架设计/接口契约-代销平台API-v0.1.md b/docs/项目框架设计/接口契约-代销平台API-v0.1.md index d8ddb4a..60462fa 100644 --- a/docs/项目框架设计/接口契约-代销平台API-v0.1.md +++ b/docs/项目框架设计/接口契约-代销平台API-v0.1.md @@ -114,7 +114,11 @@ Service 文件:`app/service/platform/customer_service.py` `product_service.py` | POST | `/api/compliance/suitability-check` | `check_suitability` | G-01 归属;**canonical 适当性** | | POST | `/api/simulate/trade` | 已有 trade_gateway | risk_demo 或 customer 本人 | -**v0.2 候选(非 v0.1 阻塞):** `GET /api/customers/{customer_id}/cash-flows`(需补 RO)。 +**v0.2 行情扩展(草案 · 未实现):** 见 [接口契约-代销平台API-v0.2-行情扩展草案.md](./接口契约-代销平台API-v0.2-行情扩展草案.md)(`GET /api/products/nav-snapshot`、`sync_market_nav`)。 + +**其他 v0.2 候选:** `GET /api/customers/{customer_id}/cash-flows`(需补 RO)。 + +**C-05 数据源选型:** [C-05-行情数据源选型对比.md](./C-05-行情数据源选型对比.md) --- @@ -162,3 +166,4 @@ Service 文件:`app/service/platform/customer_service.py` `product_service.py` | 2026-09-08 | check_source ENUM 增 `platform`(DDL + `scripts/agent/migrate-check-source-platform.sql`) | | 2026-09-08 | 实现前拍板:整包交付 · 适当性新旧并存 · analyst 全量只读 · 平台 JWT 免 X-Agent-Type | | 2026-09-08 | 脱敏:`PLATFORM_RESPONSE_DESENSITIZE` 默认 false;真库再开 · risk_demo 全量只读 | +| 2026-09-09 | 指向 v0.2 行情草案与 C-05 数据源选型对比 | diff --git a/docs/项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md b/docs/项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md new file mode 100644 index 0000000..4b77d71 --- /dev/null +++ b/docs/项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md @@ -0,0 +1,236 @@ +# 接口契约 · 代销平台 API v0.2 行情扩展(草案) + +> 状态:**草案(未实现)** · v0.1 仍为当前 canonical 实现基线 +> 范围:**C-05 产品净值 / 前端「产品行情」页** · Phase B 外部数据同步 +> 关联:[接口契约-代销平台API-v0.1.md](./接口契约-代销平台API-v0.1.md) · [C-05-行情数据源选型对比.md](./C-05-行情数据源选型对比.md) · [04-从需求到公共API开发方法.md](../项目管理/04-从需求到公共API开发方法.md) + +--- + +## 0. 与 v0.1 的关系 + +| 项 | v0.1(已实现) | v0.2 草案(本文件) | +| --- | --- | --- | +| 单产品净值 | `GET /api/products/{product_id}/nav` | **保留不变** | +| 产品列表 | `GET /api/products` | **保留不变** | +| 批量净值快照 | 无(前端 N+1 调 nav) | **新增** `GET /api/products/nav-snapshot` | +| 数据来源 | 仅 `core_product_nav` 种子 | 可选 **外部 sync 脚本** 写入同表 | +| 实时看盘/K 线 | 不在范围 | **仍不在范围** | + +**拍板原则(2026-09-09):** + +- 前端 / Agent **只调本平台 API**,禁止直连第三方行情 REST。 +- 外部数据经 **后端 sync 落 Core**,L0 权威仍为 `jinrong_core.core_product_nav`。 +- 页眉须区分 **「模拟静态」** vs **「外部同步 T+1」**(非毫秒实时)。 + +--- + +## 1. 新增端点 + +### 1.1 `GET /api/products/nav-snapshot` + +**用途:** 产品行情页、Agent 批量问净值;替代前端对每只产品单独 `GET .../nav` 的 N+1。 + +**鉴权:** 与 v0.1 `/api/products*` 相同——`Authorization: Bearer`;**不要求** `X-Agent-Type`。 + +**Query 参数:** + +| 参数 | 类型 | 默认 | 说明 | +| --- | --- | --- | --- | +| `limit` | int | 50 | 1~200 | +| `offset` | int | 0 | 分页 | +| `product_ids` | string | — | 可选,逗号分隔,如 `PROD-510300,PROD-161725`;与分页互斥时 **product_ids 优先** | + +**成功响应(200):** + +```json +{ + "code": 0, + "message": "ok", + "trace_id": "trc-…", + "data": { + "items": [ + { + "product_id": "PROD-510300", + "product_name": "沪深300ETF联接", + "min_risk_code": "R3", + "nav": 1.03, + "daily_chg_pct": 0.35, + "nav_date": "2026-09-04", + "nav_source": "core_seed" + } + ], + "total": 14, + "limit": 50, + "offset": 0, + "as_of": "2026-09-04", + "quote_mode": "static_seed" + } +} +``` + +**字段说明:** + +| 字段 | 说明 | +| --- | --- | +| `nav_source` | `core_seed` \| `external_sync` \| `missing`(无净值行) | +| `quote_mode` | 汇总口径:`static_seed`(仅种子)\| `external_t1`(含外部同步且 `MARKET_NAV_ENABLED=true`) | +| `as_of` | 本页净值最大 `nav_date`(便于 UI 展示「数据截至」) | + +**无净值产品:** 仍出现在 `items` 中,`nav`/`daily_chg_pct`/`nav_date` 为 `null`,`nav_source=missing`。 + +**错误:** 手册 §10 统一错误体;401/403 同 v0.1。 + +--- + +### 1.2 v0.2 候选(仍非本草案阻塞) + +| 方法 | 路径 | 说明 | +| --- | --- | --- | +| GET | `/api/customers/{customer_id}/cash-flows` | 资金流水;需补 `core_ro` | +| GET | `/api/products/{product_id}/nav/history` | 历史净值序列(C-05 增强);Phase C,需 DDL 或历史表 | + +--- + +## 2. Service / Repository 扩展 + +### 2.1 分层(延续 v0.1 §2.4) + +```text +GET /api/products/nav-snapshot + → app/api/products.py + → app/service/platform/product_service.py :: list_products_nav_snapshot() + → app/repository/core_ro.py :: list_products_with_latest_nav(limit, offset, product_ids?) +``` + +**禁止:** 路由层直连 `core_ro`;禁止 Service 内调用第三方 HTTP。 + +### 2.2 `core_ro` 新方法(拟) + +```python +def list_products_with_latest_nav( + self, + limit: int = 50, + offset: int = 0, + product_ids: list[str] | None = None, +) -> tuple[list[dict], int]: + """ + LEFT JOIN core_product + 各 product 最新 nav(nav_date DESC 子查询)。 + 返回 product 字段 + nav/nav_date/daily_chg_pct + nav_source(来自 nav 行扩展列或 settings 推断)。 + """ +``` + +**性能:** 单次 SQL JOIN;演示库 ~14 产品无压力。真库 >500 产品时加索引 `(product_id, nav_date DESC)`。 + +### 2.3 `nav_source` 落库(可选 DDL · Phase B) + +若需区分种子与外部同步,可在 `core_product_nav` 增列(**草案,实施前评审**): + +```sql +ALTER TABLE core_product_nav + ADD COLUMN nav_source ENUM('core_seed','external_sync') NOT NULL DEFAULT 'core_seed' + COMMENT '净值来源:种子 / 外部同步'; +``` + +不增列时:`quote_mode` 仅由 `settings.market_nav_enabled` + 同步脚本日志推断。 + +--- + +## 3. 外部净值同步(`market_nav_sync` · 脚本,非 HTTP API) + +### 3.1 架构 + +```text +第三方 REST(咕咕/iTick/Choice 等 · 见选型对比 doc) + ↓ scripts/sync/sync_market_nav.py (拟) + ↓ adapter: app/service/platform/market_nav_adapter.py (拟) +UPSERT jinrong_core.core_product_nav + ↓ +GET /api/products/nav-snapshot (读路径不变) +``` + +### 3.2 环境变量(`.env.example` 拟增) + +| 变量 | 默认 | 说明 | +| --- | --- | --- | +| `MARKET_NAV_ENABLED` | `false` | 是否启用外部同步(false 时仅种子) | +| `MARKET_NAV_PROVIDER` | `none` | `none` \| `gugu` \| `itick` \| `mock`(联调假源) | +| `MARKET_NAV_API_KEY` | — | 第三方 appkey(**勿提交 git**) | +| `MARKET_NAV_API_BASE` | — | 提供商 REST 根 URL | +| `MARKET_NAV_SYNC_CRON` | — | 文档约定,如 `0 20 * * *`(日终 T+1) | +| `MARKET_NAV_PRODUCT_MAP` | — | 可选 JSON 文件路径:外部 code → `PROD-*` | + +**settings.py 拟增:** + +```python +market_nav_enabled: bool = False +market_nav_provider: str = "none" +market_nav_api_key: str = "" +market_nav_api_base: str = "" +``` + +### 3.3 同步脚本行为(拟) + +```text +scripts/sync/sync_market_nav.py + --dry-run 只打印将写入行,不写库 + --provider gugu 覆盖 MARKET_NAV_PROVIDER + --product-id PROD-510300 单产品调试 +``` + +**规则:** + +1. 仅 **UPSERT** `core_product_nav`;**不** UPDATE `core_product` 风险等级等业务字段。 +2. 同步失败:**不删**已有种子行;写 `audit_log` 或脚本日志;API 仍返回最后成功快照。 +3. `product_id` 必须在 `core_product` 存在且 `is_open=1`,否则 skip 并 warn。 +4. 禁止爬虫天天基金 / 雪球等非授权源(见选型对比 §4 禁止清单)。 + +### 3.4 Bootstrap 补充(FLOW / README) + +```text +# Phase B 可选(MARKET_NAV_ENABLED=true 时) +python scripts/sync/sync_market_nav.py +# 然后 +GET /api/products/nav-snapshot → quote_mode=external_t1 +``` + +--- + +## 4. 前端契约(`web/` · 产品行情页) + +| 项 | P0(当前) | v0.2 接入后 | +| --- | --- | --- | +| API | `GET /api/products` + N×`GET .../nav` | **`GET /api/products/nav-snapshot`** | +| Alert 文案 | 「Core 模拟库静态净值,非实时行情」 | 「外部数据源 T+1 同步 · 非实时交易行情」或保留静态说明 | +| Tab | 无 | 可选「净值一览 \| 实时行情(Phase C 占位 Disabled)」 | + +--- + +## 5. Agent 接入 + +| 能力 | 路径 | 说明 | +| --- | --- | --- | +| 单产品净值 | `GET /api/products/{id}/nav` | v0.1 已有 | +| 批量净值 | `GET /api/products/nav-snapshot` | v0.2;Tool 合并期改调 platform Service | +| 禁止 | Agent Tool 直连第三方行情 API | 与「禁止直连 Core SQL」同级 | + +--- + +## 6. 实现检查清单(统筹) + +- [ ] `core_ro.list_products_with_latest_nav` +- [ ] `product_service.list_products_nav_snapshot` +- [ ] `GET /api/products/nav-snapshot` + 单测 +- [ ] `tests/test_platform_api.py` 增补 snapshot 用例 +- [ ] `scripts/sync/sync_market_nav.py` + adapter 抽象 +- [ ] `.env.example` + `settings` 字段 +- [ ] 可选 DDL `nav_source` +- [ ] 前端 `web/` 改调 snapshot +- [ ] 更新 v0.1 契约 §3 脚注 → 指向本文件 + +--- + +## 7. 修订记录 + +| 日期 | 说明 | +| --- | --- | +| 2026-09-09 | 首版草案:nav-snapshot · market_nav_sync · env · 分层与前端/Agent 口径 | diff --git a/docs/项目管理/03-表设计文档.md b/docs/项目管理/03-表设计文档.md index c2ce613..bc9d8b1 100644 --- a/docs/项目管理/03-表设计文档.md +++ b/docs/项目管理/03-表设计文档.md @@ -349,6 +349,7 @@ | 关键字段 | `product_id` · `nav` · `daily_chg_pct` · `nav_date` | | 关键查询 | C-05「这只基金现在净值多少」 | | 关联 | ← `core_product` | +| Phase B | 外部 REST → `sync_market_nav` UPSERT;见 [C-05-行情数据源选型对比.md](../项目框架设计/C-05-行情数据源选型对比.md) | ### 3.12 `core_industry` · 行业分类 diff --git a/docs/项目管理/04-从需求到公共API开发方法.md b/docs/项目管理/04-从需求到公共API开发方法.md index fcbda3e..35571a2 100644 --- a/docs/项目管理/04-从需求到公共API开发方法.md +++ b/docs/项目管理/04-从需求到公共API开发方法.md @@ -114,9 +114,21 @@ P1/P2 → 联调提需求 → 补 Repository → 升 API v0.2+ | 批次 | 范围 | 说明 | | --- | --- | --- | | **Phase A · API v0.1** | Core L0 + P0;对齐现有 `core_ro.py` | **现在优先**;不依赖 Agent 脑洞 | -| **Phase B · 联调驱动** | Agent 提缺的 Core 查询/聚合 | 加 Repository 方法后再暴露 API | +| **Phase B · 联调驱动** | Agent 提缺的 Core 查询/聚合;**C-05 行情外部同步** | 加 Repository 方法后再暴露 API;行情见下 §5.4 | | **Phase C · 业务公共服务** | 仪表盘 KPI、产品 CRUD+Milvus、清算模拟 | 与 Agent 并行,不阻塞 P0 联调 | +### 5.4 Phase B · C-05 行情(已拍板方向 · 2026-09-09) + +> **数据源选型:** [C-05-行情数据源选型对比.md](../项目框架设计/C-05-行情数据源选型对比.md) +> **API 草案:** [接口契约-代销平台API-v0.2-行情扩展草案.md](../项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md) + +| 能力 | Canonical REST | 实现要点 | +| --- | --- | --- | +| 批量净值快照 | `GET /api/products/nav-snapshot` | 替代前端 N+1 调 `.../nav`;读 `core_product_nav` | +| 外部 T+1 同步 | (无对外端点) | `scripts/sync/sync_market_nav.py` UPSERT Core;**禁止**前端/Agent 直连第三方行情 REST | + +P0 前端仍用 v0.1:`GET /api/products` + 逐产品 `GET .../nav`(Core 种子静态净值)。 + ### 5.3 Phase A 清单(与 `core_ro.py` 对齐) > **路由与命名权威文档:** [接口契约-代销平台API-v0.1.md](../项目框架设计/接口契约-代销平台API-v0.1.md) @@ -184,3 +196,4 @@ Agent 对话能力走第 ⑥ 步,**不重复实现 L0 查数逻辑**。 | 2026-09-08 | 首版:固化「需求→矩阵→存储→Service→API→Agent」主线及分批策略 | | 2026-09-08 | AL-09 合并完成:`merger` 分支 502 绿;下一步统筹 Phase A Core REST API v0.1 | | 2026-09-08 | 代销平台 API 拍板:路由 A(业务域)· 重复功能以平台 API 为准 · 《接口契约-代销平台API-v0.1》 | +| 2026-09-09 | 新增 §5.4 C-05 行情 Phase B:链至 v0.2 草案与 C-05 数据源选型对比 | diff --git a/docs/项目管理/README.md b/docs/项目管理/README.md index bc16c2a..41bc192 100644 --- a/docs/项目管理/README.md +++ b/docs/项目管理/README.md @@ -15,6 +15,8 @@ | 3 | [03-表设计文档.md](./03-表设计文档.md) | 给人看的表设计字典:MySQL双库/Redis/Neo4j/Milvus 分层、谁写谁读、关键字段中文释义、建表顺序 | 写代码查表、联调对字段 | | 4 | [04-从需求到公共API开发方法.md](./04-从需求到公共API开发方法.md) | **方法论文档**:需求→矩阵→存储→Service→API→Agent 主线、分批策略、统筹分工、Phase A 接口清单 | 搭公共底座/API 前;新人 onboarding | | 5 | [../项目框架设计/接口契约-代销平台API-v0.1.md](../项目框架设计/接口契约-代销平台API-v0.1.md) | **代销平台 REST 契约**:业务域路由、命名规范、v0.1 端点、Agent 合并迁移表 | 写平台 API / Review Agent 重复接口 | +| 6 | [../项目框架设计/C-05-行情数据源选型对比.md](../项目框架设计/C-05-行情数据源选型对比.md) | **C-05 净值/行情数据源**:P0 种子 vs Phase B 第三方 sync vs 生产 Wind/Choice | 答辩、发群、Phase B 拍板 | +| 7 | [../项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md](../项目框架设计/接口契约-代销平台API-v0.2-行情扩展草案.md) | **v0.2 草案**:`nav-snapshot` · `sync_market_nav` · 前端/Agent 契约 | 做产品行情页 Phase B、后端 sync | --- diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/web/.oxlintrc.json b/web/.oxlintrc.json new file mode 100644 index 0000000..6fa991d --- /dev/null +++ b/web/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["react", "typescript", "oxc"], + "rules": { + "react/rules-of-hooks": "error", + "react/only-export-components": ["warn", { "allowConstantExport": true }] + } +} diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..b943bc5 --- /dev/null +++ b/web/README.md @@ -0,0 +1,30 @@ +# JinRong Web(P0 Demo) + +React 19 + Vite + Ant Design 5 · HashRouter · 联调本机 FastAPI。 + +## 启动 + +```bash +# 终端 1:后端 +cd .. +uvicorn app.main:app --reload + +# 终端 2:前端 +cd web +npm install +npm run dev +``` + +浏览器打开 Vite 提示地址(通常 `http://127.0.0.1:5173/#/login`)。 + +## 装饰素材 + +| 文件 | 用途 | +| --- | --- | +| `public/assets/brand/logo-mark.svg` | Header / 登录 Logo | +| `public/assets/illustrations/login-finance.svg` | 登录页左侧插画 | +| `public/assets/illustrations/empty-*.svg` | Empty / 占位页 | + +可选替换:[unDraw](https://undraw.co/) 金融类 SVG(免费商用,无需署名)。 + +设计说明见 `docs/superpowers/specs/2026-09-09-frontend-p0-design.md` §8。 diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..5e3836a --- /dev/null +++ b/web/index.html @@ -0,0 +1,13 @@ + + + + + + + web + + +
+ + + diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..b8481c4 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,3438 @@ +{ + "name": "web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "0.0.0", + "dependencies": { + "@ant-design/icons": "^6.3.4", + "antd": "^6.6.3", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.3" + }, + "devDependencies": { + "@testing-library/jest-dom": "^7.0.1", + "@testing-library/react": "^16.3.3", + "@types/node": "^24.13.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitejs/plugin-react": "^6.1.0", + "jsdom": "^30.0.1", + "oxlint": "^1.79.0", + "typescript": "~6.0.2", + "vite": "^8.2.2", + "vitest": "^5.0.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ant-design/colors": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz", + "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.0" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz", + "integrity": "sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz", + "integrity": "sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^2.1.2", + "@babel/runtime": "^7.23.2", + "@rc-component/util": "^1.4.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", + "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==", + "license": "MIT", + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.4.tgz", + "integrity": "sha512-kDoUlppczVyCUTFthF6cHmPzMBYqNZQhu9j6EyIX/YIdM9j9CZNZoJUjzVw7/1v1VO8WwVMmPp15r/uwIAS0AQ==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/icons-svg": "^4.6.0", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.6.0.tgz", + "integrity": "sha512-PRomU725ABMf/lnQp5HiB7my1kjEbFY0D10N4lXYxK6TIB1gKjIVD5MRThpDaezLgw1D774J8eOeeDB0M6wHrQ==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz", + "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "clsx": "^2.1.1", + "json2mq": "^0.2.0", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.7.tgz", + "integrity": "sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.3.0", + "@csstools/css-color-parser": "^4.1.10", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.5.2" + }, + "engines": { + "node": "^22.13.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.2.tgz", + "integrity": "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.5.2" + }, + "engines": { + "node": "^22.13.0 || >=24.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.1.tgz", + "integrity": "sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.2.tgz", + "integrity": "sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.1", + "@csstools/css-calc": "^3.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.12.tgz", + "integrity": "sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.148.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", + "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.82.0.tgz", + "integrity": "sha512-a3LB+C5Dsj5b/qtmG/mv5WrzuiXEpg1KF5nXWcEvaoN5TYAqkIvxPOwTPp3Jy/FoGpRo8zsTFhMElMXfeoOEzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.82.0.tgz", + "integrity": "sha512-OBlhRgNqFblGpGenno/aqOfJLOkQ2B8Ig3iDAalfn0H8hJGZKXPeexCRTDm6uwv6YUjSA9Xnwt1y/Bgj5ZH8uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.82.0.tgz", + "integrity": "sha512-dsopxqtY5ZdyT9uLHyGt1SyiLop6hi7hWI3PKpePodkRQOkLaCm+OE4fR9CAz9qdfjiFO8531tX/QDyP/psjFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.82.0.tgz", + "integrity": "sha512-94Lu0SgTClKColU66g1VDuigV3HkcbkJBnTtZjGYfE8UPugaWDgKrm2icjC6HJVUYler2OXaHP/X0TBy8+CowQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.82.0.tgz", + "integrity": "sha512-hne/V06ewhh1i0w8+l7GDNROAGCGPmyFuOwiP7YTRu0JycyStJ4785dmF8xU5p0uUwt2emvIF9vc7Xjis+cJ0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.82.0.tgz", + "integrity": "sha512-aWY2xtbZf1LneW9Qsv/n2Sp8gOu74JrlQzEtj4coHX2SHFrCfhmAumaU+sI/A5nr+yoTRTSmI/pL2s6ADlNSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.82.0.tgz", + "integrity": "sha512-Fe+TtXCXMh/5f7kWlZ2VAwsMumZWtraFlKVk1NJlL52/beGwfDE7ov+/8gVirHzWokzGu7X65hSPq0ucPDskWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.82.0.tgz", + "integrity": "sha512-6azCZ6OJudlvipNttXCCQcyeFfcJ/NvUZdSN1z8elo73kCHtyQC7WTiUcSjWYvJ1jaq9KDUyMAoAS/vNzhBomA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.82.0.tgz", + "integrity": "sha512-PLEaSD8IAIIlwW4dwOd9YaxuxeOpwiXL4J24rcnE4iNtyM5j9Q9/3+gti08oXpx0u2ygNjRDx9xjWWpQonuJEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.82.0.tgz", + "integrity": "sha512-D94em/BwknNTn4vqxjHh5wb2oL566eFhArabqKIr0cNZMHOJuiraFp1A8tXpH05bbE5tqwEfLXTI0MWEGtn3Dw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.82.0.tgz", + "integrity": "sha512-MOprxBaoYU2D4VgxXCl3ghydThWtx7Um1lL51kGYNeQ5Al7WzsH7/tqGdNtbLrIWnjq3bsm13+nz/gRIxjrOXw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.82.0.tgz", + "integrity": "sha512-5h55QsfJ/luDXZzC20k6SNOY1Az+dCP9WvntKtcUWh2JhckAdwApY2ZusaBTwLENnReXU+A2fJtSrYvZJNKNPg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.82.0.tgz", + "integrity": "sha512-IE8NJNLlHr0CaXyGJPGVn0eTkUyoj1I2UfA8x7I4PSOYKsQ/6btVC7Pywrj5onk0cMH25r6Z38SoN3AvE5Zuog==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.82.0.tgz", + "integrity": "sha512-XUUUxaBo9XKl+J1B9EmP1cTGQPddzeURvoGkfwh/94PGnbW+hBprDljneoI2M1jzC1bzrIV3ihc7iM9UXl8+tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.82.0.tgz", + "integrity": "sha512-SWLSFulX9TDuH6yvbPYp4+VNn6jkkIvvI+KiujDM5rWBRHEfkesCC/pCneIIUr6ovkxZ5fRtpi2v5Cz5FrMJZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.82.0.tgz", + "integrity": "sha512-BQy35f6ZUdNr9a6c7B7orxQTcLjByGT2z3WAgmRovpRwmPYAaJ+NTplmMzhdjdJ4qSchfMNZy/Ukg+qRg6zseQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.82.0.tgz", + "integrity": "sha512-V4QhSTg5gctZue8RJjsGi7NpQPThr/p1/HfmiMC5kfe1KFEup9SQRVub4A6kijQjdHfxj7bLL1KO3QO7/5bwMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.82.0.tgz", + "integrity": "sha512-TUSCLaKB2yktpFAJ/r3HAUYsaV/3DT7JS4iNKyoh3a9YNwD0UG7Ezh4D8m23654vQcU6P/RQrCAjRPKe4peP/A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.82.0.tgz", + "integrity": "sha512-VTVoRIWJTb+wvUX8EYoPArfFH02whuR10goFXE/LHRRX33ajRrFgqbcONXZMiF4C5rnattfkm87HqYn8jb8hmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-6.0.0.tgz", + "integrity": "sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/cascader": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.22.0.tgz", + "integrity": "sha512-SffrA57aS9oub3VuI7ajPhJTPtaNxngSvtRhD40Rd8dwJ5vfWPSrVanWgeepdWFGBt7EHftIK5RUU0u3rCTwWw==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.10.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/checkbox": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz", + "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/collapse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz", + "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.1.tgz", + "integrity": "sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.1", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.2.tgz", + "integrity": "sha512-uiGpAlblCNlziHPwj4S4Iy/oemeuz/hR03mbiEjTCXwsqOIN3BOzsRMyDwpyO5Fm0vIEEJRUf9ZtbRLbhksuTA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dialog": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.10.0.tgz", + "integrity": "sha512-eDukNlz9vNszAGv7i3zKXdxEd3wgVmNxuJijYt8zvTh17QwTu8KK/bdURRd/lU4qaMzhO1HKKmMrwOnkaw0BvQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.3.3", + "@rc-component/portal": "^2.1.0", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/drawer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz", + "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.1.3", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dropdown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.3.tgz", + "integrity": "sha512-YTST/N6kpqpDz3IMuM/PSSZnrDpSOA6dgHv12gPA90ZTSLv2CoqkZ0+9NtwTY6BeO7dstPblSic2QJg7dSFy/g==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/@rc-component/form": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.8.6.tgz", + "integrity": "sha512-1EXVsSKPZC6kGrIqxVck7kAWM6T65b6et/n5wIcyiaIa41VrDshD0nVLHoBDEIZr2ATQsOP6icb4XQkNFMLBAw==", + "license": "MIT", + "dependencies": { + "@rc-component/async-validator": "^6.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/image": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.10.0.tgz", + "integrity": "sha512-BjeZCRQ+hw+4WAhvrw8rJvy5fckA2xpf/X2XQEOABUHvLTNB9inB98X3Mp54jYQ7g10DfWERQWHXeC4ylxp1Uw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/portal": "^2.1.2", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/input": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.3.1.tgz", + "integrity": "sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==", + "license": "MIT", + "dependencies": { + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/input-number": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz", + "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==", + "license": "MIT", + "dependencies": { + "@rc-component/mini-decimal": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/listy": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rc-component/listy/-/listy-1.2.3.tgz", + "integrity": "sha512-IXiMjV5s0rczLBlfh7G5nB4M3365mrEeedjwKtf5I+Ns3PqRUsebR2h5u8CeFarsVfLUPC2I5p0h09TNoOWyvQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.3.1", + "@rc-component/virtual-list": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/mentions": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.12.0.tgz", + "integrity": "sha512-v5MBx8zDcqCT+uybJgKpcx3Fgrvj0m3u+A/15RlSBaMx12lLoJZTTYggC3pbYYfohcKujWWUyBz6O6Gp2vGiNA==", + "license": "MIT", + "dependencies": { + "@rc-component/input": "~1.3.1", + "@rc-component/menu": "~1.5.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/menu": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.5.0.tgz", + "integrity": "sha512-RjvzPsZkwVEg3xu+/jDDqDeUCBJAnWylONFlGeQgg5enlNAnlpybqhaMGE/UPEIvr6hL90PGhjFBguJqj0Qb2Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz", + "integrity": "sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/motion": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.3.tgz", + "integrity": "sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz", + "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/notification": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.8.tgz", + "integrity": "sha512-MTRA3b8eHE14gh5R9nlXzbWZEDEBFVkEHj/FI1VOXw2zrSnSxqbuuU3drsq+k53e6MHwtegoFay1tnNYhtl61Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/overflow": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.1.tgz", + "integrity": "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/pagination": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.4.0.tgz", + "integrity": "sha512-CW1g7P9V8u+e8JQdUsl2RWg+GCsoee0mtJjZUCCxn/vb3jzOwDKm6hAdwddHCVBfWJ58eGUBZz3IvnU8rRktjw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/picker": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.12.2.tgz", + "integrity": "sha512-ZB1A+tzTQkgtR4flPrAMvxIAOelIIOOOsp22fU2Nad6PbZcJacnyGJUJqUH7ZS9mwxeUwurf1pLMKXrVsK1tlA==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/trigger": "^3.6.15", + "@rc-component/util": "^1.13.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/@rc-component/portal": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.1.tgz", + "integrity": "sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/progress": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.3.tgz", + "integrity": "sha512-Q1nPLIvKr95el/q76yaBh5nv1hqCsWaHQb8U7vXEDMefEhHgM7n3CPoN+o4LHnTyANQHmMFgoS/naAT4Bhxuaw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-2.0.0.tgz", + "integrity": "sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/rate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz", + "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/resize-observer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz", + "integrity": "sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/segmented": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz", + "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/select": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.10.1.tgz", + "integrity": "sha512-H+yQsl+qED9NilQ3g6zdpsMwUgwVjrcMTkNHAWRVU/MoNCYgTbDgU+MIMgZDK+rVdd2JUfI/MkysMcZZ0cyQKw==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/slider": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.1.1.tgz", + "integrity": "sha512-LSzgWGYDgeCDgR4r1XlU29gbYws6HpLnvJd/uMhLeW/vQgxldeR+Wb4uzHDCHiYEbr1bnEHWdjkPxjJRHxuiig==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/steps": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.3.tgz", + "integrity": "sha512-/b9gHcvDGjTDWJZW7+1kKFvzs0jrEeQ8mFvvIgX+h7xNT0FsOYODJYKjyJR4fPbvMBCfCOEqaBK66HBxov5BQA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz", + "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/table": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.11.1.tgz", + "integrity": "sha512-OWdS6DMmeWb7bJBGqPxYZpQbzBlBiXZUu2sqo6Ii7Sjs9GeK1IsrXrWk26SL2c6KEseabswdxrRj7WUm9LdECw==", + "license": "MIT", + "dependencies": { + "@rc-component/context": "^2.0.1", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tabs": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.13.0.tgz", + "integrity": "sha512-3FXr+9ZII8tFLWuDWrgjRaxZd2mazkKJAGRFEfVmp4r1qnBglOlxQ78l54Td1aj6/iFaaEReKSAF2HToztU5uw==", + "license": "MIT", + "dependencies": { + "@rc-component/dropdown": "~1.0.0", + "@rc-component/menu": "~1.5.0", + "@rc-component/motion": "^1.1.3", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tooltip": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.5.2.tgz", + "integrity": "sha512-VLq4BclU3LgRe46g2hTr2bdsM8tFYs7dVwpuD88rPNNg4lCGe8mBSS4FiRzYzS+ffnZ46y4SI8KYKo9K7qBnhw==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.10.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.4.0.tgz", + "integrity": "sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg==", + "license": "MIT", + "dependencies": { + "@rc-component/portal": "^2.2.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.7.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tree": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.4.0.tgz", + "integrity": "sha512-dGsJGDJQedA0BqqVgj3F8BvHXTSZijyhTXdbAdkcx8lynzZkty/CV3Z3LOm/fxz+BCfl3dfGiAQpb7Q5XNvl0Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/tree-select": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.16.1.tgz", + "integrity": "sha512-a1Oi6EJhqAhdOxxupdJi6fP0RPHMKn5TcfkX2+llaQ4lF4nwfH7b6SCHcnsybaa2s+pk1yZYwVyeOYkDnEBRdg==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.10.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/trigger": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.10.1.tgz", + "integrity": "sha512-mXlDN0IXdtV8Yqqm8195ECCyrbmfvvfKvwVvSlH0+qvKD6BUF8gRhEjSy0FOcD1+CcDRHgTiX99LoxfQrmh3Cw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.3.3", + "@rc-component/portal": "^2.2.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/upload": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.1.tgz", + "integrity": "sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/util": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.13.0.tgz", + "integrity": "sha512-IXf2zBfZrbQGT+G8LxnoICDBGnMHIPIiq5HCRn3EDfKUx28x1rnbZBj46ULJf4sM8ImTG1SepqfNBdxZziqQ4w==", + "license": "MIT", + "dependencies": { + "is-mobile": "^5.0.0", + "react-is": "^19.2.7" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.5.1.tgz", + "integrity": "sha512-boqHxdtyWC88u8quYgEO49bcBy5fzRiOcnBge+N4nLzs2k8hUQ/yw7JE9dM6yCBE4jSm5YSHVCVMS+suBuJGKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^8.0.0", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list/node_modules/@babel/runtime": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-8.0.0.tgz", + "integrity": "sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==", + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", + "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", + "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", + "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", + "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", + "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", + "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", + "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", + "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", + "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", + "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", + "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", + "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", + "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", + "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", + "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.1.tgz", + "integrity": "sha512-oMDTC3oA+6CXSO2JZnvOI7CA6oVub6kij5ggk9ohwye5slmkwxYDXcPOVxgMw/RQlticjtO0C1RZkR97HgrWMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=22", + "npm": ">=6", + "yarn": ">=1" + }, + "peerDependencies": { + "@testing-library/dom": ">=10 <11", + "vitest": ">= 0.32" + }, + "peerDependenciesMeta": { + "vitest": { + "optional": true + } + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz", + "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.1.tgz", + "integrity": "sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.145.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.0.tgz", + "integrity": "sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.0", + "estree-walker": "^3.0.3", + "magic-string": "^1.2.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/spy": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.0.tgz", + "integrity": "sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.6.3.tgz", + "integrity": "sha512-USnOxcOhWEQc65pHapSiCrZOijWef783XnqYZv8fuYIcYVoFxNxl6TsnsUhmoJyZheD/XwAx9CyO+hlyekDCQA==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/cssinjs": "^2.1.2", + "@ant-design/cssinjs-utils": "^2.1.2", + "@ant-design/fast-color": "^3.0.1", + "@ant-design/icons": "^6.3.4", + "@ant-design/react-slick": "~2.0.0", + "@babel/runtime": "^7.29.2", + "@rc-component/cascader": "~1.22.0", + "@rc-component/checkbox": "~2.0.0", + "@rc-component/collapse": "~1.2.0", + "@rc-component/color-picker": "~3.1.1", + "@rc-component/dialog": "~1.10.0", + "@rc-component/drawer": "~1.4.2", + "@rc-component/dropdown": "~1.0.3", + "@rc-component/form": "~1.8.6", + "@rc-component/image": "~1.10.0", + "@rc-component/input": "~1.3.1", + "@rc-component/input-number": "~1.6.2", + "@rc-component/listy": "~1.2.3", + "@rc-component/mentions": "~1.12.0", + "@rc-component/menu": "~1.5.0", + "@rc-component/motion": "^1.3.3", + "@rc-component/mutate-observer": "^2.0.1", + "@rc-component/notification": "~2.0.8", + "@rc-component/pagination": "~1.4.0", + "@rc-component/picker": "~1.12.2", + "@rc-component/progress": "~1.0.3", + "@rc-component/qrcode": "~2.0.0", + "@rc-component/rate": "~1.0.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/segmented": "~1.3.0", + "@rc-component/select": "~1.10.1", + "@rc-component/slider": "~1.1.1", + "@rc-component/steps": "~1.2.3", + "@rc-component/switch": "~1.0.3", + "@rc-component/table": "~1.11.1", + "@rc-component/tabs": "~1.13.0", + "@rc-component/tooltip": "~1.5.2", + "@rc-component/tour": "~2.4.0", + "@rc-component/tree": "~1.4.0", + "@rc-component/tree-select": "~1.16.1", + "@rc-component/trigger": "^3.10.1", + "@rc-component/upload": "~1.1.1", + "@rc-component/util": "^1.13.0", + "clsx": "^2.1.1", + "dayjs": "^1.11.11", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bidi-js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", + "license": "MIT" + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/entities": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-mobile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz", + "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==", + "license": "MIT" + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/jsdom": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz", + "integrity": "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^6.0.5", + "@asamuzakjp/dom-selector": "^8.3.0", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.7", + "@exodus/bytes": "^1.15.1", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.5.2", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.2", + "undici": "^8.9.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^17.1.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" + }, + "peerDependencies": { + "canvas": "^3.2.3" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", + "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/oxlint": { + "version": "1.82.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.82.0.tgz", + "integrity": "sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.82.0", + "@oxlint/binding-android-arm64": "1.82.0", + "@oxlint/binding-darwin-arm64": "1.82.0", + "@oxlint/binding-darwin-x64": "1.82.0", + "@oxlint/binding-freebsd-x64": "1.82.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.82.0", + "@oxlint/binding-linux-arm-musleabihf": "1.82.0", + "@oxlint/binding-linux-arm64-gnu": "1.82.0", + "@oxlint/binding-linux-arm64-musl": "1.82.0", + "@oxlint/binding-linux-ppc64-gnu": "1.82.0", + "@oxlint/binding-linux-riscv64-gnu": "1.82.0", + "@oxlint/binding-linux-riscv64-musl": "1.82.0", + "@oxlint/binding-linux-s390x-gnu": "1.82.0", + "@oxlint/binding-linux-x64-gnu": "1.82.0", + "@oxlint/binding-linux-x64-musl": "1.82.0", + "@oxlint/binding-openharmony-arm64": "1.82.0", + "@oxlint/binding-win32-arm64-msvc": "1.82.0", + "@oxlint/binding-win32-ia32-msvc": "1.82.0", + "@oxlint/binding-win32-x64-msvc": "1.82.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2001", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-is": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.8.tgz", + "integrity": "sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==", + "license": "MIT" + }, + "node_modules/react-router": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.3.tgz", + "integrity": "sha512-gyXgtdr5uACJ5b1Q4udzjVV+tb/rlHIMJKuJ0e89R4Kzgz47z/rgP0dIKxktqIEUhDHluGTPJJH/wRha7CyqsA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.3.tgz", + "integrity": "sha512-ytVbyBBM7vMfRCam25r0WMhSVSom909A8p+8m0/f1w853dz/xfFu6etAT2SEbVoSnI+ZoPRDqIsQXVT89gp7kg==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.3" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", + "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.148.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.7", + "@rolldown/binding-android-arm64": "1.2.7", + "@rolldown/binding-darwin-arm64": "1.2.7", + "@rolldown/binding-darwin-x64": "1.2.7", + "@rolldown/binding-freebsd-x64": "1.2.7", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", + "@rolldown/binding-linux-arm64-gnu": "1.2.7", + "@rolldown/binding-linux-arm64-musl": "1.2.7", + "@rolldown/binding-linux-ppc64-gnu": "1.2.7", + "@rolldown/binding-linux-s390x-gnu": "1.2.7", + "@rolldown/binding-linux-x64-gnu": "1.2.7", + "@rolldown/binding-linux-x64-musl": "1.2.7", + "@rolldown/binding-openharmony-arm64": "1.2.7", + "@rolldown/binding-win32-arm64-msvc": "1.2.7", + "@rolldown/binding-win32-x64-msvc": "1.2.7" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tinybench": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", + "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tldts": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.12.tgz", + "integrity": "sha512-WylhSDKVeYnWXL3a+vKTaOxjnOeEGw938hImY8zoRWJjRRK/Jp1K+IihBzIONpUmW4e3WmXT6q5FW6vlESVZCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.12" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.12.tgz", + "integrity": "sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz", + "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz", + "integrity": "sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.0", + "chai": "^6.2.2", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", + "tinyglobby": "^0.2.17", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.0", + "@vitest/browser-preview": "5.0.0", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.0", + "@vitest/coverage-v8": "5.0.0", + "@vitest/ui": "5.0.0", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz", + "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.15.1", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^22.14.0 || >=24.0.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..85ee0cc --- /dev/null +++ b/web/package.json @@ -0,0 +1,33 @@ +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "oxlint", + "preview": "vite preview", + "test": "vitest run" + }, + "dependencies": { + "@ant-design/icons": "^6.3.4", + "antd": "^6.6.3", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.3" + }, + "devDependencies": { + "@testing-library/jest-dom": "^7.0.1", + "@testing-library/react": "^16.3.3", + "@types/node": "^24.13.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitejs/plugin-react": "^6.1.0", + "jsdom": "^30.0.1", + "oxlint": "^1.79.0", + "typescript": "~6.0.2", + "vite": "^8.2.2", + "vitest": "^5.0.0" + } +} diff --git a/web/public/assets/brand/logo-mark.svg b/web/public/assets/brand/logo-mark.svg new file mode 100644 index 0000000..6224c28 --- /dev/null +++ b/web/public/assets/brand/logo-mark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/web/public/assets/illustrations/empty-chat.svg b/web/public/assets/illustrations/empty-chat.svg new file mode 100644 index 0000000..9f3eac4 --- /dev/null +++ b/web/public/assets/illustrations/empty-chat.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/web/public/assets/illustrations/empty-data.svg b/web/public/assets/illustrations/empty-data.svg new file mode 100644 index 0000000..d31e86f --- /dev/null +++ b/web/public/assets/illustrations/empty-data.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/web/public/assets/illustrations/login-finance.svg b/web/public/assets/illustrations/login-finance.svg new file mode 100644 index 0000000..5a4278f --- /dev/null +++ b/web/public/assets/illustrations/login-finance.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/web/public/favicon.svg b/web/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/web/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/icons.svg b/web/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/web/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/src/App.css b/web/src/App.css new file mode 100644 index 0000000..f90339d --- /dev/null +++ b/web/src/App.css @@ -0,0 +1,184 @@ +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/web/src/App.tsx b/web/src/App.tsx new file mode 100644 index 0000000..4b80a88 --- /dev/null +++ b/web/src/App.tsx @@ -0,0 +1,67 @@ +import { Navigate, Route, Routes } from 'react-router-dom' +import { AppLayout } from './layouts/AppLayout' +import { LoginPage } from './pages/login/LoginPage' +import { RoleHomePage } from './pages/home/RoleHomePage' +import { PlaceholderPage } from './pages/PlaceholderPage' +import { loadAuth } from './stores/authStore' +import { AgentBanner } from './components/AgentBanner' + +function RequireAuth({ children }: { children: React.ReactNode }) { + const auth = loadAuth() + if (!auth) return + return children +} + +export default function AppRoutes() { + const auth = loadAuth() + + return ( + + : } /> + + + + } + > + } /> + } /> + + } /> + } /> + } /> + } /> + + } /> + + + + + } + /> + + } /> + + + + + } + /> + + } /> + } /> + + } /> + + } /> + + ) +} diff --git a/web/src/api/auth.ts b/web/src/api/auth.ts new file mode 100644 index 0000000..7d2e3d9 --- /dev/null +++ b/web/src/api/auth.ts @@ -0,0 +1,16 @@ +import { apiFetch } from './client' + +export type LoginResponse = { + access_token: string + expires_in: number + sub: string + roles: string[] + token_type?: string +} + +export async function login(actorId: string, tokenType: 'customer' | 'staff') { + return apiFetch('/api/auth/login', { + method: 'POST', + body: JSON.stringify({ actor_id: actorId, token_type: tokenType }), + }) +} diff --git a/web/src/api/client.ts b/web/src/api/client.ts new file mode 100644 index 0000000..be48a5d --- /dev/null +++ b/web/src/api/client.ts @@ -0,0 +1,55 @@ +export class ApiError extends Error { + errorCode: string + traceId: string + requestId: string + + constructor(message: string, errorCode: string, traceId = '', requestId = '') { + super(message) + this.name = 'ApiError' + this.errorCode = errorCode + this.traceId = traceId + this.requestId = requestId + } +} + +type ApiEnvelope = { + code?: number + message?: string + data?: T + trace_id?: string + error_code?: string + request_id?: string +} + +export async function apiFetch( + path: string, + init: RequestInit & { token?: string | null } = {}, +): Promise<{ data: T; traceId: string }> { + const headers = new Headers(init.headers) + headers.set('Accept', 'application/json') + if (init.body && !headers.has('Content-Type')) { + headers.set('Content-Type', 'application/json') + } + if (init.token) { + headers.set('Authorization', `Bearer ${init.token}`) + } + + const resp = await fetch(path, { ...init, headers }) + const traceId = resp.headers.get('X-Trace-Id') ?? '' + const json = (await resp.json()) as ApiEnvelope & Record + + if (!resp.ok) { + throw new ApiError( + (json.message as string) || resp.statusText, + (json.error_code as string) || 'HTTP_ERROR', + (json.trace_id as string) || traceId, + (json.request_id as string) || '', + ) + } + + if (json.code !== undefined && json.code !== 0 && json.code !== 200) { + throw new ApiError(json.message || 'request failed', 'API_ERROR', json.trace_id || traceId) + } + + return { data: (json.data ?? json) as T, traceId: json.trace_id || traceId } +} diff --git a/web/src/assets/hero.png b/web/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/web/src/assets/hero.png differ diff --git a/web/src/assets/react.svg b/web/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/web/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/vite.svg b/web/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/web/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/web/src/components/AgentBanner.tsx b/web/src/components/AgentBanner.tsx new file mode 100644 index 0000000..91aeae4 --- /dev/null +++ b/web/src/components/AgentBanner.tsx @@ -0,0 +1,11 @@ +import { Alert } from 'antd' + +type AgentBannerProps = { + message?: string +} + +export function AgentBanner({ + message = '当前 Agent 业务 Tool 仍在接入中,可先使用通用对话;查数/明细能力以权限与后端返回为准。', +}: AgentBannerProps) { + return +} diff --git a/web/src/components/ApiErrorResult.tsx b/web/src/components/ApiErrorResult.tsx new file mode 100644 index 0000000..9caafe3 --- /dev/null +++ b/web/src/components/ApiErrorResult.tsx @@ -0,0 +1,32 @@ +import { Button, Result, Typography } from 'antd' +import type { ApiError } from '../api/client' + +type ApiErrorResultProps = { + error: ApiError | Error + onRetry?: () => void +} + +export function ApiErrorResult({ error, onRetry }: ApiErrorResultProps) { + const api = error instanceof Error && 'errorCode' in error ? (error as ApiError) : null + return ( + + {api?.traceId && ( + + trace_id: {api.traceId} + + )} + {onRetry && ( + + )} + + } + /> + ) +} diff --git a/web/src/components/PageShell.tsx b/web/src/components/PageShell.tsx new file mode 100644 index 0000000..48b350d --- /dev/null +++ b/web/src/components/PageShell.tsx @@ -0,0 +1,36 @@ +import { Breadcrumb, Typography } from 'antd' +import type { ReactNode } from 'react' +import { Link } from 'react-router-dom' + +type Crumb = { title: string; path?: string } + +type PageShellProps = { + title: string + crumbs?: Crumb[] + extra?: ReactNode + alert?: ReactNode + children: ReactNode +} + +export function PageShell({ title, crumbs = [], extra, alert, children }: PageShellProps) { + return ( +
+ {crumbs.length > 0 && ( + ({ + title: c.path ? {c.title} : c.title, + }))} + /> + )} +
+ + {title} + + {extra} +
+ {alert} + {children} +
+ ) +} diff --git a/web/src/config/demoAccounts.ts b/web/src/config/demoAccounts.ts new file mode 100644 index 0000000..89223e7 --- /dev/null +++ b/web/src/config/demoAccounts.ts @@ -0,0 +1,48 @@ +export type DemoAccount = { + key: string + label: string + description: string + actorId: string + tokenType: 'customer' | 'staff' + defaultRoute: string + roleLabel: string +} + +export const DEMO_ACCOUNTS: DemoAccount[] = [ + { + key: 'customer', + label: '客户 Demo', + description: 'CUST-9527 · 客户财富线', + actorId: 'CUST-9527', + tokenType: 'customer', + defaultRoute: '#/app/customer/home', + roleLabel: '客户', + }, + { + key: 'advisor', + label: '理财师 Demo', + description: 'STAFF-10086 · 代理人助手', + actorId: 'STAFF-10086', + tokenType: 'staff', + defaultRoute: '#/app/advisor/customers', + roleLabel: '理财师', + }, + { + key: 'analyst', + label: '分析员 Demo', + description: 'STAFF-20001 · 数据分析', + actorId: 'STAFF-20001', + tokenType: 'staff', + defaultRoute: '#/app/analytics/query', + roleLabel: '分析员', + }, + { + key: 'risk', + label: '风控专员 Demo', + description: 'STAFF-30001 · 风控监测', + actorId: 'STAFF-30001', + tokenType: 'staff', + defaultRoute: '#/app/risk/alerts', + roleLabel: '风控', + }, +] diff --git a/web/src/index.css b/web/src/index.css new file mode 100644 index 0000000..5fb3313 --- /dev/null +++ b/web/src/index.css @@ -0,0 +1,111 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 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: 0.18px; + 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 (max-width: 1024px) { + 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: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +#root { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} diff --git a/web/src/layouts/AppLayout.tsx b/web/src/layouts/AppLayout.tsx new file mode 100644 index 0000000..e7f6bc6 --- /dev/null +++ b/web/src/layouts/AppLayout.tsx @@ -0,0 +1,106 @@ +import { + Layout, + Menu, + Button, + Space, + Tag, + Typography, + theme, +} from 'antd' +import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons' +import { useMemo, useState } from 'react' +import { Outlet, useLocation, useNavigate } from 'react-router-dom' +import { clearAuth, loadAuth, type AuthState } from '../stores/authStore' +import { buildMenuGroups, flattenMenuPaths, toAntdMenuItems } from '../routes/menus' + +const { Header, Sider, Content } = Layout + +type AppLayoutProps = { + auth: AuthState +} + +export function AppLayout({ auth }: AppLayoutProps) { + const [collapsed, setCollapsed] = useState(false) + const navigate = useNavigate() + const location = useLocation() + const { token } = theme.useToken() + + const menuGroups = useMemo(() => buildMenuGroups(auth.roleLabel), [auth.roleLabel]) + const menuItems = useMemo(() => toAntdMenuItems(menuGroups), [menuGroups]) + const allowedPaths = useMemo(() => flattenMenuPaths(menuGroups), [menuGroups]) + + const selectedKey = + allowedPaths.find((p) => location.pathname.startsWith(p)) ?? location.pathname + + const onMenuClick = ({ key }: { key: string }) => { + navigate(key) + } + + const logout = () => { + clearAuth() + navigate('/login') + } + + return ( + + +
+ + {!collapsed && ( + + JinRong + + )} +
+ + + +
+ + +
+ + + +
+ + ) +} + +export function useAppAuth(): AuthState { + const auth = loadAuth() + if (!auth) throw new Error('not authenticated') + return auth +} diff --git a/web/src/main.tsx b/web/src/main.tsx new file mode 100644 index 0000000..a8d4c8b --- /dev/null +++ b/web/src/main.tsx @@ -0,0 +1,18 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { ConfigProvider } from 'antd' +import zhCN from 'antd/locale/zh_CN' +import { HashRouter } from 'react-router-dom' +import App from './App' +import { appTheme } from './theme' +import './styles/global.css' + +createRoot(document.getElementById('root')!).render( + + + + + + + , +) diff --git a/web/src/pages/PlaceholderPage.tsx b/web/src/pages/PlaceholderPage.tsx new file mode 100644 index 0000000..6b32276 --- /dev/null +++ b/web/src/pages/PlaceholderPage.tsx @@ -0,0 +1,24 @@ +import { Empty } from 'antd' +import { PageShell } from '../components/PageShell' + +type PlaceholderPageProps = { + title: string + illustration?: string + description?: string +} + +export function PlaceholderPage({ + title, + illustration = '/assets/illustrations/empty-data.svg', + description = '功能开发中,等待 Agent / 平台能力接入。', +}: PlaceholderPageProps) { + return ( + + + + ) +} diff --git a/web/src/pages/home/RoleHomePage.tsx b/web/src/pages/home/RoleHomePage.tsx new file mode 100644 index 0000000..f1099c4 --- /dev/null +++ b/web/src/pages/home/RoleHomePage.tsx @@ -0,0 +1,48 @@ +import { Card, Col, Row, Statistic, Typography } from 'antd' +import { Link } from 'react-router-dom' +import { PageShell } from '../../components/PageShell' +import type { AuthState } from '../../stores/authStore' + +export function RoleHomePage({ auth }: { auth: AuthState }) { + const quickLinks: { title: string; path: string }[] = + auth.roleLabel === '客户' + ? [ + { title: '我的持仓', path: '/app/customer/holdings' }, + { title: '产品行情', path: '/app/market' }, + { title: '客户助手', path: '/app/customer/chat' }, + ] + : auth.roleLabel === '风控' + ? [ + { title: '预警台账', path: '/app/risk/alerts' }, + { title: '风控助手', path: '/app/risk/chat' }, + ] + : [{ title: '数据分析', path: '/app/analytics/query' }] + + return ( + + + + 演示} /> + + + + + + + + + + + + + + {quickLinks.map((q) => ( + + {q.title} + + ))} + + + + ) +} diff --git a/web/src/pages/login/LoginPage.tsx b/web/src/pages/login/LoginPage.tsx new file mode 100644 index 0000000..64e0107 --- /dev/null +++ b/web/src/pages/login/LoginPage.tsx @@ -0,0 +1,88 @@ +import { Button, Card, Space, Typography, message } from 'antd' +import { useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { login } from '../../api/auth' +import { ApiError } from '../../api/client' +import { DEMO_ACCOUNTS } from '../../config/demoAccounts' +import { matchDemoAccount, resolveRoleLabel, saveAuth } from '../../stores/authStore' + +export function LoginPage() { + const navigate = useNavigate() + const [loadingKey, setLoadingKey] = useState(null) + + const handleLogin = async (accountKey: string) => { + const account = DEMO_ACCOUNTS.find((a) => a.key === accountKey) + if (!account) return + setLoadingKey(accountKey) + try { + const { data } = await login(account.actorId, account.tokenType) + const demo = matchDemoAccount(data.sub) + saveAuth({ + accessToken: data.access_token, + actorId: data.sub, + roles: data.roles, + tokenType: account.tokenType, + roleLabel: demo?.roleLabel ?? resolveRoleLabel(data.roles), + defaultRoute: demo?.defaultRoute.replace('#', '') ?? '/app/home', + }) + message.success(`欢迎,${demo?.roleLabel ?? data.sub}`) + navigate(demo?.defaultRoute.replace('#', '') ?? '/app/home') + } catch (err) { + const msg = err instanceof ApiError ? `${err.errorCode}: ${err.message}` : '登录失败' + message.error(msg) + } finally { + setLoadingKey(null) + } + } + + return ( +
+
+
+ + +
+ + JinRong 智能管家 + + 四 Agent · 代销平台 · 演示环境 +
+
+ 金融数据与资产守护示意 + + 稳重金融工作台风格 · 平台读 API 真接 · Agent 能力逐步接入 + +
+
+
+ + + {DEMO_ACCOUNTS.map((acc) => ( + + ))} + + + Mock JWT · 联调本机 FastAPI :8000 · 仅供开发演示 + + +
+
+ ) +} diff --git a/web/src/routes/menus.tsx b/web/src/routes/menus.tsx new file mode 100644 index 0000000..47caa69 --- /dev/null +++ b/web/src/routes/menus.tsx @@ -0,0 +1,127 @@ +import { + AlertOutlined, + BarChartOutlined, + CommentOutlined, + HomeOutlined, + LineChartOutlined, + TeamOutlined, + UserOutlined, + WalletOutlined, +} from '@ant-design/icons' +import type { MenuProps } from 'antd' +import type { ReactNode } from 'react' + +export type RouteMenuLeaf = { + key: string + label: string + icon: ReactNode + path: string +} + +export type RouteMenuGroup = { + type: 'group' + label: string + children: RouteMenuLeaf[] +} + +const sharedPlatform: RouteMenuLeaf[] = [ + { key: '/app/market', icon: , label: '产品行情', path: '/app/market' }, + { key: '/app/analytics/query', icon: , label: '数据分析', path: '/app/analytics/query' }, +] + +export function buildMenuGroups(roleLabel: string): RouteMenuGroup[] { + switch (roleLabel) { + case '客户': + return [ + { + type: 'group', + label: '工作台', + children: [ + { key: '/app/customer/home', icon: , label: '首页', path: '/app/customer/home' }, + { key: '/app/customer/profile', icon: , label: '我的档案', path: '/app/customer/profile' }, + { key: '/app/customer/holdings', icon: , label: '我的持仓', path: '/app/customer/holdings' }, + ], + }, + { + type: 'group', + label: '智能 Agent', + children: [ + { key: '/app/customer/chat', icon: , label: '客户助手', path: '/app/customer/chat' }, + ], + }, + { type: 'group', label: '平台服务', children: sharedPlatform }, + ] + case '理财师': + return [ + { + type: 'group', + label: '工作台', + children: [ + { key: '/app/advisor/customers', icon: , label: '名下客户', path: '/app/advisor/customers' }, + ], + }, + { + type: 'group', + label: '智能 Agent', + children: [ + { key: '/app/advisor/chat', icon: , label: '顾问助手', path: '/app/advisor/chat' }, + ], + }, + { type: 'group', label: '平台服务', children: sharedPlatform }, + ] + case '分析员': + return [ + { + type: 'group', + label: '工作台', + children: [ + { key: '/app/analytics/query', icon: , label: '问数工作台', path: '/app/analytics/query' }, + { key: '/app/analytics/chat', icon: , label: '分析对话', path: '/app/analytics/chat' }, + ], + }, + { + type: 'group', + label: '平台服务', + children: [{ key: '/app/market', icon: , label: '产品行情', path: '/app/market' }], + }, + ] + case '风控': + return [ + { + type: 'group', + label: '工作台', + children: [ + { key: '/app/risk/alerts', icon: , label: '预警台账', path: '/app/risk/alerts' }, + ], + }, + { + type: 'group', + label: '智能 Agent', + children: [ + { key: '/app/risk/chat', icon: , label: '风控助手', path: '/app/risk/chat' }, + ], + }, + { type: 'group', label: '平台服务', children: sharedPlatform }, + ] + default: + return [ + { + type: 'group', + label: '工作台', + children: [{ key: '/app/home', icon: , label: '首页', path: '/app/home' }], + }, + ] + } +} + +export function toAntdMenuItems(groups: RouteMenuGroup[]): MenuProps['items'] { + return groups.map((g) => ({ + type: 'group' as const, + label: g.label, + children: g.children.map((c) => ({ key: c.key, icon: c.icon, label: c.label })), + })) +} + +export function flattenMenuPaths(groups: RouteMenuGroup[]): string[] { + return groups.flatMap((g) => g.children.map((c) => c.path)) +} diff --git a/web/src/stores/authStore.ts b/web/src/stores/authStore.ts new file mode 100644 index 0000000..fd03859 --- /dev/null +++ b/web/src/stores/authStore.ts @@ -0,0 +1,42 @@ +import { DEMO_ACCOUNTS } from '../config/demoAccounts' + +const STORAGE_KEY = 'jinrong.auth' + +export type AuthState = { + accessToken: string + actorId: string + roles: string[] + tokenType: 'customer' | 'staff' + roleLabel: string + defaultRoute: string +} + +export function loadAuth(): AuthState | null { + const raw = localStorage.getItem(STORAGE_KEY) + if (!raw) return null + try { + return JSON.parse(raw) as AuthState + } catch { + return null + } +} + +export function saveAuth(state: AuthState) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(state)) +} + +export function clearAuth() { + localStorage.removeItem(STORAGE_KEY) +} + +export function resolveRoleLabel(roles: string[]): string { + if (roles.includes('customer')) return '客户' + if (roles.includes('risk_officer')) return '风控' + if (roles.includes('advisor')) return '理财师' + if (roles.includes('analyst')) return '分析员' + return roles[0] ?? '用户' +} + +export function matchDemoAccount(actorId: string) { + return DEMO_ACCOUNTS.find((a) => a.actorId === actorId) +} diff --git a/web/src/styles/global.css b/web/src/styles/global.css new file mode 100644 index 0000000..a0973c8 --- /dev/null +++ b/web/src/styles/global.css @@ -0,0 +1,48 @@ +html, +body, +#root { + margin: 0; + min-height: 100%; +} + +body { + background: #f5f5f5; +} + +.tabular-nums { + font-variant-numeric: tabular-nums; +} + +.login-hero-wrap { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + background: linear-gradient(135deg, #e6f4ff 0%, #f0f5ff 55%, #ffffff 100%); +} + +.login-hero-art { + max-width: 420px; + width: 100%; + padding: 48px; +} + +.login-panel { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: #fff; + padding: 48px 32px; +} + +.chat-stream-cursor::after { + content: '▍'; + animation: blink 1s step-start infinite; +} + +@keyframes blink { + 50% { + opacity: 0; + } +} diff --git a/web/src/theme.ts b/web/src/theme.ts new file mode 100644 index 0000000..e954c7f --- /dev/null +++ b/web/src/theme.ts @@ -0,0 +1,21 @@ +import type { ThemeConfig } from 'antd' + +export const appTheme: ThemeConfig = { + token: { + colorPrimary: '#1677ff', + borderRadius: 6, + fontSize: 14, + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif', + }, + components: { + Layout: { + headerBg: '#ffffff', + siderBg: '#ffffff', + bodyBg: '#f5f5f5', + }, + Table: { + headerBg: '#fafafa', + }, + }, +} diff --git a/web/tsconfig.app.json b/web/tsconfig.app.json new file mode 100644 index 0000000..6830b6f --- /dev/null +++ b/web/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023", "DOM"], + "module": "esnext", + "types": ["vite/client"], + "allowArbitraryExtensions": true, + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/web/tsconfig.node.json b/web/tsconfig.node.json new file mode 100644 index 0000000..8455dcb --- /dev/null +++ b/web/tsconfig.node.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2023", + "lib": ["ES2023"], + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "module": "nodenext", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..50be89f --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,18 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + plugins: [react()], + server: { + port: 5173, + proxy: { + '/api': { + target: 'http://127.0.0.1:8000', + changeOrigin: true, + }, + }, + }, + test: { + environment: 'jsdom', + }, +})