| name | hugagent-frontend-dev |
| description | HugAgentOS 前端开发规范。当需要新增或修改前端代码(组件、Store、Hook、API调用、样式等)时使用此 skill, 确保组件结构、状态管理、样式命名、类型安全等与项目现有规范保持一致。 |
HugAgentOS 前端开发规范
本 skill 定义了 HugAgentOS 项目前端(React + TypeScript + Ant Design + Zustand)的开发规范与流程。
所有前端代码变更必须遵守以下规范。
文件索引
模板 (templates/)
| 文件 | 用途 |
|---|
component.tsx | React 组件模板(Props + Hooks + 错误处理) |
store.ts | Zustand Store 模板(CRUD + localStorage 持久化) |
hook.ts | 自定义 Hook 模板(初始化 + 清理 + abort) |
css-module.css | CSS 样式模板(.jx- 前缀 + 响应式) |
参考文档 (references/)
| 文件 | 内容 |
|---|
ui-design-spec.md | UI 设计规范(颜色系统、字体排版、图标、圆角、间距、组件规范、Ant Design 主题映射) |
icon-assets.md | 图标资产清单(全部 SVG 图标索引、使用方式、分类说明) |
component-tree.md | 完整组件树 |
css-variables.md | CSS 变量、命名规则、常用尺寸、样式模式(与 UI 设计规范对齐) |
api-patterns.md | API 调用模式(authFetch、SSE) |
sse-events.md | SSE 事件类型详解(run_started/content/thinking/tool_call/tool_result/tool_pending/batch_confirm/file_confirm/meta/error + [DONE]) |
图标资产 (assets/)
| 目录 | 内容 |
|---|
assets/icons/ | 通用 UI 图标(30 个 SVG:logo、导航、操作按钮、场景图标等) |
assets/mcp-icons/ | MCP 工具库图标(12 个 SVG:互联网、数据、报告、知识等) |
脚本 (scripts/)
| 文件 | 用途 |
|---|
scaffold_component.sh | 一键生成新组件(.tsx + .css + index.ts) |
模板中 ${ComponentName} / ${componentName} / ${group} 为占位符,使用时替换。
1. 目录结构
src/frontend/src/
├── main.tsx # 入口:按路径分发应用壳
├── App.tsx # 主聊天应用(用户端)
├── ApiDocApp.tsx # /api-docs 开放 API 文档
├── SharePreviewApp.tsx # ?share= 分享预览页
├── types.ts # 共享 TypeScript 类型
├── api.ts # API 客户端(信封解包)
├── storage.ts # localStorage 工具 + defaultCatalog
│
├── components/ # 组件组(每组 index.ts barrel export)
│ ├── chat/ # ChatArea, InputArea, MessageBubble, PromptHubPanel
│ ├── agent/ # 子智能体
│ ├── apidoc/ # 开放 API 文档
│ ├── automation/ # 定时任务/自动化
│ ├── batch/ # 批量执行
│ ├── canvas/ # 画布产物
│ ├── catalog/ # CatalogPanel(能力中心)
│ ├── citation/ # CitationBadge, CitationHtmlBlock, CitationMarkdownBlock
│ ├── common/ # AuthExpiredModal, ImagePreview 等
│ ├── docs/ # DocsPanel(版本说明/能力介绍)
│ ├── file/ # FileAttachmentCard
│ ├── kb/ # 知识库(CreateKBModal, ReindexModal 等)
│ ├── lab/ # 实验室(技能蒸馏等)
│ ├── memory/ # 记忆中心
│ ├── myspace/ # 我的空间
│ ├── projects/ # 项目空间
│ ├── settings/ # SettingsModal(含记忆 L1/L2/L3 Tab)
│ ├── share/ # 分享
│ ├── sidebar/ # Sidebar
│ └── tool/ # ToolOutputRenderer, ToolResultPanel
│
├── hooks/ # 7 个自定义 Hook
│ ├── useChatInit.ts # 认证、Catalog、会话、消息初始化
│ ├── useChatActions.ts # 聊天 CRUD、重命名、导出
│ ├── useStreaming.ts # SSE 流式处理、文件上传、发送逻辑、断线续播
│ ├── usePlanMode.ts # Plan 模式
│ ├── usePageConfig.ts # 页面配置
│ ├── useDelayedFlag.ts # 延迟标志位
│ └── useStallDetector.ts # 流式停滞检测
│
├── stores/ # 18 个 Zustand Store
│ ├── authStore.ts # 认证状态
│ ├── chatStore.ts # 聊天会话、消息、activeRun
│ ├── catalogStore.ts # Catalog(技能、智能体、MCP、知识库)
│ ├── uiStore.ts # UI 面板、搜索、筛选、pending confirm 队列
│ ├── settingsStore.ts # 记忆、排序、偏好
│ ├── fileStore.ts # 文件上传
│ ├── kbStore.ts # 知识库管理
│ ├── agentStore.ts # 子智能体
│ ├── automationStore.ts / automationChatStore.ts # 自动化
│ ├── batchStore.ts # 批量执行(pendingConfirm)
│ ├── canvasStore.ts # 画布
│ ├── editionStore.ts # CE/EE 门控(消费 /v1/meta/edition)
│ ├── modelCapabilitiesStore.ts # 模型能力探测
│ ├── mySpaceStore.ts # 我的空间
│ ├── pageConfigStore.ts # 页面配置
│ ├── projectStore.ts # 项目空间
│ └── skillDistillStore.ts # 技能蒸馏
│
├── utils/ # 20+ 工具模块
│ ├── citations.ts # 引用解析与去重
│ ├── constants.ts # 工具名称、快捷场景、能力卡片
│ ├── export.ts # PDF 导出
│ ├── fileParser.ts # 文件解析、OSS 上传
│ ├── history.ts # 话题推断、日期分组
│ ├── highlight.ts # 代码高亮
│ ├── markdown.ts # Markdown 渲染
│ ├── segments.ts # 消息分段(thinking/tool/text)
│ ├── apiError.ts / avatar.ts / confirmDelete.ts / date.ts / fileIcon.ts /
│ │ folderTree.ts / iconLibrary.ts / pageConfigDefaults.ts / roles.ts /
│ │ scroll.ts / xlsxRange.ts / codeExecParser.ts / codeExecUtils.ts
│ └── index.ts # Barrel export
│
├── styles/ # 18 个 CSS 模块
│ ├── variables.css # CSS 自定义属性(颜色、阴影)
│ ├── common.css # 全局工具类
│ ├── chat.css / sidebar.css / catalog.css / tool.css
│ ├── automation.css / automation-timeline.css / batch(并入相关文件)
│ ├── canvas.css / config.css / mcp.css / myspace.css / plan.css
│ └── projects.css / search-modal.css / settings.css / skill-distill.css / team-folder.css
核心原则: 按功能分组,每组有 index.ts barrel export。
2. 组件规范
2.1 组件文件结构
import React, { useState, useMemo, useCallback, useRef } from 'react';
import { Button, Input, message } from 'antd';
import { useChatStore } from '../../stores';
import type { ChatMessage } from '../../types';
import './styles.css';
interface MyComponentProps {
chatId: string;
onClose: () => void;
items?: SomeItem[];
}
export function MyComponent({ chatId, onClose, items = [] }: MyComponentProps) {
const { store, currentChatId } = useChatStore();
const [loading, setLoading] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const filteredItems = useMemo(() => {
return items.( item.);
}, [items]);
handleSubmit = ( () => {
();
{
();
message.();
} (e) {
message.();
} {
();
}
}, [chatId]);
(!items.) {
;
}
(
);
}
2.2 组件规则
- 命名: PascalCase,一个文件一个组件
- 导出: 使用 named export(
export function),非 default export
- Props: 必须定义 TypeScript 接口,以
Props 结尾
- Hooks: 必须在组件顶层调用,不能在条件/循环中
- 状态: 全局状态用 Zustand Store,局部状态用 useState
- 性能: 用
useMemo 缓存计算,useCallback 稳定函数引用
- 错误处理: try-catch +
message.error() 提示用户
- 注册: 新组件必须在对应目录的
index.ts 中 export
3. Zustand Store 规范
3.1 Store 定义模板
import { create } from 'zustand';
interface MyState {
items: Item[];
loading: boolean;
selectedId: string | null;
setItems: (items: Item[]) => void;
setLoading: (loading: boolean) => void;
selectItem: (id: string | null) => void;
fetchItems: () => Promise<void>;
}
export const useMyStore = create<MyState>((set, get) => ({
items: [],
loading: false,
selectedId: null,
setItems: (items) => set({ items }),
setLoading: ({ loading }),
: ({ : id }),
: () => {
({ : });
{
data = ();
({ : data });
} (e) {
.(, e);
} {
({ : });
}
},
}));
3.2 Store 使用方式
const { items, loading, fetchItems } = useMyStore();
const currentItems = useMyStore.getState().items;
useChatStore.getState().updateStore((prev) => ({
chats: { ...prev.chats, [id]: updatedChat },
order: [id, ...prev.order.filter(x => x !== id)],
}));
3.3 Store 规则
- 接口驱动:先定义 interface,再实现
- 状态与 Actions 在同一接口中
- 简单更新用
set(),复杂逻辑用 get() 读取当前值
- 需要持久化的状态在 setter 中调用
localStorage 保存
- Set/Map 类型用于内部状态(如
expandedToolCalls: Set<string>)
4. 自定义 Hook 规范
export function useMyFeature(apiUrl: string) {
const { someState } = useSomeStore();
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, []);
useEffect(() => {
}, [someState, apiUrl]);
return {
doSomething,
timerRef,
};
}
规则:
- Hook 文件放在
hooks/ 目录
- 复杂初始化逻辑提取到 Hook 中,保持组件简洁
- 在
hooks/index.ts 中 barrel export
5. API 调用规范
5.1 API 客户端(api.ts)
interface ApiEnvelope<T> {
code: number;
message: string;
data: T;
trace_id?: string;
timestamp?: number;
}
export async function authFetch(url: string, init?: RequestInit): Promise<Response> {
const headers = new Headers(init?.headers || {});
const token = getAuthToken();
if (token) headers.set('Authorization', `Bearer ${token}`);
const response = await fetch(url, { ...init, headers });
if (response.status === 401) onUnauthorized?.(loginUrl);
return response;
}
const r = await authFetch(`${apiUrl}/v1/catalog`);
const { code, data } = await r.json();
5.2 规则
- 所有 API 调用通过
authFetch()
- 请求路径使用
/v1/ 前缀
- 响应解包:检查
code 字段,取 data
- 401 响应自动触发登录弹窗
6. TypeScript 类型规范
6.1 核心类型(types.ts)
export type PanelKey =
| 'chat' | 'skills' | 'agents' | 'mcp' | 'kb' | 'docs'
| 'app_center' | 'settings' | 'share_records' | 'my_space'
| 'ability_center' | 'lab' | 'projects' | 'project_detail';
export interface ChatMessage {
role: ChatRole;
content: string;
isMarkdown?: boolean;
ts: number;
messageId?: string;
toolCalls?: ToolCall[];
thinking?: ThinkingBlock[];
segments?: MessageSegment[];
citations?: CitationItem[];
followUpQuestions?: string[];
attachments?: Array<{ name: string; mime_type?: string; file_id?: string; download_url?: string }>;
}
{
: ;
: ;
: ;
: ;
?: [];
?: ;
}
6.2 类型规则
- 所有共享类型定义在
types.ts
- 组件 Props 接口定义在组件文件中
- 使用
interface 而非 type(除非需要联合类型)
- 避免
any,必要时用 unknown + 类型守卫
- 枚举类型用
type = 'a' | 'b' | 'c' 字面量联合
7. CSS 样式规范
完整设计规范: 参见 references/ui-design-spec.md,包含颜色系统、字体排版、图标、圆角、间距等全部视觉标准。
7.1 命名规则
.jx-chatArea { }
.jx-chatArea-header { }
.jx-chatArea-content { }
.jx-chatArea--empty { }
7.2 核心颜色(摘要)
:root {
--color-primary: #126DFF;
--color-primary-hover: #3C87FF;
--color-primary-active: #0862F3;
--color-primary-bg: #DBE9FF;
--color-primary-light: #EBF2FF;
--color-text: #262626;
--color-text-secondary: #4D4D4D;
--color-text-tertiary: #808080;
--color-text-placeholder: #B3B3B3;
--color-border: #E3E6EA;
--color-fill: #D8DBE2;
--color-bg-gray: #F5F6F7;
--color-success: #02B589;
--color-warning: #F8AB42;
--color-error: #FC5D5D;
--radius-xs: 4px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 20px;
: ; : ;
: ; : ; : ;
: , , , sans-serif;
: , , , sans-serif;
}
7.3 样式规则
- 前缀: 所有自定义类使用
.jx- 前缀
- BEM: 子元素用
-,修饰符用 --
- 颜色: 必须使用 CSS 变量,禁止硬编码色值
- 间距: 以 4px 为基准,常用 4/8/16/20/40px
- 圆角: 按组件类型使用对应层级(4/8/12/20px)
- 字号: 12/14/16/18/22/44px,默认正文 14px
- 字体: 中文 PingFang SC / 微软雅黑,特殊数字用 DingTalk Sans
- Icon: 推荐 IconPark 图标库,蓝色系为主
- 布局: Flexbox 为主
- 文件: 按功能放在
styles/ 对应文件中
- Ant Design: 通过 ConfigProvider token 定制主题色,不覆写组件内部样式(详见
ui-design-spec.md 第 13 节)
8. SSE 流式处理规范
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let sseBuffer = '';
断线续播:run_started 给出的 run_id 可用 GET /v1/chats/stream/{run_id} 重新跟随后台 run。
9. 文件上传规范
const promise = Promise.all([
parseFileContent(file, apiUrl),
uploadFileToOSS(file, apiUrl, chatId),
]).then(([content, { file_id, download_url }]) => ({
name: file.name,
content,
file_id,
download_url,
mime_type: file.type,
}));
10. localStorage 持久化
export const STORAGE_KEY = 'hugagent_ui_chat_history_v2';
export const ENABLE_KEY = 'hugagent_ui_enabled_catalog_v1';
export function nowId(prefix = 'chat') {
const d = new Date();
const pad = (n: number) => String(n).padStart(2, '0');
return `${prefix}_${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
}
setStore: (store) => {
set({ store });
saveChatStore(store);
},
11. 状态管理模式
乐观更新
toggleItem: async (kind, itemId, enabled) => {
const { catalog } = get();
const updated = {
...catalog,
[kind]: catalog[kind].map(item =>
item.id === itemId ? { ...item, enabled } : item,
),
};
set({ catalog: updated });
saveCatalog(updated);
try {
await updateCatalogItem(kind, itemId, enabled);
} catch (e) {
console.error('Sync failed:', e);
}
},
派生状态(useMemo)
const filteredList = useMemo(() => {
return list
.filter(item => matchesFilter(item, filter))
.sort((a, b) => b.updatedAt - a.updatedAt);
}, [list, filter]);
12. Nginx 代理配置
# SPA 路由回退
location / {
try_files $uri $uri/ /index.html;
}
# API 代理(支持 SSE)
location /api/ {
proxy_pass http://backend:${BACKEND_PORT}/;
client_max_body_size 50m;
proxy_buffering off; # SSE 必须关闭缓冲
proxy_cache off;
proxy_read_timeout 300s;
}
# 静态资源(长期缓存)
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
# index.html(不缓存)
location = /index.html {
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
}
13. Docker 开发流程
docker-compose up -d --build frontend
cd src/frontend
npm run build
docker cp dist/. hugagent-frontend:/usr/share/nginx/html/
docker exec hugagent-frontend nginx -s reload
cd src/frontend && npm run lint
14. 新功能开发检查清单