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 @@
+
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
+
+ )}
+
+
+
+
+
+ : }
+ onClick={() => setCollapsed((v) => !v)}
+ />
+
+ {auth.roleLabel}
+ {auth.actorId}
+
+
+
+
+
+
+
+
+ )
+}
+
+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',
+ },
+})