340 lines
5.8 KiB
Markdown
340 lines
5.8 KiB
Markdown
|
|
# 前端交互设计
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
PR-Helper 前端采用渐进式增强设计,结合 HTMX、D3.js、diff2html 等库,提供流畅的交互体验。
|
|||
|
|
|
|||
|
|
## 技术栈
|
|||
|
|
|
|||
|
|
| 技术 | 用途 |
|
|||
|
|
|------|------|
|
|||
|
|
| Go html/template | 服务端渲染 |
|
|||
|
|
| HTMX 2.x | 动态交互 |
|
|||
|
|
| D3.js 7.x | Git 图形可视化 |
|
|||
|
|
| diff2html 3.x | 差异渲染 |
|
|||
|
|
| highlight.js 11.x | 语法高亮 |
|
|||
|
|
| marked.js | Markdown 渲染 |
|
|||
|
|
| Tailwind CSS | 样式框架 |
|
|||
|
|
|
|||
|
|
## 核心组件
|
|||
|
|
|
|||
|
|
### 1. SSE 客户端 (sse.js)
|
|||
|
|
|
|||
|
|
#### 设计目标
|
|||
|
|
|
|||
|
|
- 支持 POST 请求的 SSE(EventSource 只支持 GET)
|
|||
|
|
- 流式读取响应体
|
|||
|
|
- 事件分发机制
|
|||
|
|
|
|||
|
|
#### API 设计
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const SSE = {
|
|||
|
|
async post(url, body, handlers = {}) {
|
|||
|
|
// 返回 controller,支持 abort()
|
|||
|
|
return { abort: () => controller.abort() };
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 使用示例
|
|||
|
|
SSE.post('/api/repos/1/review', { base: 'main', head: 'feature' }, {
|
|||
|
|
start: (data) => console.log('开始', data),
|
|||
|
|
content: (data) => console.log('内容', data.content),
|
|||
|
|
suggestion: (data) => console.log('建议', data),
|
|||
|
|
summary: (data) => console.log('汇总', data),
|
|||
|
|
done: () => console.log('完成'),
|
|||
|
|
error: (data) => console.error('错误', data.message)
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 实现细节
|
|||
|
|
|
|||
|
|
- 使用 `fetch()` + `ReadableStream`
|
|||
|
|
- 手动解析 SSE 协议(event: / data:)
|
|||
|
|
- 支持中断请求
|
|||
|
|
- 自动处理 JSON 解析
|
|||
|
|
|
|||
|
|
### 2. Git 图形 (graph.js)
|
|||
|
|
|
|||
|
|
#### 功能特性
|
|||
|
|
|
|||
|
|
- D3.js SVG 渲染
|
|||
|
|
- 多分支彩色泳道
|
|||
|
|
- 交互式 base/head 选择
|
|||
|
|
- 分支标签右对齐
|
|||
|
|
- 图例显示
|
|||
|
|
|
|||
|
|
#### 布局算法
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
assignLanes() {
|
|||
|
|
// 1. HEAD 分支获得泳道 0
|
|||
|
|
// 2. 其他分支按顺序分配泳道
|
|||
|
|
// 3. 合并提交的第二父提交获得新泳道
|
|||
|
|
// 4. 未访问的提交分配到泳道 0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 交互设计
|
|||
|
|
|
|||
|
|
- **点击**: 选择 base/head 提交
|
|||
|
|
- **悬停**: 高亮节点
|
|||
|
|
- **选择状态**:
|
|||
|
|
- 绿色: base 提交
|
|||
|
|
- 橙色: head 提交
|
|||
|
|
- 蓝色: 普通提交
|
|||
|
|
|
|||
|
|
#### 回调机制
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
gitGraph.onSelectionChange = (baseHash, headHash) => {
|
|||
|
|
// 更新 base/head 选择
|
|||
|
|
// 触发差异加载
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 差异查看器 (diff-viewer.js)
|
|||
|
|
|
|||
|
|
#### 功能特性
|
|||
|
|
|
|||
|
|
- diff2html 渲染
|
|||
|
|
- 文件树侧边栏
|
|||
|
|
- 增量渲染(非阻塞)
|
|||
|
|
- 懒加载语法高亮
|
|||
|
|
- 滚动同步
|
|||
|
|
- 大型差异保护
|
|||
|
|
|
|||
|
|
#### 增量渲染
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
_renderBatch(outputFormat, startIndex) {
|
|||
|
|
const FILES_PER_BATCH = 5;
|
|||
|
|
// 每帧渲染 5 个文件
|
|||
|
|
// 使用 requestAnimationFrame
|
|||
|
|
// 避免阻塞主线程
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 大型差异保护
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
_isTooLarge(files) {
|
|||
|
|
const MAX_FILES = 300;
|
|||
|
|
const MAX_LINES = 10000;
|
|||
|
|
return files.length > MAX_FILES || (totalAdd + totalDel) > MAX_LINES;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 超过阈值显示警告
|
|||
|
|
- 用户确认后强制渲染
|
|||
|
|
- 避免浏览器卡顿
|
|||
|
|
|
|||
|
|
#### 文件树
|
|||
|
|
|
|||
|
|
- 目录折叠/展开
|
|||
|
|
- 文件类型图标
|
|||
|
|
- 变更统计(+/-)
|
|||
|
|
- 严重程度标记
|
|||
|
|
|
|||
|
|
#### 滚动同步
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
_setupScrollSpy() {
|
|||
|
|
// IntersectionObserver 监听文件可见性
|
|||
|
|
// 自动高亮侧边栏对应文件
|
|||
|
|
// 点击侧边栏滚动到文件
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 内联建议 (review-inline.js)
|
|||
|
|
|
|||
|
|
#### 功能
|
|||
|
|
|
|||
|
|
- 在差异中嵌入 AI 建议
|
|||
|
|
- 按严重程度着色
|
|||
|
|
- 支持文件级和行级建议
|
|||
|
|
|
|||
|
|
#### 插入机制
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
insertSuggestion(filename, line, side, severity, content, suggestionId) {
|
|||
|
|
// 1. 懒加载文件索引
|
|||
|
|
// 2. 查找目标行
|
|||
|
|
// 3. 创建建议卡片
|
|||
|
|
// 4. 插入到行后
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 严重程度样式
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const severityStyles = {
|
|||
|
|
critical: 'border-l-4 border-red-500 bg-red-50',
|
|||
|
|
warning: 'border-l-4 border-yellow-500 bg-yellow-50',
|
|||
|
|
info: 'border-l-4 border-green-500 bg-green-50',
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. Markdown 渲染 (markdown.js)
|
|||
|
|
|
|||
|
|
#### 功能
|
|||
|
|
|
|||
|
|
- 使用 marked.js 渲染
|
|||
|
|
- 代码块语法高亮
|
|||
|
|
- 支持内联代码
|
|||
|
|
|
|||
|
|
#### 集成
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
function renderMarkdown(text) {
|
|||
|
|
return marked.parse(text, {
|
|||
|
|
highlight: function(code, lang) {
|
|||
|
|
return hljs.highlightAuto(code).value;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6. 笔记编辑器 (note-editor.js)
|
|||
|
|
|
|||
|
|
#### 功能
|
|||
|
|
|
|||
|
|
- 内联编辑审查笔记
|
|||
|
|
- 自动保存
|
|||
|
|
- 三种作用域
|
|||
|
|
|
|||
|
|
## 页面结构
|
|||
|
|
|
|||
|
|
### 首页 (/)
|
|||
|
|
|
|||
|
|
- 仓库列表
|
|||
|
|
- 克隆表单
|
|||
|
|
- 快速操作
|
|||
|
|
|
|||
|
|
### 仓库详情页 (/repo/:id)
|
|||
|
|
|
|||
|
|
- Git 图形
|
|||
|
|
- 分支选择器
|
|||
|
|
- 差异查看器
|
|||
|
|
- PR 生成
|
|||
|
|
- AI 代码审查
|
|||
|
|
|
|||
|
|
### 设置页 (/settings)
|
|||
|
|
|
|||
|
|
- LLM 配置
|
|||
|
|
- 审查参数
|
|||
|
|
- 缓存设置
|
|||
|
|
|
|||
|
|
## HTMX 集成
|
|||
|
|
|
|||
|
|
### 动态加载
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<div hx-get="/api/repos/1/graph" hx-trigger="load">
|
|||
|
|
<!-- 图形将在这里渲染 -->
|
|||
|
|
</div>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 表单提交
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<form hx-post="/api/repos" hx-target="#repo-list">
|
|||
|
|
<input type="text" name="url" required>
|
|||
|
|
<button type="submit">克隆</button>
|
|||
|
|
</form>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 事件触发
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<button hx-post="/api/repos/1/pull"
|
|||
|
|
hx-trigger="click"
|
|||
|
|
hx-indicator="#spinner">
|
|||
|
|
拉取更新
|
|||
|
|
</button>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 响应式设计
|
|||
|
|
|
|||
|
|
### 断点
|
|||
|
|
|
|||
|
|
- 移动端: < 640px
|
|||
|
|
- 平板: 640px - 1024px
|
|||
|
|
- 桌面: > 1024px
|
|||
|
|
|
|||
|
|
### 适配策略
|
|||
|
|
|
|||
|
|
- 文件树侧边栏可折叠
|
|||
|
|
- 差异查看器自适应宽度
|
|||
|
|
- 图形可横向滚动
|
|||
|
|
|
|||
|
|
## 性能优化
|
|||
|
|
|
|||
|
|
### 1. 增量渲染
|
|||
|
|
|
|||
|
|
- 分批渲染文件
|
|||
|
|
- requestAnimationFrame
|
|||
|
|
- 避免长任务阻塞
|
|||
|
|
|
|||
|
|
### 2. 懒加载
|
|||
|
|
|
|||
|
|
- 语法高亮按需执行
|
|||
|
|
- 文件索引按需构建
|
|||
|
|
- IntersectionObserver
|
|||
|
|
|
|||
|
|
### 3. 虚拟滚动
|
|||
|
|
|
|||
|
|
- 大型差异只渲染可见部分
|
|||
|
|
- 减少 DOM 节点数
|
|||
|
|
|
|||
|
|
### 4. 缓存
|
|||
|
|
|
|||
|
|
- 静态资源缓存
|
|||
|
|
- API 响应缓存
|
|||
|
|
- 本地状态缓存
|
|||
|
|
|
|||
|
|
## 错误处理
|
|||
|
|
|
|||
|
|
### 网络错误
|
|||
|
|
|
|||
|
|
- 显示错误消息
|
|||
|
|
- 提供重试按钮
|
|||
|
|
- 自动重连(SSE)
|
|||
|
|
|
|||
|
|
### 渲染错误
|
|||
|
|
|
|||
|
|
- 降级到纯文本
|
|||
|
|
- 显示原始数据
|
|||
|
|
- 记录错误日志
|
|||
|
|
|
|||
|
|
## 无障碍
|
|||
|
|
|
|||
|
|
### 键盘导航
|
|||
|
|
|
|||
|
|
- Tab 切换焦点
|
|||
|
|
- Enter 确认选择
|
|||
|
|
- Escape 取消操作
|
|||
|
|
|
|||
|
|
### 屏幕阅读器
|
|||
|
|
|
|||
|
|
- ARIA 标签
|
|||
|
|
- 语义化 HTML
|
|||
|
|
- 焦点管理
|
|||
|
|
|
|||
|
|
## 浏览器兼容性
|
|||
|
|
|
|||
|
|
### 支持的浏览器
|
|||
|
|
|
|||
|
|
- Chrome 90+
|
|||
|
|
- Firefox 88+
|
|||
|
|
- Safari 14+
|
|||
|
|
- Edge 90+
|
|||
|
|
|
|||
|
|
### 依赖的现代 API
|
|||
|
|
|
|||
|
|
- fetch
|
|||
|
|
- ReadableStream
|
|||
|
|
- IntersectionObserver
|
|||
|
|
- requestAnimationFrame
|
|||
|
|
- CSS Grid/Flexbox
|