From b1dec691e9f4c4e6507c0bf0df76c3c4173b1d77 Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Tue, 2 Jun 2026 23:17:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor(web):=20=E5=89=8D=E7=AB=AF=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E9=87=8D=E6=9E=84=20=E2=80=94=20consoles/pages=20?= =?UTF-8?q?=E2=86=92=20layouts/features=20+=20shared?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - consoles/admin/ → layouts/admin-layout/ - consoles/workbench/ → layouts/workbench-layout/ + features/chat/ - pages/ → features/ (dashboard, models, projects, not-found) - components/ → shared/components/ - hooks/ → shared/hooks/ - utils/ → shared/utils/ - 更新所有 import 路径 (src/web/ + tests/web/) - 更新开发文档 (README.md, frontend.md, architecture.md) --- docs/development/README.md | 146 ++++++++++++++++-- docs/development/architecture.md | 3 + docs/development/frontend.md | 77 +++++---- .../pages => features/chat}/ChatPage.tsx | 10 +- .../chat/ChatPanel.tsx | 6 +- .../chat/ChatScrollArea.tsx | 2 +- .../chat/parts/CodeBlockWithCopy.tsx | 2 +- .../chat/parts/ReasoningPart.tsx | 0 .../chat/parts/TextPart.tsx | 2 +- .../chat/parts/ToolPart.tsx | 0 .../chat/parts/types.ts | 0 .../chat/use-chat-scroll.ts | 0 .../{pages => features}/dashboard/index.tsx | 2 +- .../models/components/ModelFormModal.tsx | 0 .../models/components/ModelTable.tsx | 0 .../models/components/ModelsToolbar.tsx | 0 .../models/components/ProviderFormModal.tsx | 0 .../models/components/ProviderTable.tsx | 0 src/web/{pages => features}/models/index.tsx | 4 +- .../404 => features/not-found}/index.tsx | 0 .../projects/components/ProjectFormModal.tsx | 0 .../projects/components/ProjectTable.tsx | 0 .../projects/components/ProjectToolbar.tsx | 0 .../{pages => features}/projects/index.tsx | 2 +- .../admin-layout}/AdminConsoleLayout.tsx | 2 +- .../admin => layouts/admin-layout}/menu.tsx | 0 .../workbench-layout}/ProjectContext.tsx | 0 .../workbench-layout}/ProjectContextValue.ts | 0 .../WorkbenchConsoleLayout.tsx | 2 +- .../WorkbenchProjectGate.tsx | 2 +- .../workbench-layout}/routes.ts | 0 .../workbench-layout}/useCurrentProject.ts | 0 src/web/main.tsx | 4 +- src/web/routes.tsx | 14 +- .../components/ConsoleShell/ConsoleOutlet.tsx | 0 .../components/ConsoleShell/ConsoleShell.tsx | 2 +- .../components/ConsoleShell/types.ts | 2 +- .../{ => shared}/components/ErrorBoundary.tsx | 0 .../{ => shared}/components/Sidebar/index.tsx | 2 +- .../{ => shared}/hooks/use-conversations.ts | 2 +- src/web/{ => shared}/hooks/use-is-dark.ts | 0 src/web/{ => shared}/hooks/use-logger.ts | 0 src/web/{ => shared}/hooks/use-meta.ts | 2 +- src/web/{ => shared}/hooks/use-models.ts | 2 +- src/web/{ => shared}/hooks/use-projects.ts | 2 +- src/web/{ => shared}/hooks/use-providers.ts | 2 +- .../hooks/use-sidebar-collapsed.ts | 0 .../hooks/use-theme-preference.ts | 0 src/web/{ => shared}/utils/api.ts | 0 src/web/{ => shared}/utils/logger.ts | 0 src/web/{ => shared}/utils/time.ts | 0 tests/web/components/ChatPanel.test.tsx | 2 +- tests/web/components/ErrorBoundary.test.tsx | 2 +- tests/web/components/ModelTable.test.tsx | 2 +- tests/web/components/ProviderTable.test.tsx | 2 +- tests/web/components/Sidebar/index.test.tsx | 4 +- .../chat/CodeBlockWithCopy.test.tsx | 2 +- .../components/chat/ReasoningPart.test.tsx | 2 +- tests/web/components/chat/ToolPart.test.tsx | 2 +- .../components/query-client-logging.test.tsx | 2 +- tests/web/hooks/on-success-logging.test.tsx | 6 +- tests/web/hooks/use-chat-scroll.test.ts | 2 +- tests/web/hooks/use-logger.test.ts | 4 +- tests/web/hooks/use-models.test.ts | 2 +- tests/web/hooks/use-projects.test.ts | 2 +- tests/web/hooks/use-providers.test.ts | 2 +- tests/web/hooks/use-sidebar-collapsed.test.ts | 2 +- tests/web/hooks/use-theme-preference.test.ts | 2 +- tests/web/routes/404.test.tsx | 2 +- tests/web/routes/dashboard.test.tsx | 2 +- tests/web/routes/models.test.tsx | 4 +- tests/web/routes/projects.test.tsx | 4 +- tests/web/test-utils.tsx | 2 +- tests/web/utils/api.test.ts | 2 +- tests/web/utils/logger.test.ts | 4 +- tests/web/utils/time.test.ts | 2 +- 76 files changed, 249 insertions(+), 111 deletions(-) rename src/web/{consoles/workbench/pages => features/chat}/ChatPage.tsx (90%) rename src/web/{consoles/workbench/components => features}/chat/ChatPanel.tsx (98%) rename src/web/{consoles/workbench/components => features}/chat/ChatScrollArea.tsx (96%) rename src/web/{consoles/workbench/components => features}/chat/parts/CodeBlockWithCopy.tsx (97%) rename src/web/{consoles/workbench/components => features}/chat/parts/ReasoningPart.tsx (100%) rename src/web/{consoles/workbench/components => features}/chat/parts/TextPart.tsx (94%) rename src/web/{consoles/workbench/components => features}/chat/parts/ToolPart.tsx (100%) rename src/web/{consoles/workbench/components => features}/chat/parts/types.ts (100%) rename src/web/{consoles/workbench/components => features}/chat/use-chat-scroll.ts (100%) rename src/web/{pages => features}/dashboard/index.tsx (95%) rename src/web/{pages => features}/models/components/ModelFormModal.tsx (100%) rename src/web/{pages => features}/models/components/ModelTable.tsx (100%) rename src/web/{pages => features}/models/components/ModelsToolbar.tsx (100%) rename src/web/{pages => features}/models/components/ProviderFormModal.tsx (100%) rename src/web/{pages => features}/models/components/ProviderTable.tsx (100%) rename src/web/{pages => features}/models/index.tsx (98%) rename src/web/{pages/404 => features/not-found}/index.tsx (100%) rename src/web/{pages => features}/projects/components/ProjectFormModal.tsx (100%) rename src/web/{pages => features}/projects/components/ProjectTable.tsx (100%) rename src/web/{pages => features}/projects/components/ProjectToolbar.tsx (100%) rename src/web/{pages => features}/projects/index.tsx (98%) rename src/web/{consoles/admin => layouts/admin-layout}/AdminConsoleLayout.tsx (65%) rename src/web/{consoles/admin => layouts/admin-layout}/menu.tsx (100%) rename src/web/{consoles/workbench => layouts/workbench-layout}/ProjectContext.tsx (100%) rename src/web/{consoles/workbench => layouts/workbench-layout}/ProjectContextValue.ts (100%) rename src/web/{consoles/workbench => layouts/workbench-layout}/WorkbenchConsoleLayout.tsx (92%) rename src/web/{consoles/workbench => layouts/workbench-layout}/WorkbenchProjectGate.tsx (95%) rename src/web/{consoles/workbench => layouts/workbench-layout}/routes.ts (100%) rename src/web/{consoles/workbench => layouts/workbench-layout}/useCurrentProject.ts (100%) rename src/web/{ => shared}/components/ConsoleShell/ConsoleOutlet.tsx (100%) rename src/web/{ => shared}/components/ConsoleShell/ConsoleShell.tsx (98%) rename src/web/{ => shared}/components/ConsoleShell/types.ts (76%) rename src/web/{ => shared}/components/ErrorBoundary.tsx (100%) rename src/web/{ => shared}/components/Sidebar/index.tsx (94%) rename src/web/{ => shared}/hooks/use-conversations.ts (98%) rename src/web/{ => shared}/hooks/use-is-dark.ts (100%) rename src/web/{ => shared}/hooks/use-logger.ts (100%) rename src/web/{ => shared}/hooks/use-meta.ts (88%) rename src/web/{ => shared}/hooks/use-models.ts (99%) rename src/web/{ => shared}/hooks/use-projects.ts (99%) rename src/web/{ => shared}/hooks/use-providers.ts (99%) rename src/web/{ => shared}/hooks/use-sidebar-collapsed.ts (100%) rename src/web/{ => shared}/hooks/use-theme-preference.ts (100%) rename src/web/{ => shared}/utils/api.ts (100%) rename src/web/{ => shared}/utils/logger.ts (100%) rename src/web/{ => shared}/utils/time.ts (100%) diff --git a/docs/development/README.md b/docs/development/README.md index 452122e..cf7cd99 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -37,20 +37,127 @@ AI 工具必须严格遵守以下全部约束。 ### 目录边界 -| 目录 | 约束 | -| ------------------------ | -------------------------------------------- | -| `src/server/` | 后端,禁止 import src/web/ | -| `src/server/db/` | 数据库层:schema、connection、migration、DAO | -| `src/server/ai/` | AI Provider Registry + Agent + 工具 | -| `src/server/config/` | 配置子系统:types、variables、issues、schema | -| `src/server/helpers/` | 跨路由工具:响应格式化、URL 拼接 | -| `src/server/middleware/` | 参数校验 + 错误处理中间件 | -| `src/web/` | 前端,禁止 import src/server/ 运行时实现 | -| `src/web/consoles/` | 控制台外壳(Admin / Workbench) | -| `src/shared/` | 前后端共享类型(api.ts)和常量(app.ts) | -| `scripts/` | 独立脚本,可 import 项目源码 | -| `drizzle/` | SQL migration 文件(开发期产出) | -| `tests/` | 测试目录,镜像 src/ 结构 | +| 目录 | 约束 | +| ------------------------ | --------------------------------------------------- | +| `src/server/` | 后端,禁止 import src/web/ | +| `src/server/db/` | 数据库层:schema、connection、migration、DAO | +| `src/server/ai/` | AI Provider Registry + Agent + 工具 | +| `src/server/config/` | 配置子系统:types、variables、issues、schema | +| `src/server/helpers/` | 跨路由工具:响应格式化、URL 拼接 | +| `src/server/middleware/` | 参数校验 + 错误处理中间件 | +| `src/web/` | 前端,禁止 import src/server/ 运行时实现 | +| `src/web/layouts/` | 布局组件(AdminLayout / WorkbenchLayout) | +| `src/web/features/` | 功能模块(dashboard / projects / models / chat 等) | +| `src/web/shared/` | 前端共享代码(components / hooks / utils / types) | +| `src/shared/` | 前后端共享类型(api.ts)和常量(app.ts) | +| `scripts/` | 独立脚本,可 import 项目源码 | +| `drizzle/` | SQL migration 文件(开发期产出) | +| `tests/` | 测试目录,镜像 src/ 结构 | + +### 前端目录使用规范 + +#### 目录职责定义 + +``` +src/web/ +├── main.tsx ← 入口(Provider 装配、全局初始化) +├── app.tsx ← App 组件(路由挂载) +├── routes.tsx ← 全局路由表(集中声明) +├── styles.css ← 全局样式 +├── menu.tsx ← 菜单配置类型定义 +├── layouts/ ← 布局组件 +│ ├── admin-layout/ ← Admin 布局 + 侧边栏 + 菜单配置 +│ └── workbench-layout/ ← Workbench 布局 + 项目上下文 + 入口守卫 +├── features/ ← 功能模块(核心目录) +│ ├── dashboard/ ← 仪表盘页面 + 私有组件/hooks +│ ├── projects/ ← 项目管理页面 + 私有组件/hooks +│ ├── models/ ← 模型管理页面 + 私有组件/hooks +│ ├── chat/ ← 聊天页面 + 私有组件/hooks +│ └── <新增功能>/ ← 新功能直接新增目录 +├── shared/ ← 跨 feature 共享代码 +│ ├── components/ ← 通用 UI 组件(ErrorBoundary, Sidebar, ConsoleShell 等) +│ ├── hooks/ ← 通用 hooks(use-logger, use-theme-preference, use-sidebar-collapsed 等) +│ ├── utils/ ← 通用工具函数(api, logger, time) +│ └── types.ts ← 前端内部共享类型 +└── css.d.ts ← CSS 模块类型声明 +``` + +#### 依赖规则 + +```text +layouts/ → shared/ ✅ 布局可使用共享代码 +features/* → shared/ ✅ 功能模块可使用共享代码 +features/* → layouts/ ❌ 功能模块不依赖布局(路由表负责组合) +features/A → features/B ❌ 功能模块间禁止直接依赖 +shared/ → features/* ❌ 共享代码不依赖功能模块 +``` + +#### feature 内部组织 + +每个 feature 目录自治,不强制统一内部结构,按需组织以下子目录: + +```text +features// +├── index.tsx ← 页面组件(必须,路由入口) +├── components/ ← 私有 UI 组件 +├── hooks/ ← 私有 hooks(如 use-conversations 仅 chat 使用) +├── utils/ ← 私有工具函数 +└── types.ts ← 私有类型定义 +``` + +- 只有 `index.tsx`(页面组件)是必需的,其他按实际复杂度按需创建。 +- feature 内部文件只在本 feature 内导入。被外部使用的必须提升到 `shared/`。 + +#### 组件归属判定规则 + +| 判定维度 | 放在 feature/ | 放在 shared/ | +| -------- | -------------------------------------------------- | -------------------------------------------- | +| 使用范围 | 仅一个功能模块使用 | 两个及以上功能模块使用 | +| 业务耦合 | 包含特定业务逻辑(如项目 CRUD 表单、聊天消息渲染) | 纯展示或通用交互(如 ErrorBoundary、侧边栏) | +| 数据依赖 | 依赖特定 API 或业务数据 | 无业务数据依赖或通过 props 注入 | +| 可替换性 | 替换需理解业务上下文 | 可直接复用于任何页面 | + +#### 组件升降级流程 + +**升级(feature → shared):** 当一个 feature 内的组件/hook/tool 同时满足以下条件时,应提升到 `shared/`: + +1. 至少被 2 个不同的 feature 或 layout 使用 +2. 已消除对原 feature 业务逻辑的直接依赖(数据通过 props/callback 注入) +3. 有清晰的 props 接口定义 + +升级步骤: + +1. 将文件从 `features//` 移动到 `shared/` 对应子目录 +2. 更新所有 import 路径 +3. 如原 feature 有对应的测试文件,一并迁移(`tests/web/shared/`) +4. 运行 `bun run check` 确认无遗漏 + +**降级(shared → feature):** 当一个 shared 组件/hook/tool 仅被一个 feature 使用时,应降级到该 feature 内部: + +1. 确认仅一个消费方(全局搜索 import) +2. 移动到消费方 feature 的对应子目录 +3. 更新 import 路径 +4. 迁移对应测试文件 +5. 运行 `bun run check` 确认无遗漏 + +#### 新增功能开发检查清单 + +新增功能模块时按以下顺序操作: + +1. 在 `features/` 下创建以功能名命名的目录(kebab-case) +2. 创建 `index.tsx` 作为页面组件入口 +3. 在 `routes.tsx` 中注册路由,选择对应 layout 包裹 +4. 如需布局内菜单项,更新对应 layout 的菜单配置 +5. 组件/hooks/utils 先写在 feature 内部 +6. 当确认需要跨 feature 复用时,按升级流程提升到 `shared/` +7. 测试文件创建在 `tests/web/features//` + +#### 禁止事项 + +- 禁止在 feature 目录外直接创建页面组件(`pages/` 目录不再使用) +- 禁止 feature 间通过 `../features/other-feature/` 直接导入 +- 禁止在 shared/ 中放置仅单个 feature 使用的代码 +- 禁止跳过升降级流程直接在 shared/ 中新建"预判通用"的代码(先写 feature,确认复用后再提升) ### 类型与配置 @@ -141,10 +248,17 @@ AI 工具必须严格遵守以下全部约束。 2. antd 布局组件(Layout、Space、Flex) 3. antd theme token + CSS 变量 4. TanStack Query + useState -5. 已有 hooks(`use-*.ts`)和工具函数(`utils/`) -6. CSS Modules(就近放置) +5. 已有 hooks(`shared/hooks/use-*.ts`)和工具函数(`shared/utils/`) +6. CSS Modules(就近放置在 feature 内部) 7. 引入新依赖(需说明原因) +### 前端代码组织 + +- 新增页面在 `features/` 下创建功能目录,不使用 `pages/`。 +- 新增组件/hook/tool 默认放在所属 feature 内部;跨 feature 复用时提升到 `shared/`。 +- 布局组件放 `layouts/`,布局与页面通过 `routes.tsx` 组合,不互相导入。 +- 详细规则见上方「前端目录使用规范」章节。 + ### 样式红线 - 严禁内联 `style`、覆盖 `.ant-*`、`!important`、硬编码色值。 diff --git a/docs/development/architecture.md b/docs/development/architecture.md index e9406a6..87376b6 100644 --- a/docs/development/architecture.md +++ b/docs/development/architecture.md @@ -45,6 +45,9 @@ Request -> Bun.serve routes 声明式匹配 -> routes/*.ts handler -> helpers/ | `src/server/ai/` | AI Provider Registry + Agent + 工具 | | `src/server/helpers/` | 响应格式化、URL 工具 | | `src/server/middleware/` | 参数校验 + 错误处理 | +| `src/web/layouts/` | 前端布局组件(AdminLayout / WorkbenchLayout) | +| `src/web/features/` | 前端功能模块(dashboard / projects / models / chat) | +| `src/web/shared/` | 前端共享代码(components / hooks / utils) | | `src/shared/api.ts` | 前后端共享 API 类型 | | `src/shared/app.ts` | 应用全局常量 | diff --git a/docs/development/frontend.md b/docs/development/frontend.md index 2ac6971..e8fcde0 100644 --- a/docs/development/frontend.md +++ b/docs/development/frontend.md @@ -2,26 +2,35 @@ 开发规范见 [开发规范文档](README.md)。 -## 控制台架构 +## 布局架构 -两个控制台入口共享 ConsoleShell(`src/web/components/ConsoleShell/`): +两个布局入口共享 ConsoleShell(`src/web/shared/components/ConsoleShell/`): -- **Admin**(`src/web/consoles/admin/`):路由 `/`(总览)、`/projects`、`/models`。 -- **Workbench**(`src/web/consoles/workbench/`):路由 `/workbench/:projectId`、`/workbench/:projectId/chat`。`WorkbenchProjectGate` 从 URL 读 projectId,通过 `ProjectContext` 提供项目上下文,仅 active 项目渲染。 +- **AdminLayout**(`src/web/layouts/admin-layout/`):路由 `/`(总览)、`/projects`、`/models`。 +- **WorkbenchLayout**(`src/web/layouts/workbench-layout/`):路由 `/workbench/:projectId`、`/workbench/:projectId/chat`。`WorkbenchProjectGate` 从 URL 读 projectId,通过 `ProjectContext` 提供项目上下文,仅 active 项目渲染。 -ConsoleShell 包含:`XProvider(zhCN + zhCN_X)` + `AntApp` + `Layout`(Header/Sider/Content) + 主题切换(明亮/黑暗/系统)+ 侧边栏折叠。Header 显示品牌名、版本号和控制台标题。 +ConsoleShell 包含:`XProvider(zhCN + zhCN_X)` + `AntApp` + `Layout`(Header/Sider/Content) + 主题切换(明亮/黑暗/系统)+ 侧边栏折叠。Header 显示品牌名、版本号和布局标题。 -`Sidebar`(`src/web/components/Sidebar/`)纯展示组件,通过 `menuItems` props 接收配置。 +`Sidebar`(`src/web/shared/components/Sidebar/`)纯展示组件,通过 `menuItems` props 接收配置。 + +## 功能模块 + +| 功能模块 | 路径 | 说明 | +| -------- | --------------------- | --------------------------- | +| 仪表盘 | `features/dashboard/` | 总览页面 | +| 项目管理 | `features/projects/` | 项目 CRUD、归档、搜索 | +| 模型管理 | `features/models/` | 供应商/模型管理、连通性测试 | +| 聊天 | `features/chat/` | 会话管理、消息渲染、AI 对话 | ## 页面 -| 页面 | 路径 | 入口 | -| -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| 总览 | `/` | `pages/dashboard/index.tsx` | -| 项目管理 | `/projects` | `pages/projects/index.tsx` — ProjectToolbar(Tab 切换 active/archived + 搜索 + 新建) + ProjectTable + ProjectFormModal。支持创建/编辑/归档/恢复/删除,仅 active 项目可跳转工作台。 | -| 模型管理 | `/models` | `pages/models/index.tsx` — antd Tabs 切换供应商/模型视图。模型表单和表格使用 `GET /api/providers/options`。供应商表单支持预保存连通性测试(`POST /api/providers/test`),新建时 type 默认 `openai-compatible`,测试 `ok: false` 展示失败但不阻止保存。 | -| 聊天室 | `/workbench/:id` | `consoles/workbench/pages/ChatPage.tsx` | -| 404 | `*` | `pages/404/index.tsx` | +| 页面 | 路径 | 入口 | +| -------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 总览 | `/` | `features/dashboard/index.tsx` | +| 项目管理 | `/projects` | `features/projects/index.tsx` — ProjectToolbar(Tab 切换 active/archived + 搜索 + 新建) + ProjectTable + ProjectFormModal。支持创建/编辑/归档/恢复/删除,仅 active 项目可跳转工作台。 | +| 模型管理 | `/models` | `features/models/index.tsx` — antd Tabs 切换供应商/模型视图。模型表单和表格使用 `GET /api/providers/options`。供应商表单支持预保存连通性测试(`POST /api/providers/test`),新建时 type 默认 `openai-compatible`,测试 `ok: false` 展示失败但不阻止保存。 | +| 聊天室 | `/workbench/:id` | `features/chat/index.tsx` | +| 404 | `*` | `features/not-found/index.tsx` | ### 聊天页面 @@ -31,19 +40,31 @@ ConsoleShell 包含:`XProvider(zhCN + zhCN_X)` + `AntApp` + `Layout`(Header/Si - **ChatPanel**:`useChat`(@ai-sdk/react)+ `DefaultChatTransport`(ai 包)与后端 SSE 通信。按 `part.type` 分派渲染:TextPart(XMarkdown)、ReasoningPart、ToolPart(四态)。支持编辑重发、重新生成、复制。 - **Sender**(@ant-design/x):输入框 + 发送/停止按钮 + 模型 Select(footer slot)。 -## Hooks +## 共享代码 -| Hook | 说明 | -| ----------------------- | ----------------------------------------- | -| `use-meta.ts` | `/api/meta`(30s 轮询,5s staleTime) | -| `use-projects.ts` | 项目 CRUD + archive/restore | -| `use-providers.ts` | 供应商 CRUD + test connection | -| `use-models.ts` | 模型 CRUD + test connection | -| `use-conversations.ts` | 会话和消息 fetch 函数(不含 Query hooks) | -| `use-theme-preference` | 主题偏好 localStorage 持久化 | -| `use-sidebar-collapsed` | 侧边栏折叠 localStorage 持久化 | +### 共享组件 -## 工具函数 +| 组件 | 路径 | 说明 | +| ------------- | ------------------------------------- | --------------------------------- | +| ConsoleShell | `shared/components/ConsoleShell/` | 全局布局外壳(Provider + Layout) | +| Sidebar | `shared/components/Sidebar/` | 侧边栏纯展示组件 | +| ErrorBoundary | `shared/components/ErrorBoundary.tsx` | 生产环境错误边界 | + +### 共享 Hooks + +| Hook | 路径 | 说明 | +| ----------------------- | --------------------------------------- | ----------------------------------------- | +| `use-meta.ts` | `shared/hooks/use-meta.ts` | `/api/meta`(30s 轮询,5s staleTime) | +| `use-providers.ts` | `shared/hooks/use-providers.ts` | 供应商 CRUD + test connection | +| `use-models.ts` | `shared/hooks/use-models.ts` | 模型 CRUD + test connection | +| `use-projects.ts` | `shared/hooks/use-projects.ts` | 项目 CRUD + archive/restore | +| `use-conversations.ts` | `shared/hooks/use-conversations.ts` | 会话和消息 fetch 函数(不含 Query hooks) | +| `use-logger` | `shared/hooks/use-logger.ts` | Logger hook(组件内使用) | +| `use-theme-preference` | `shared/hooks/use-theme-preference.ts` | 主题偏好 localStorage 持久化 | +| `use-sidebar-collapsed` | `shared/hooks/use-sidebar-collapsed.ts` | 侧边栏折叠 localStorage 持久化 | +| `use-is-dark` | `shared/hooks/use-is-dark.ts` | 当前是否暗色主题 | + +### 共享工具函数 | 文件 | 导出 | | --------------- | --------------------------------------------------------------------------------------------- | @@ -52,13 +73,13 @@ ConsoleShell 包含:`XProvider(zhCN + zhCN_X)` + `AntApp` + `Layout`(Header/Si ## 更新触发条件 -修改前端技术栈、组件边界、数据流、样式规则、测试环境、前端验证方式、运行时代码结构、页面组成、组件索引或 hooks/工具清单时,必须更新本文档。 +修改前端技术栈、组件边界、数据流、样式规则、测试环境、前端验证方式、运行时代码结构、页面组成、组件索引、hooks/工具清单、目录结构或功能模块归属时,必须更新本文档。 ## 日志模块 ### Logger 接口 -`src/web/utils/logger.ts` 提供与后端镜像的 Logger 抽象: +`src/web/shared/utils/logger.ts` 提供与后端镜像的 Logger 抽象: ```typescript export interface Logger { @@ -85,7 +106,7 @@ export interface Logger { **组件内(推荐):** ```typescript -import { useLogger } from "../hooks/use-logger"; +import { useLogger } from "../../shared/hooks/use-logger"; function MyComponent() { const logger = useLogger({ component: "MyComponent" }); @@ -98,7 +119,7 @@ function MyComponent() { **非组件纯函数:** ```typescript -import { createConsoleLogger } from "../utils/logger"; +import { createConsoleLogger } from "../../shared/utils/logger"; const logger = createConsoleLogger(); logger.debug("调试信息"); diff --git a/src/web/consoles/workbench/pages/ChatPage.tsx b/src/web/features/chat/ChatPage.tsx similarity index 90% rename from src/web/consoles/workbench/pages/ChatPage.tsx rename to src/web/features/chat/ChatPage.tsx index c6af3a9..c302c99 100644 --- a/src/web/consoles/workbench/pages/ChatPage.tsx +++ b/src/web/features/chat/ChatPage.tsx @@ -4,12 +4,12 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { App, Spin } from "antd"; import { useState } from "react"; -import type { Conversation } from "../../../../shared/api"; +import type { Conversation } from "../../../shared/api"; -import { createConversation, deleteConversation, fetchConversations } from "../../../hooks/use-conversations"; -import { useModelList } from "../../../hooks/use-models"; -import { ChatPanel } from "../components/chat/ChatPanel"; -import { useCurrentProject } from "../useCurrentProject"; +import { useCurrentProject } from "../../layouts/workbench-layout/useCurrentProject"; +import { createConversation, deleteConversation, fetchConversations } from "../../shared/hooks/use-conversations"; +import { useModelList } from "../../shared/hooks/use-models"; +import { ChatPanel } from "./ChatPanel"; export function ChatPage() { const project = useCurrentProject(); diff --git a/src/web/consoles/workbench/components/chat/ChatPanel.tsx b/src/web/features/chat/ChatPanel.tsx similarity index 98% rename from src/web/consoles/workbench/components/chat/ChatPanel.tsx rename to src/web/features/chat/ChatPanel.tsx index 79a4ee7..c620977 100644 --- a/src/web/consoles/workbench/components/chat/ChatPanel.tsx +++ b/src/web/features/chat/ChatPanel.tsx @@ -11,9 +11,9 @@ import { fetchConversation, fetchMessages, updateConversation, -} from "../../../../hooks/use-conversations"; -import { useLogger } from "../../../../hooks/use-logger"; -import { useModelList } from "../../../../hooks/use-models"; +} from "../../shared/hooks/use-conversations"; +import { useLogger } from "../../shared/hooks/use-logger"; +import { useModelList } from "../../shared/hooks/use-models"; import { ChatScrollArea } from "./ChatScrollArea"; import { ReasoningPart } from "./parts/ReasoningPart"; import { TextPart } from "./parts/TextPart"; diff --git a/src/web/consoles/workbench/components/chat/ChatScrollArea.tsx b/src/web/features/chat/ChatScrollArea.tsx similarity index 96% rename from src/web/consoles/workbench/components/chat/ChatScrollArea.tsx rename to src/web/features/chat/ChatScrollArea.tsx index 7ce3122..1147375 100644 --- a/src/web/consoles/workbench/components/chat/ChatScrollArea.tsx +++ b/src/web/features/chat/ChatScrollArea.tsx @@ -6,7 +6,7 @@ import "overlayscrollbars/styles/overlayscrollbars.css"; import { OverlayScrollbarsComponent, type OverlayScrollbarsComponentRef } from "overlayscrollbars-react"; import { useCallback, useRef, useState } from "react"; -import { useIsDark } from "../../../../hooks/use-is-dark"; +import { useIsDark } from "../../shared/hooks/use-is-dark"; import { useChatScroll } from "./use-chat-scroll"; interface ChatScrollAreaProps { diff --git a/src/web/consoles/workbench/components/chat/parts/CodeBlockWithCopy.tsx b/src/web/features/chat/parts/CodeBlockWithCopy.tsx similarity index 97% rename from src/web/consoles/workbench/components/chat/parts/CodeBlockWithCopy.tsx rename to src/web/features/chat/parts/CodeBlockWithCopy.tsx index 8cbff14..75aba09 100644 --- a/src/web/consoles/workbench/components/chat/parts/CodeBlockWithCopy.tsx +++ b/src/web/features/chat/parts/CodeBlockWithCopy.tsx @@ -4,7 +4,7 @@ import { App, Button, Flex, Typography } from "antd"; import React from "react"; import { oneDark, oneLight } from "react-syntax-highlighter/dist/esm/styles/prism"; -import { useIsDark } from "../../../../../hooks/use-is-dark"; +import { useIsDark } from "../../../shared/hooks/use-is-dark"; type SyntaxTheme = Record>; diff --git a/src/web/consoles/workbench/components/chat/parts/ReasoningPart.tsx b/src/web/features/chat/parts/ReasoningPart.tsx similarity index 100% rename from src/web/consoles/workbench/components/chat/parts/ReasoningPart.tsx rename to src/web/features/chat/parts/ReasoningPart.tsx diff --git a/src/web/consoles/workbench/components/chat/parts/TextPart.tsx b/src/web/features/chat/parts/TextPart.tsx similarity index 94% rename from src/web/consoles/workbench/components/chat/parts/TextPart.tsx rename to src/web/features/chat/parts/TextPart.tsx index 2c9b4cd..c26ab2f 100644 --- a/src/web/consoles/workbench/components/chat/parts/TextPart.tsx +++ b/src/web/features/chat/parts/TextPart.tsx @@ -5,7 +5,7 @@ import { Typography } from "antd"; import type { PartProps } from "./types"; -import { useIsDark } from "../../../../../hooks/use-is-dark"; +import { useIsDark } from "../../../shared/hooks/use-is-dark"; import { CodeBlockWithCopy } from "./CodeBlockWithCopy"; interface TextPartProps extends PartProps { diff --git a/src/web/consoles/workbench/components/chat/parts/ToolPart.tsx b/src/web/features/chat/parts/ToolPart.tsx similarity index 100% rename from src/web/consoles/workbench/components/chat/parts/ToolPart.tsx rename to src/web/features/chat/parts/ToolPart.tsx diff --git a/src/web/consoles/workbench/components/chat/parts/types.ts b/src/web/features/chat/parts/types.ts similarity index 100% rename from src/web/consoles/workbench/components/chat/parts/types.ts rename to src/web/features/chat/parts/types.ts diff --git a/src/web/consoles/workbench/components/chat/use-chat-scroll.ts b/src/web/features/chat/use-chat-scroll.ts similarity index 100% rename from src/web/consoles/workbench/components/chat/use-chat-scroll.ts rename to src/web/features/chat/use-chat-scroll.ts diff --git a/src/web/pages/dashboard/index.tsx b/src/web/features/dashboard/index.tsx similarity index 95% rename from src/web/pages/dashboard/index.tsx rename to src/web/features/dashboard/index.tsx index 54f76a9..a698b8b 100644 --- a/src/web/pages/dashboard/index.tsx +++ b/src/web/features/dashboard/index.tsx @@ -3,7 +3,7 @@ import type { DescriptionsProps } from "antd"; import { Alert, Card, Descriptions, Space, Spin, Typography } from "antd"; import { APP } from "../../../shared/app"; -import { useMeta } from "../../hooks/use-meta"; +import { useMeta } from "../../shared/hooks/use-meta"; export function DashboardPage() { const { data: meta, error, isLoading } = useMeta(); diff --git a/src/web/pages/models/components/ModelFormModal.tsx b/src/web/features/models/components/ModelFormModal.tsx similarity index 100% rename from src/web/pages/models/components/ModelFormModal.tsx rename to src/web/features/models/components/ModelFormModal.tsx diff --git a/src/web/pages/models/components/ModelTable.tsx b/src/web/features/models/components/ModelTable.tsx similarity index 100% rename from src/web/pages/models/components/ModelTable.tsx rename to src/web/features/models/components/ModelTable.tsx diff --git a/src/web/pages/models/components/ModelsToolbar.tsx b/src/web/features/models/components/ModelsToolbar.tsx similarity index 100% rename from src/web/pages/models/components/ModelsToolbar.tsx rename to src/web/features/models/components/ModelsToolbar.tsx diff --git a/src/web/pages/models/components/ProviderFormModal.tsx b/src/web/features/models/components/ProviderFormModal.tsx similarity index 100% rename from src/web/pages/models/components/ProviderFormModal.tsx rename to src/web/features/models/components/ProviderFormModal.tsx diff --git a/src/web/pages/models/components/ProviderTable.tsx b/src/web/features/models/components/ProviderTable.tsx similarity index 100% rename from src/web/pages/models/components/ProviderTable.tsx rename to src/web/features/models/components/ProviderTable.tsx diff --git a/src/web/pages/models/index.tsx b/src/web/features/models/index.tsx similarity index 98% rename from src/web/pages/models/index.tsx rename to src/web/features/models/index.tsx index 420252c..aa68269 100644 --- a/src/web/pages/models/index.tsx +++ b/src/web/features/models/index.tsx @@ -9,7 +9,7 @@ import { useModelList, useTestModelConnection, useUpdateModel, -} from "../../hooks/use-models"; +} from "../../shared/hooks/use-models"; import { useCreateProvider, useDeleteProvider, @@ -17,7 +17,7 @@ import { useProviderOptions, useTestProviderConfig, useUpdateProvider, -} from "../../hooks/use-providers"; +} from "../../shared/hooks/use-providers"; import { ModelFormModal } from "./components/ModelFormModal"; import { ModelsToolbar } from "./components/ModelsToolbar"; import { ModelTable } from "./components/ModelTable"; diff --git a/src/web/pages/404/index.tsx b/src/web/features/not-found/index.tsx similarity index 100% rename from src/web/pages/404/index.tsx rename to src/web/features/not-found/index.tsx diff --git a/src/web/pages/projects/components/ProjectFormModal.tsx b/src/web/features/projects/components/ProjectFormModal.tsx similarity index 100% rename from src/web/pages/projects/components/ProjectFormModal.tsx rename to src/web/features/projects/components/ProjectFormModal.tsx diff --git a/src/web/pages/projects/components/ProjectTable.tsx b/src/web/features/projects/components/ProjectTable.tsx similarity index 100% rename from src/web/pages/projects/components/ProjectTable.tsx rename to src/web/features/projects/components/ProjectTable.tsx diff --git a/src/web/pages/projects/components/ProjectToolbar.tsx b/src/web/features/projects/components/ProjectToolbar.tsx similarity index 100% rename from src/web/pages/projects/components/ProjectToolbar.tsx rename to src/web/features/projects/components/ProjectToolbar.tsx diff --git a/src/web/pages/projects/index.tsx b/src/web/features/projects/index.tsx similarity index 98% rename from src/web/pages/projects/index.tsx rename to src/web/features/projects/index.tsx index b252424..3ae549c 100644 --- a/src/web/pages/projects/index.tsx +++ b/src/web/features/projects/index.tsx @@ -10,7 +10,7 @@ import { useProjectList, useRestoreProject, useUpdateProject, -} from "../../hooks/use-projects"; +} from "../../shared/hooks/use-projects"; import { ProjectFormModal } from "./components/ProjectFormModal"; import { ProjectTable } from "./components/ProjectTable"; import { ProjectToolbar } from "./components/ProjectToolbar"; diff --git a/src/web/consoles/admin/AdminConsoleLayout.tsx b/src/web/layouts/admin-layout/AdminConsoleLayout.tsx similarity index 65% rename from src/web/consoles/admin/AdminConsoleLayout.tsx rename to src/web/layouts/admin-layout/AdminConsoleLayout.tsx index 3b8e91d..b951dfc 100644 --- a/src/web/consoles/admin/AdminConsoleLayout.tsx +++ b/src/web/layouts/admin-layout/AdminConsoleLayout.tsx @@ -1,4 +1,4 @@ -import { ConsoleShell } from "../../components/ConsoleShell/ConsoleShell"; +import { ConsoleShell } from "../../shared/components/ConsoleShell/ConsoleShell"; import { ADMIN_MENU_ITEMS } from "./menu"; export function AdminConsoleLayout() { diff --git a/src/web/consoles/admin/menu.tsx b/src/web/layouts/admin-layout/menu.tsx similarity index 100% rename from src/web/consoles/admin/menu.tsx rename to src/web/layouts/admin-layout/menu.tsx diff --git a/src/web/consoles/workbench/ProjectContext.tsx b/src/web/layouts/workbench-layout/ProjectContext.tsx similarity index 100% rename from src/web/consoles/workbench/ProjectContext.tsx rename to src/web/layouts/workbench-layout/ProjectContext.tsx diff --git a/src/web/consoles/workbench/ProjectContextValue.ts b/src/web/layouts/workbench-layout/ProjectContextValue.ts similarity index 100% rename from src/web/consoles/workbench/ProjectContextValue.ts rename to src/web/layouts/workbench-layout/ProjectContextValue.ts diff --git a/src/web/consoles/workbench/WorkbenchConsoleLayout.tsx b/src/web/layouts/workbench-layout/WorkbenchConsoleLayout.tsx similarity index 92% rename from src/web/consoles/workbench/WorkbenchConsoleLayout.tsx rename to src/web/layouts/workbench-layout/WorkbenchConsoleLayout.tsx index de804ae..cbe3017 100644 --- a/src/web/consoles/workbench/WorkbenchConsoleLayout.tsx +++ b/src/web/layouts/workbench-layout/WorkbenchConsoleLayout.tsx @@ -4,7 +4,7 @@ import { useNavigate } from "react-router"; import type { Project } from "../../../shared/api"; -import { ConsoleShell } from "../../components/ConsoleShell/ConsoleShell"; +import { ConsoleShell } from "../../shared/components/ConsoleShell/ConsoleShell"; import { ProjectProvider } from "./ProjectContext"; import { getWorkbenchMenuItems } from "./routes"; import { useCurrentProject } from "./useCurrentProject"; diff --git a/src/web/consoles/workbench/WorkbenchProjectGate.tsx b/src/web/layouts/workbench-layout/WorkbenchProjectGate.tsx similarity index 95% rename from src/web/consoles/workbench/WorkbenchProjectGate.tsx rename to src/web/layouts/workbench-layout/WorkbenchProjectGate.tsx index 8ff4a81..d589118 100644 --- a/src/web/consoles/workbench/WorkbenchProjectGate.tsx +++ b/src/web/layouts/workbench-layout/WorkbenchProjectGate.tsx @@ -1,7 +1,7 @@ import { Alert, Button, Spin } from "antd"; import { useNavigate, useParams } from "react-router"; -import { useProject } from "../../hooks/use-projects"; +import { useProject } from "../../shared/hooks/use-projects"; import { WorkbenchConsoleLayout } from "./WorkbenchConsoleLayout"; export function WorkbenchProjectGate() { diff --git a/src/web/consoles/workbench/routes.ts b/src/web/layouts/workbench-layout/routes.ts similarity index 100% rename from src/web/consoles/workbench/routes.ts rename to src/web/layouts/workbench-layout/routes.ts diff --git a/src/web/consoles/workbench/useCurrentProject.ts b/src/web/layouts/workbench-layout/useCurrentProject.ts similarity index 100% rename from src/web/consoles/workbench/useCurrentProject.ts rename to src/web/layouts/workbench-layout/useCurrentProject.ts diff --git a/src/web/main.tsx b/src/web/main.tsx index 25e4483..331bf06 100644 --- a/src/web/main.tsx +++ b/src/web/main.tsx @@ -5,8 +5,8 @@ import { createRoot } from "react-dom/client"; import { BrowserRouter } from "react-router"; import { App } from "./app"; -import { ErrorBoundary } from "./components/ErrorBoundary"; -import { createConsoleLogger } from "./utils/logger"; +import { ErrorBoundary } from "./shared/components/ErrorBoundary"; +import { createConsoleLogger } from "./shared/utils/logger"; import "./styles.css"; const logger = createConsoleLogger(); diff --git a/src/web/routes.tsx b/src/web/routes.tsx index 253e1d4..b8a22e2 100644 --- a/src/web/routes.tsx +++ b/src/web/routes.tsx @@ -1,12 +1,12 @@ import { Route, Routes } from "react-router"; -import { AdminConsoleLayout } from "./consoles/admin/AdminConsoleLayout"; -import { ChatPage } from "./consoles/workbench/pages/ChatPage"; -import { WorkbenchProjectGate } from "./consoles/workbench/WorkbenchProjectGate"; -import { NotFoundPage } from "./pages/404"; -import { DashboardPage } from "./pages/dashboard"; -import { ModelsPage } from "./pages/models"; -import { ProjectsPage } from "./pages/projects"; +import { ChatPage } from "./features/chat/ChatPage"; +import { DashboardPage } from "./features/dashboard"; +import { ModelsPage } from "./features/models"; +import { NotFoundPage } from "./features/not-found"; +import { ProjectsPage } from "./features/projects"; +import { AdminConsoleLayout } from "./layouts/admin-layout/AdminConsoleLayout"; +import { WorkbenchProjectGate } from "./layouts/workbench-layout/WorkbenchProjectGate"; export function AppRoutes() { return ( diff --git a/src/web/components/ConsoleShell/ConsoleOutlet.tsx b/src/web/shared/components/ConsoleShell/ConsoleOutlet.tsx similarity index 100% rename from src/web/components/ConsoleShell/ConsoleOutlet.tsx rename to src/web/shared/components/ConsoleShell/ConsoleOutlet.tsx diff --git a/src/web/components/ConsoleShell/ConsoleShell.tsx b/src/web/shared/components/ConsoleShell/ConsoleShell.tsx similarity index 98% rename from src/web/components/ConsoleShell/ConsoleShell.tsx rename to src/web/shared/components/ConsoleShell/ConsoleShell.tsx index 8dc095c..5b6fddf 100644 --- a/src/web/components/ConsoleShell/ConsoleShell.tsx +++ b/src/web/shared/components/ConsoleShell/ConsoleShell.tsx @@ -6,7 +6,7 @@ import zhCN from "antd/locale/zh_CN"; import type { ConsoleShellProps } from "./types"; -import { APP } from "../../../shared/app"; +import { APP } from "../../../../shared/app"; import { useMeta } from "../../hooks/use-meta"; import { useSidebarCollapsed } from "../../hooks/use-sidebar-collapsed"; import { useThemePreference } from "../../hooks/use-theme-preference"; diff --git a/src/web/components/ConsoleShell/types.ts b/src/web/shared/components/ConsoleShell/types.ts similarity index 76% rename from src/web/components/ConsoleShell/types.ts rename to src/web/shared/components/ConsoleShell/types.ts index b0e0cfd..42a6716 100644 --- a/src/web/components/ConsoleShell/types.ts +++ b/src/web/shared/components/ConsoleShell/types.ts @@ -1,6 +1,6 @@ import type { ReactNode } from "react"; -import type { MenuItemConfig } from "../../menu"; +import type { MenuItemConfig } from "../../../menu"; export interface ConsoleShellProps { headerExtra?: ReactNode; diff --git a/src/web/components/ErrorBoundary.tsx b/src/web/shared/components/ErrorBoundary.tsx similarity index 100% rename from src/web/components/ErrorBoundary.tsx rename to src/web/shared/components/ErrorBoundary.tsx diff --git a/src/web/components/Sidebar/index.tsx b/src/web/shared/components/Sidebar/index.tsx similarity index 94% rename from src/web/components/Sidebar/index.tsx rename to src/web/shared/components/Sidebar/index.tsx index 9c6538e..a2a4613 100644 --- a/src/web/components/Sidebar/index.tsx +++ b/src/web/shared/components/Sidebar/index.tsx @@ -3,7 +3,7 @@ import type { MenuProps } from "antd"; import { Menu } from "antd"; import { useLocation, useNavigate } from "react-router"; -import type { MenuItemConfig } from "../../menu"; +import type { MenuItemConfig } from "../../../menu"; type MenuItem = Required["items"][number]; diff --git a/src/web/hooks/use-conversations.ts b/src/web/shared/hooks/use-conversations.ts similarity index 98% rename from src/web/hooks/use-conversations.ts rename to src/web/shared/hooks/use-conversations.ts index f417f7d..e3290ee 100644 --- a/src/web/hooks/use-conversations.ts +++ b/src/web/shared/hooks/use-conversations.ts @@ -4,7 +4,7 @@ import type { ConversationResponse, MessageListResponse, UpdateConversationRequest, -} from "../../shared/api"; +} from "../../../shared/api"; import { handleResponse, handleVoidResponse } from "../utils/api"; import { createConsoleLogger } from "../utils/logger"; diff --git a/src/web/hooks/use-is-dark.ts b/src/web/shared/hooks/use-is-dark.ts similarity index 100% rename from src/web/hooks/use-is-dark.ts rename to src/web/shared/hooks/use-is-dark.ts diff --git a/src/web/hooks/use-logger.ts b/src/web/shared/hooks/use-logger.ts similarity index 100% rename from src/web/hooks/use-logger.ts rename to src/web/shared/hooks/use-logger.ts diff --git a/src/web/hooks/use-meta.ts b/src/web/shared/hooks/use-meta.ts similarity index 88% rename from src/web/hooks/use-meta.ts rename to src/web/shared/hooks/use-meta.ts index ab0d120..48c0729 100644 --- a/src/web/hooks/use-meta.ts +++ b/src/web/shared/hooks/use-meta.ts @@ -1,6 +1,6 @@ import { useQuery } from "@tanstack/react-query"; -import type { MetaResponse } from "../../shared/api"; +import type { MetaResponse } from "../../../shared/api"; export function useMeta() { return useQuery({ diff --git a/src/web/hooks/use-models.ts b/src/web/shared/hooks/use-models.ts similarity index 99% rename from src/web/hooks/use-models.ts rename to src/web/shared/hooks/use-models.ts index da6db5c..390236c 100644 --- a/src/web/hooks/use-models.ts +++ b/src/web/shared/hooks/use-models.ts @@ -9,7 +9,7 @@ import type { ModelTestResultResponse, TestModelRequest, UpdateModelRequest, -} from "../../shared/api"; +} from "../../../shared/api"; import { handleResponse, handleVoidResponse } from "../utils/api"; import { createConsoleLogger } from "../utils/logger"; diff --git a/src/web/hooks/use-projects.ts b/src/web/shared/hooks/use-projects.ts similarity index 99% rename from src/web/hooks/use-projects.ts rename to src/web/shared/hooks/use-projects.ts index 0253336..6fd5570 100644 --- a/src/web/hooks/use-projects.ts +++ b/src/web/shared/hooks/use-projects.ts @@ -7,7 +7,7 @@ import type { ProjectResponse, ProjectStatus, UpdateProjectRequest, -} from "../../shared/api"; +} from "../../../shared/api"; import { handleResponse, handleVoidResponse } from "../utils/api"; import { createConsoleLogger } from "../utils/logger"; diff --git a/src/web/hooks/use-providers.ts b/src/web/shared/hooks/use-providers.ts similarity index 99% rename from src/web/hooks/use-providers.ts rename to src/web/shared/hooks/use-providers.ts index 0ae6ba0..40cb11a 100644 --- a/src/web/hooks/use-providers.ts +++ b/src/web/shared/hooks/use-providers.ts @@ -9,7 +9,7 @@ import type { ProviderTestResponse, ProviderTestResultResponse, UpdateProviderRequest, -} from "../../shared/api"; +} from "../../../shared/api"; import { handleResponse, handleVoidResponse } from "../utils/api"; import { createConsoleLogger } from "../utils/logger"; diff --git a/src/web/hooks/use-sidebar-collapsed.ts b/src/web/shared/hooks/use-sidebar-collapsed.ts similarity index 100% rename from src/web/hooks/use-sidebar-collapsed.ts rename to src/web/shared/hooks/use-sidebar-collapsed.ts diff --git a/src/web/hooks/use-theme-preference.ts b/src/web/shared/hooks/use-theme-preference.ts similarity index 100% rename from src/web/hooks/use-theme-preference.ts rename to src/web/shared/hooks/use-theme-preference.ts diff --git a/src/web/utils/api.ts b/src/web/shared/utils/api.ts similarity index 100% rename from src/web/utils/api.ts rename to src/web/shared/utils/api.ts diff --git a/src/web/utils/logger.ts b/src/web/shared/utils/logger.ts similarity index 100% rename from src/web/utils/logger.ts rename to src/web/shared/utils/logger.ts diff --git a/src/web/utils/time.ts b/src/web/shared/utils/time.ts similarity index 100% rename from src/web/utils/time.ts rename to src/web/shared/utils/time.ts diff --git a/tests/web/components/ChatPanel.test.tsx b/tests/web/components/ChatPanel.test.tsx index 27123f0..6811ff7 100644 --- a/tests/web/components/ChatPanel.test.tsx +++ b/tests/web/components/ChatPanel.test.tsx @@ -4,7 +4,7 @@ import { createElement } from "react"; import type { Conversation, Model } from "../../../src/shared/api"; -import { ChatPanel } from "../../../src/web/consoles/workbench/components/chat/ChatPanel"; +import { ChatPanel } from "../../../src/web/features/chat/ChatPanel"; import { installFetchMock, jsonResponse, renderWithProviders } from "../test-utils"; const PROJECT_ID = "proj-1"; diff --git a/tests/web/components/ErrorBoundary.test.tsx b/tests/web/components/ErrorBoundary.test.tsx index 6f7fd7a..b5159c0 100644 --- a/tests/web/components/ErrorBoundary.test.tsx +++ b/tests/web/components/ErrorBoundary.test.tsx @@ -3,7 +3,7 @@ import { App as AntApp, ConfigProvider } from "antd"; import { describe, expect, test } from "bun:test"; import { createElement } from "react"; -import { ErrorBoundary } from "../../../src/web/components/ErrorBoundary"; +import { ErrorBoundary } from "../../../src/web/shared/components/ErrorBoundary"; function BrokenChild(): never { throw new Error("render failed"); diff --git a/tests/web/components/ModelTable.test.tsx b/tests/web/components/ModelTable.test.tsx index fb94cfd..893bf2a 100644 --- a/tests/web/components/ModelTable.test.tsx +++ b/tests/web/components/ModelTable.test.tsx @@ -4,7 +4,7 @@ import { createElement } from "react"; import type { Model, ProviderOption } from "../../../src/shared/api"; -import { ModelTable } from "../../../src/web/pages/models/components/ModelTable"; +import { ModelTable } from "../../../src/web/features/models/components/ModelTable"; import { renderWithProviders } from "../test-utils"; const OPENAI_PROVIDER: ProviderOption = { diff --git a/tests/web/components/ProviderTable.test.tsx b/tests/web/components/ProviderTable.test.tsx index b55b88b..928942b 100644 --- a/tests/web/components/ProviderTable.test.tsx +++ b/tests/web/components/ProviderTable.test.tsx @@ -4,7 +4,7 @@ import { createElement } from "react"; import type { Provider } from "../../../src/shared/api"; -import { ProviderTable } from "../../../src/web/pages/models/components/ProviderTable"; +import { ProviderTable } from "../../../src/web/features/models/components/ProviderTable"; import { renderWithProviders } from "../test-utils"; const OPENAI_PROVIDER: Provider = { diff --git a/tests/web/components/Sidebar/index.test.tsx b/tests/web/components/Sidebar/index.test.tsx index 5abb29d..0179bdb 100644 --- a/tests/web/components/Sidebar/index.test.tsx +++ b/tests/web/components/Sidebar/index.test.tsx @@ -3,8 +3,8 @@ import { describe, expect, test } from "bun:test"; import { createElement } from "react"; import { useLocation } from "react-router"; -import { Sidebar } from "../../../../src/web/components/Sidebar"; -import { ADMIN_MENU_ITEMS } from "../../../../src/web/consoles/admin/menu"; +import { ADMIN_MENU_ITEMS } from "../../../../src/web/layouts/admin-layout/menu"; +import { Sidebar } from "../../../../src/web/shared/components/Sidebar"; import { renderWithProviders } from "../../test-utils"; function LocationProbe() { diff --git a/tests/web/components/chat/CodeBlockWithCopy.test.tsx b/tests/web/components/chat/CodeBlockWithCopy.test.tsx index 073a16c..af495e9 100644 --- a/tests/web/components/chat/CodeBlockWithCopy.test.tsx +++ b/tests/web/components/chat/CodeBlockWithCopy.test.tsx @@ -2,7 +2,7 @@ import { screen } from "@testing-library/react"; import { describe, expect, mock, test } from "bun:test"; import { createElement } from "react"; -import { CodeBlockWithCopy } from "../../../../src/web/consoles/workbench/components/chat/parts/CodeBlockWithCopy"; +import { CodeBlockWithCopy } from "../../../../src/web/features/chat/parts/CodeBlockWithCopy"; import { renderWithProviders } from "../../test-utils"; const mockWriteText = mock(() => Promise.resolve()); diff --git a/tests/web/components/chat/ReasoningPart.test.tsx b/tests/web/components/chat/ReasoningPart.test.tsx index 9ddba2e..4664ff8 100644 --- a/tests/web/components/chat/ReasoningPart.test.tsx +++ b/tests/web/components/chat/ReasoningPart.test.tsx @@ -2,7 +2,7 @@ import { fireEvent, screen } from "@testing-library/react"; import { describe, expect, test } from "bun:test"; import { createElement } from "react"; -import { ReasoningPart } from "../../../../src/web/consoles/workbench/components/chat/parts/ReasoningPart"; +import { ReasoningPart } from "../../../../src/web/features/chat/parts/ReasoningPart"; import { renderWithProviders } from "../../test-utils"; describe("ReasoningPart", () => { diff --git a/tests/web/components/chat/ToolPart.test.tsx b/tests/web/components/chat/ToolPart.test.tsx index 245a6c2..dfa6dc6 100644 --- a/tests/web/components/chat/ToolPart.test.tsx +++ b/tests/web/components/chat/ToolPart.test.tsx @@ -2,7 +2,7 @@ import { screen } from "@testing-library/react"; import { describe, expect, test } from "bun:test"; import { createElement } from "react"; -import { ToolPart } from "../../../../src/web/consoles/workbench/components/chat/parts/ToolPart"; +import { ToolPart } from "../../../../src/web/features/chat/parts/ToolPart"; import { renderWithProviders } from "../../test-utils"; describe("ToolPart 工具显示名", () => { diff --git a/tests/web/components/query-client-logging.test.tsx b/tests/web/components/query-client-logging.test.tsx index 6fddebd..aa6b7ae 100644 --- a/tests/web/components/query-client-logging.test.tsx +++ b/tests/web/components/query-client-logging.test.tsx @@ -3,7 +3,7 @@ import { render } from "@testing-library/react"; import { describe, expect, test } from "bun:test"; import { createElement, useRef } from "react"; -import { useCreateProject } from "../../../src/web/hooks/use-projects"; +import { useCreateProject } from "../../../src/web/shared/hooks/use-projects"; import { installFetchMock, jsonResponse } from "../test-utils"; describe("QueryClient MutationCache onError", () => { diff --git a/tests/web/hooks/on-success-logging.test.tsx b/tests/web/hooks/on-success-logging.test.tsx index 4958de1..463a597 100644 --- a/tests/web/hooks/on-success-logging.test.tsx +++ b/tests/web/hooks/on-success-logging.test.tsx @@ -8,20 +8,20 @@ import { useDeleteModel, useTestModelConnection, useUpdateModel, -} from "../../../src/web/hooks/use-models"; +} from "../../../src/web/shared/hooks/use-models"; import { useArchiveProject, useCreateProject, useDeleteProject, useRestoreProject, useUpdateProject, -} from "../../../src/web/hooks/use-projects"; +} from "../../../src/web/shared/hooks/use-projects"; import { useCreateProvider, useDeleteProvider, useTestProviderConfig, useUpdateProvider, -} from "../../../src/web/hooks/use-providers"; +} from "../../../src/web/shared/hooks/use-providers"; import { installFetchMock, jsonResponse } from "../test-utils"; const MODEL = { diff --git a/tests/web/hooks/use-chat-scroll.test.ts b/tests/web/hooks/use-chat-scroll.test.ts index ad14502..e94b381 100644 --- a/tests/web/hooks/use-chat-scroll.test.ts +++ b/tests/web/hooks/use-chat-scroll.test.ts @@ -3,7 +3,7 @@ import type { UIMessage } from "ai"; import { act, renderHook } from "@testing-library/react"; import { describe, expect, mock, test } from "bun:test"; -import { useChatScroll } from "../../../src/web/consoles/workbench/components/chat/use-chat-scroll"; +import { useChatScroll } from "../../../src/web/features/chat/use-chat-scroll"; interface HookProps { loadingHistory: boolean; diff --git a/tests/web/hooks/use-logger.test.ts b/tests/web/hooks/use-logger.test.ts index 6471467..00343c9 100644 --- a/tests/web/hooks/use-logger.test.ts +++ b/tests/web/hooks/use-logger.test.ts @@ -1,9 +1,9 @@ import { describe, expect, mock, test } from "bun:test"; import { act, createElement, useState } from "react"; -import type { Logger } from "../../../src/web/utils/logger"; +import type { Logger } from "../../../src/web/shared/utils/logger"; -import { useLogger } from "../../../src/web/hooks/use-logger"; +import { useLogger } from "../../../src/web/shared/hooks/use-logger"; import { renderWithProviders } from "../test-utils"; function BindingsHookTester({ diff --git a/tests/web/hooks/use-models.test.ts b/tests/web/hooks/use-models.test.ts index 3d0fc6b..c6a17b4 100644 --- a/tests/web/hooks/use-models.test.ts +++ b/tests/web/hooks/use-models.test.ts @@ -7,7 +7,7 @@ import { fetchModelList, testModelConnection, updateModel, -} from "../../../src/web/hooks/use-models"; +} from "../../../src/web/shared/hooks/use-models"; import { installFetchMock, jsonResponse } from "../test-utils"; const MODEL = { diff --git a/tests/web/hooks/use-projects.test.ts b/tests/web/hooks/use-projects.test.ts index 8376a68..2f428c8 100644 --- a/tests/web/hooks/use-projects.test.ts +++ b/tests/web/hooks/use-projects.test.ts @@ -8,7 +8,7 @@ import { fetchProjectList, restoreProject, updateProject, -} from "../../../src/web/hooks/use-projects"; +} from "../../../src/web/shared/hooks/use-projects"; import { installFetchMock, jsonResponse } from "../test-utils"; const PROJECT = { diff --git a/tests/web/hooks/use-providers.test.ts b/tests/web/hooks/use-providers.test.ts index f816b50..30c1dfc 100644 --- a/tests/web/hooks/use-providers.test.ts +++ b/tests/web/hooks/use-providers.test.ts @@ -8,7 +8,7 @@ import { fetchProviderOptions, testProviderConfig, updateProvider, -} from "../../../src/web/hooks/use-providers"; +} from "../../../src/web/shared/hooks/use-providers"; import { installFetchMock, jsonResponse } from "../test-utils"; const PROVIDER = { diff --git a/tests/web/hooks/use-sidebar-collapsed.test.ts b/tests/web/hooks/use-sidebar-collapsed.test.ts index ed8d765..e993665 100644 --- a/tests/web/hooks/use-sidebar-collapsed.test.ts +++ b/tests/web/hooks/use-sidebar-collapsed.test.ts @@ -5,7 +5,7 @@ import { readSidebarCollapsed, SIDEBAR_COLLAPSED_STORAGE_KEY, writeSidebarCollapsed, -} from "../../../src/web/hooks/use-sidebar-collapsed"; +} from "../../../src/web/shared/hooks/use-sidebar-collapsed"; function createStorage(initial?: string): Storage { const values = new Map(); diff --git a/tests/web/hooks/use-theme-preference.test.ts b/tests/web/hooks/use-theme-preference.test.ts index 84eb69f..18caf30 100644 --- a/tests/web/hooks/use-theme-preference.test.ts +++ b/tests/web/hooks/use-theme-preference.test.ts @@ -8,7 +8,7 @@ import { THEME_MEDIA_QUERY, THEME_PREFERENCE_STORAGE_KEY, writeThemePreference, -} from "../../../src/web/hooks/use-theme-preference"; +} from "../../../src/web/shared/hooks/use-theme-preference"; function createStorage(initial?: string): Storage { const values = new Map(); diff --git a/tests/web/routes/404.test.tsx b/tests/web/routes/404.test.tsx index cfabe9c..322dacd 100644 --- a/tests/web/routes/404.test.tsx +++ b/tests/web/routes/404.test.tsx @@ -3,7 +3,7 @@ import { describe, expect, test } from "bun:test"; import { createElement } from "react"; import { useLocation } from "react-router"; -import { NotFoundPage } from "../../../src/web/pages/404"; +import { NotFoundPage } from "../../../src/web/features/not-found"; import { renderWithProviders } from "../test-utils"; function LocationProbe() { diff --git a/tests/web/routes/dashboard.test.tsx b/tests/web/routes/dashboard.test.tsx index fa3bd84..e8a7be2 100644 --- a/tests/web/routes/dashboard.test.tsx +++ b/tests/web/routes/dashboard.test.tsx @@ -3,7 +3,7 @@ import { screen } from "@testing-library/react"; import { describe, expect, test } from "bun:test"; import { createElement } from "react"; -import { DashboardPage } from "../../../src/web/pages/dashboard"; +import { DashboardPage } from "../../../src/web/features/dashboard"; import { renderWithProviders } from "../test-utils"; describe("DashboardPage", () => { diff --git a/tests/web/routes/models.test.tsx b/tests/web/routes/models.test.tsx index 6d552da..a23b0ad 100644 --- a/tests/web/routes/models.test.tsx +++ b/tests/web/routes/models.test.tsx @@ -4,8 +4,8 @@ import { createElement } from "react"; import type { Model, Provider } from "../../../src/shared/api"; -import { ModelFormModal } from "../../../src/web/pages/models/components/ModelFormModal"; -import { ProviderFormModal } from "../../../src/web/pages/models/components/ProviderFormModal"; +import { ModelFormModal } from "../../../src/web/features/models/components/ModelFormModal"; +import { ProviderFormModal } from "../../../src/web/features/models/components/ProviderFormModal"; import { renderWithProviders } from "../test-utils"; const ENABLED_PROVIDER: Provider = { diff --git a/tests/web/routes/projects.test.tsx b/tests/web/routes/projects.test.tsx index bcb17c5..02c6f16 100644 --- a/tests/web/routes/projects.test.tsx +++ b/tests/web/routes/projects.test.tsx @@ -6,8 +6,8 @@ import { useLocation } from "react-router"; import type { Project } from "../../../src/shared/api"; import { App } from "../../../src/web/app"; -import { ProjectFormModal } from "../../../src/web/pages/projects/components/ProjectFormModal"; -import { ProjectTable } from "../../../src/web/pages/projects/components/ProjectTable"; +import { ProjectFormModal } from "../../../src/web/features/projects/components/ProjectFormModal"; +import { ProjectTable } from "../../../src/web/features/projects/components/ProjectTable"; import { installFetchMock, jsonResponse, mockMetaResponse, renderWithProviders } from "../test-utils"; const ACTIVE_PROJECT: Project = { diff --git a/tests/web/test-utils.tsx b/tests/web/test-utils.tsx index 67450ff..1277da0 100644 --- a/tests/web/test-utils.tsx +++ b/tests/web/test-utils.tsx @@ -6,7 +6,7 @@ import { mock } from "bun:test"; import { createElement, StrictMode } from "react"; import { MemoryRouter } from "react-router"; -import { ErrorBoundary } from "../../src/web/components/ErrorBoundary"; +import { ErrorBoundary } from "../../src/web/shared/components/ErrorBoundary"; const REAL_FETCH = globalThis.fetch.bind(globalThis); diff --git a/tests/web/utils/api.test.ts b/tests/web/utils/api.test.ts index cb8d637..6f4fa77 100644 --- a/tests/web/utils/api.test.ts +++ b/tests/web/utils/api.test.ts @@ -1,6 +1,6 @@ import { describe, expect, mock, test } from "bun:test"; -import { handleResponse, handleVoidResponse } from "../../../src/web/utils/api"; +import { handleResponse, handleVoidResponse } from "../../../src/web/shared/utils/api"; function expectRejects(action: () => Promise, message: string) { return action().then( diff --git a/tests/web/utils/logger.test.ts b/tests/web/utils/logger.test.ts index ae4f9ec..172c079 100644 --- a/tests/web/utils/logger.test.ts +++ b/tests/web/utils/logger.test.ts @@ -1,6 +1,6 @@ import { describe, expect, mock, test } from "bun:test"; -import type { Sink } from "../../../src/web/utils/logger"; +import type { Sink } from "../../../src/web/shared/utils/logger"; import { AntdMessageSink, @@ -9,7 +9,7 @@ import { createDefaultLogger, createMemoryLogger, createNoopLogger, -} from "../../../src/web/utils/logger"; +} from "../../../src/web/shared/utils/logger"; describe("ConsoleSink", () => { test("调试环境输出 debug 级别", () => { diff --git a/tests/web/utils/time.test.ts b/tests/web/utils/time.test.ts index 7999c32..b11783b 100644 --- a/tests/web/utils/time.test.ts +++ b/tests/web/utils/time.test.ts @@ -6,7 +6,7 @@ import { formatRelativeTime, isOlderThan, subtractHours, -} from "../../../src/web/utils/time"; +} from "../../../src/web/shared/utils/time"; describe("subtractHours", () => { test("正常扣减小时", () => {