refactor(web): 前端目录重构 — consoles/pages → layouts/features + shared
- 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)
This commit is contained in:
@@ -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/<name>/
|
||||
├── 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/<name>/` 移动到 `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/<name>/`
|
||||
|
||||
#### 禁止事项
|
||||
|
||||
- 禁止在 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`、硬编码色值。
|
||||
|
||||
@@ -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` | 应用全局常量 |
|
||||
|
||||
|
||||
@@ -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("调试信息");
|
||||
|
||||
@@ -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();
|
||||
@@ -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";
|
||||
@@ -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 {
|
||||
@@ -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<string, Record<string, null | number | string>>;
|
||||
|
||||
@@ -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 {
|
||||
@@ -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();
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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() {
|
||||
@@ -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";
|
||||
@@ -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() {
|
||||
@@ -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();
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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";
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import type { MenuItemConfig } from "../../menu";
|
||||
import type { MenuItemConfig } from "../../../menu";
|
||||
|
||||
export interface ConsoleShellProps {
|
||||
headerExtra?: ReactNode;
|
||||
@@ -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<MenuProps>["items"][number];
|
||||
|
||||
@@ -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";
|
||||
@@ -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({
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 工具显示名", () => {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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<string, string>();
|
||||
|
||||
@@ -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<string, string>();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<unknown>, message: string) {
|
||||
return action().then(
|
||||
|
||||
@@ -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 级别", () => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
formatRelativeTime,
|
||||
isOlderThan,
|
||||
subtractHours,
|
||||
} from "../../../src/web/utils/time";
|
||||
} from "../../../src/web/shared/utils/time";
|
||||
|
||||
describe("subtractHours", () => {
|
||||
test("正常扣减小时", () => {
|
||||
|
||||
Reference in New Issue
Block a user