feat: enhance web extension with export functionality and utility improvements
Some checks failed
Tests / Tests (push) Has been cancelled
ESLint Check / ESLint Check and Report Upload (push) Has been cancelled
Prettier Check / Format Check (push) Has been cancelled
Prettier Check / Format Code (push) Has been cancelled
ESLint Check / Build Base for Bundle Size Comparison (push) Has been cancelled

- Add export functionality to SessionList and Player pages
- Add new utility modules: dataOperations, format, path, settings
- Update manifest with export and download permissions
- Enhance storage utility with new data operations
- Add various test scripts and documentation files
This commit is contained in:
xugp
2026-04-16 10:15:10 +08:00
parent 2a7084db5b
commit 71438691b3
38 changed files with 11241 additions and 2338 deletions

View File

@@ -0,0 +1,715 @@
# rrweb 浏览器插件开发计划
## 📋 项目概述
基于 rrweb 开发一个功能完善的浏览器插件,支持录制用户操作、本地回放、多格式导出等功能。
---
## 🎯 开发阶段规划
### 阶段一基础录制功能1-2周
#### 核心功能清单
-**录制开始/停止**
-**基本数据收集**(事件、时间戳)
-**简单 popup UI**(状态显示、控制按钮)
-**文件保存到默认路径**(自动 JSON 导出)
-**快捷键支持**Ctrl+Shift+R 开始/停止)
#### UI 设计(简化版)
```
┌─────────────────────────────────────┐
│ 📹 rrweb 录制插件 │
├─────────────────────────────────────┤
│ 状态: [❌] 停止录制 │
│ │
│ [🎬 开始录制] [⚙️ 设置] │
│ │
│ 快捷键: Ctrl+Shift+R │
│ 帮助 📄 │
└─────────────────────────────────────┘
```
#### 技术实现
- Background Service 管理录制状态
- Content Script 注入录制脚本
- 基础的数据收集和存储
- 简单的 popup 界面
---
### 阶段二UI 增强和回放2-3周
#### 功能增强
- 🎨 **现代化 UI 设计**
- 使用 Tailwind CSS
- 添加图标和动画
- 响应式布局
- 🎬 **本地回放功能**
- 在 popup 中预览录制
- 基本播放控制(播放/暂停/进度条)
- 📊 **录制状态显示**
- 实时事件计数
- 录制时长显示
- 文件大小显示
- 🔧 **设置页面**
- 基础配置选项
- 隐私控制设置
#### UI 设计(增强版)
```
┌─────────────────────────────────────┐
│ 📹 rrweb 录制插件 │
├─────────────────────────────────────┤
│ ● 正在录制 [00:15:23] │
│ 事件数: 1,234 | 文件: 2.3MB │
│ │
│ ┌─────────────────────────────┐ │
│ │ [🎬 实时预览] │ │
│ │ 👆 点击、输入、滚动等操作 │ │
│ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────────┐ │
│ │ [▶ 播放录制] [⏸ 暂停] │ │
│ │ [📤 导出] [🗑️ 删除] │ │
│ └─────────────────────────────┘ │
│ │
│ 设置 ⚙️ 历史 📄 快捷键 ⌨️ │
└─────────────────────────────────────┘
```
#### 技术实现
- 现代化 UI 组件设计
- 录制数据实时预览
- 基础回放功能实现
- 设置页面配置管理
---
### 阶段三数据管理和导出1-2周
#### 核心功能
- 💾 **自定义保存路径**
- 用户选择文件夹
- 支持自定义文件名格式
- 路径持久化存储
- 📤 **多格式导出**
- JSON 原始数据
- HTML 回放页面
- ZIP 压缩包
- 📄 **录制历史管理**
- 录制列表显示
- 详情预览
- 批量操作
- 🗑️ **数据清理**
- 手动删除
- 自动清理选项
#### 历史管理界面
```
┌─────────────────────────────────────┐
📄 录制历史 │
├─────────────────────────────────────┤
│ 🔍 搜索: [_________________] │
│ │
│ ┌─────────────────────────────┐ │
│ │ 🎬 product-demo.web.app │ │
│ │ ● 02:35 | 342事件 | 1.2MB │ │
│ │ 2024-01-15 14:30 │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ [▶ 播放] [📤 导出] [🗑️] │ │ │
│ │ └─────────────────────────┘ │ │
│ │ │ │
│ │ 🎬 login-test.com │ │
│ │ ● 01:15 | 156事件 | 0.8MB │ │
│ │ 2024-01-15 10:20 │ │
│ └─────────────────────────────┘ │
│ │
│ [←] [1/5] [→] 清空全部 🗑️ │
└─────────────────────────────────────┘
```
#### 技术实现
- 文件路径管理系统
- 多格式导出工具
- 录制历史数据管理
- 用户配置持久化
---
## 🛠️ 技术架构设计
### 1. 项目结构
```
web-extension/
├── manifest.json # 扩展配置
├── popup/ # 弹出窗口
│ ├── index.html # popup HTML
│ ├── popup.css # 样式文件
│ ├── popup.js # popup 脚本
│ └── components/ # UI 组件
│ ├── StatusIndicator.js
│ ├── RecordingPreview.js
│ └── ControlButtons.js
├── options/ # 设置页面
│ ├── index.html
│ ├── options.css
│ ├── options.js
│ └── settings.html
├── background/ # 后台服务
│ ├── service-worker.js
│ └── recording-manager.js
├── content/ # 内容脚本
│ ├── inject.js # 注入脚本
│ └── enhanced-recorder.js # 增强录制器
├── utils/ # 工具函数
│ ├── file-manager.js
│ ├── export-manager.js
│ └── shortcut-manager.js
├── assets/ # 资源文件
│ ├── icons/
│ └── styles/
└── lib/ # 第三方库
└── rrweb-dist/
```
### 2. 核心模块实现
#### Background Service Worker
```javascript
// background/service-worker.js
class RecordingManager {
constructor() {
this.recordings = new Map();
this.currentRecording = null;
}
async startRecording(tabId) {
try {
const recorder = new EnhancedRecorder({
tabId,
onData: (events) => this.saveEvents(tabId, events),
options: await this.getRecordingOptions()
});
this.currentRecording = {
tabId,
recorder,
startTime: Date.now(),
events: [],
status: 'recording'
};
await this.saveToStorage();
// 更新 popup 状态
chrome.runtime.sendMessage({
type: 'RECORDING_STARTED',
data: { tabId }
});
} catch (error) {
console.error('Start recording failed:', error);
}
}
async stopRecording(tabId) {
if (!this.currentRecording) return;
const recording = this.currentRecording;
recording.recorder.stop();
recording.status = 'stopped';
recording.endTime = Date.now();
// 保存文件
await this.saveRecordingFile(recording);
this.currentRecording = null;
await this.saveToStorage();
chrome.runtime.sendMessage({
type: 'RECORDING_STOPPED',
data: { tabId }
});
}
}
```
#### Enhanced Recorder
```javascript
// content/enhanced-recorder.js
class EnhancedRecorder {
constructor({ tabId, onData, options }) {
this.tabId = tabId;
this.onData = onData;
this.options = options;
this.events = [];
this.recorder = null;
this.init();
}
init() {
this.recorder = record({
emit: (event) => {
this.events.push(event);
this.onData(this.events);
},
recordCrossOriginIframes: true,
blockClass: 'rr-block',
maskTextClass: 'rr-mask',
...this.options
});
}
stop() {
if (this.recorder) {
this.recorder();
this.recorder = null;
}
}
}
```
#### File Manager
```javascript
// utils/file-manager.js
class FileManager {
constructor() {
this.settings = this.loadSettings();
}
async saveRecording(data) {
const filename = this.generateFilename();
const filepath = await this.getFilePath(filename);
// 转换为 JSON
const json = JSON.stringify({
version: '1.0',
timestamp: data.timestamp,
events: data.events,
metadata: {
duration: data.duration,
eventCount: data.events.length,
url: data.url
}
}, null, 2);
// 保存文件
await this.writeFile(filepath, json);
return {
filename,
filepath,
size: json.length
};
}
generateFilename() {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
return `rrweb-${timestamp}.json`;
}
async getFilePath(filename) {
const savePath = this.settings.savePath || this.getDefaultPath();
const fullPath = `${savePath}/${filename}`;
// 确保目录存在
await this.ensureDirectory(savePath);
return fullPath;
}
}
```
#### Export Manager
```javascript
// utils/export-manager.js
class ExportManager {
async exportJSON(events, filename) {
const data = this.prepareEventData(events);
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: 'application/json'
});
return this.downloadFile(blob, `${filename}.json`);
}
async exportHTML(events, filename) {
const html = this.generateReplayHTML(events);
const blob = new Blob([html], { type: 'text/html' });
return this.downloadFile(blob, `${filename}.html`);
}
async exportZIP(events, filename) {
const JSZip = await import('jszip');
const zip = new JSZip();
// 添加 JSON 数据
const data = this.prepareEventData(events);
zip.file('recording.json', JSON.stringify(data, null, 2));
// 添加回放 HTML
zip.file('replay.html', this.generateReplayHTML(events));
// 添加 README
zip.file('README.md', this.generateReadme(events));
const content = await zip.generateAsync({ type: 'blob' });
return this.downloadFile(content, `${filename}.zip`);
}
generateReplayHTML(events) {
return `
<!DOCTYPE html>
<html>
<head>
<title>rrweb 回放</title>
<script src="https://unpkg.com/rrweb@latest/dist/rrweb.umd.cjs"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#replayer { width: 100%; height: 600px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>rrweb 录制回放</h1>
<div id="replayer"></div>
<script>
const replayer = new rrweb.Replayer(${JSON.stringify(events)});
replayer.play();
</script>
</body>
</html>`;
}
}
```
### 3. UI 组件设计
#### Popup Main Component
```html
<!-- popup/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>rrweb 录制插件</title>
<script src="popup.js" type="module"></script>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="app">
<!-- 状态栏 -->
<div class="status-bar" id="statusBar">
<div class="status-indicator" id="statusIndicator"></div>
<span id="statusText">停止录制</span>
<span id="statsText"></span>
</div>
<!-- 预览区域 -->
<div class="preview-section" id="previewSection">
<div class="preview-container">
<iframe id="previewFrame" sandbox="allow-same-origin"></iframe>
</div>
</div>
<!-- 控制按钮 -->
<div class="controls">
<button id="recordBtn" class="btn btn-primary">
<span class="btn-icon">🎬</span>
<span class="btn-text">开始录制</span>
</button>
<button id="playBtn" class="btn btn-secondary" disabled>
<span class="btn-icon"></span>
<span class="btn-text">播放</span>
</button>
<button id="exportBtn" class="btn btn-secondary" disabled>
<span class="btn-icon">📤</span>
<span class="btn-text">导出</span>
</button>
<button id="deleteBtn" class="btn btn-danger" disabled>
<span class="btn-icon">🗑️</span>
<span class="btn-text">删除</span>
</button>
</div>
<!-- 底部导航 -->
<div class="nav-bar">
<button class="nav-item active" data-page="main">
<span class="nav-icon">📹</span>
<span class="nav-text">主页</span>
</button>
<button class="nav-item" data-page="history">
<span class="nav-icon">📄</span>
<span class="nav-text">历史</span>
</button>
<button class="nav-item" data-page="settings">
<span class="nav-icon">⚙️</span>
<span class="nav-text">设置</span>
</button>
<button class="nav-item" data-page="help">
<span class="nav-icon"></span>
<span class="nav-text">帮助</span>
</button>
</div>
</div>
</body>
</html>
```
#### CSS 样式
```css
/* popup.css */
@import 'https://cdn.tailwindcss.com';
:root {
--primary-color: #3B82F6;
--success-color: #10B981;
--danger-color: #EF4444;
}
.app {
width: 400px;
height: 600px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.status-bar {
display: flex;
align-items: center;
padding: 12px;
background: #F3F4F6;
border-bottom: 1px solid #E5E7EB;
}
.status-indicator {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
background: #9CA3AF;
}
.status-indicator.recording {
background: #EF4444;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.btn {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
border: none;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: #2563EB;
transform: translateY(-1px);
}
.btn-secondary {
background: #6B7280;
color: white;
}
.btn-secondary:hover {
background: #4B5563;
}
.btn-danger {
background: var(--danger-color);
color: white;
}
.btn-danger:hover {
background: #DC2626;
}
.nav-bar {
display: flex;
border-top: 1px solid #E5E7EB;
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 12px;
background: white;
border: none;
cursor: pointer;
transition: background 0.2s;
}
.nav-item.active {
background: #F9FAFB;
color: var(--primary-color);
}
.nav-item:hover {
background: #F3F4F6;
}
```
---
## 📊 开发时间估算
| 阶段 | 主要任务 | 预估时间 | 优先级 |
|------|---------|---------|-------|
| **阶段一** | 基础录制功能 | 1-2周 | 🏆 高 |
| | 录制核心逻辑 | 3天 | |
| | 基础 UI | 2天 | |
| | 快捷键 | 1天 | |
| | 文件保存 | 2天 | |
| **阶段二** | UI 增强和回放 | 2-3周 | 🥈 中 |
| | UI 设计优化 | 4天 | |
| | 本地回放 | 5天 | |
| | 状态管理 | 3天 | |
| | 设置页面 | 3天 | |
| **阶段三** | 数据管理和导出 | 1-2周 | 🥉 中 |
| | 自定义路径 | 3天 | |
| | 多格式导出 | 4天 | |
| | 历史管理 | 3天 | |
| | 数据清理 | 2天 | |
---
## 🎯 功能特性清单
### 基础录制功能
- [ ] 开始/停止录制
- [ ] 实时状态显示
- [ ] 事件计数
- [ ] 录制时长
- [ ] 快捷键支持
### UI 增强功能
- [ ] 现代化界面设计
- [ ] 实时预览窗口
- [ ] 录制控制按钮
- [ ] 状态指示器
- [ ] 响应式布局
### 回放功能
- [ ] 本地回放
- [ ] 播放/暂停控制
- [ ] 进度条
- [ ] 速度调节
### 数据管理
- [ ] 自定义保存路径
- [ ] 文件命名规则
- [ ] 录制历史
- [ ] 搜索功能
- [ ] 批量操作
### 导出功能
- [ ] JSON 格式导出
- [ ] HTML 回放页面
- [ ] ZIP 压缩包
- [ ] 批量导出
- [ ] 云端同步(后续版本)
### 隐私保护
- [ ] 敏感信息遮罩
- [ ] 自定义屏蔽规则
- [ ] 网站白名单
- [ ] 数据加密(后续版本)
---
## 🛠️ 技术栈
| 组件 | 技术选择 | 理由 |
|------|---------|------|
| UI Framework | Tailwind CSS + Alpine.js | 快速开发,美观 |
| State Management | Zustand/Redux | 轻量级状态管理 |
| File System | File System API | 原生文件访问 |
| Storage | IndexedDB + Chrome Storage | 大数据存储 |
| Icons | Lucide Icons | 现代化图标库 |
| Charts | Chart.js | 数据可视化 |
---
## 🚀 实施计划
### 第一步:评估现有扩展
- [ ] 测试当前 rrweb web-extension 功能
- [ ] 确认录制质量是否满足需求
- [ ] 分析需要改进的地方
### 第二步:阶段一实施
- [ ] 实现基础录制功能
- [ ] 创建简单的 popup UI
- [ ] 添加文件保存功能
- [ ] 实现快捷键支持
### 第三步:阶段二实施
- [ ] UI 设计优化
- [ ] 实现本地回放
- [ ] 添加设置页面
- [ ] 完善状态管理
### 第四步:阶段三实施
- [ ] 实现自定义保存路径
- [ ] 开发多格式导出
- [ ] 创建历史管理界面
- [ ] 添加数据清理功能
---
## 📝 注意事项
1. **浏览器兼容性**
- 支持 Chrome 88+
- 支持 Firefox 75+
- 避免使用不兼容的 API
2. **性能考虑**
- 大文件处理优化
- 内存管理
- 录制性能监控
3. **用户隐私**
- 明确的录制提示
- 敏感信息保护
- 数据安全存储
4. **用户体验**
- 流畅的界面交互
- 清晰的状态反馈
- 易用的操作流程
---
## 🎉 总结
本计划提供了 rrweb 浏览器插件开发的完整路线图,从基础录制功能到高级的数据管理和导出功能。通过分阶段实施,可以确保每个阶段都有明确的交付成果,同时保持开发的灵活性。
**主要特点:**
- ✅ 功能完整,覆盖录制、回放、管理、导出
- ✅ 现代化 UI 设计,用户体验良好
- ✅ 技术方案成熟,可扩展性强
- ✅ 分阶段实施,风险可控
**预期成果:**
一个功能完善、界面美观、易于使用的浏览器录制插件。