feat: 添加离屏文档创建和下载功能,优化录制逻辑
This commit is contained in:
@@ -1,3 +1,27 @@
|
||||
export default defineBackground(() => {
|
||||
console.log('Hello background!', { id: browser.runtime.id });
|
||||
|
||||
chrome.runtime.onMessage.addListener(async (msg, sender, sendResponse) => {
|
||||
if (msg.type === 'CREATE_OFFSCREEN') {
|
||||
const exists = await chrome.offscreen.hasDocument();
|
||||
if (!exists) {
|
||||
await chrome.offscreen.createDocument({
|
||||
url: 'offscreen.html',
|
||||
reasons: ['DISPLAY_MEDIA', 'USER_MEDIA', 'BLOBS', 'AUDIO_PLAYBACK'],
|
||||
justification: 'rrweb record & replay',
|
||||
});
|
||||
console.log('[bg] offscreen created');
|
||||
}
|
||||
sendResponse({ ok: true });
|
||||
}
|
||||
|
||||
if (msg.type === 'DOWNLOAD') {
|
||||
console.log('[bg] DOWNLOAD received');
|
||||
chrome.downloads.download({
|
||||
url: msg.dataUrl,
|
||||
filename: `rrweb-${Date.now()}.json`,
|
||||
saveAs: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="main.tsx" type="module"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useRef } from 'react';
|
||||
console.log('[offscreen] loaded');
|
||||
|
||||
const events = [];
|
||||
|
||||
chrome.runtime.onMessage.addListener((msg) => {
|
||||
if (msg.type === 'SAVE_EVENT') {
|
||||
console.log('[offscreen] rrweb event', msg.event);
|
||||
events.push(msg.event);
|
||||
// 这里你可以存 IndexedDB / memory / file
|
||||
}
|
||||
|
||||
if (msg.type === 'SAVE_EVENTS') {
|
||||
console.log('[offscreen] SAVE_EVENTS received');
|
||||
console.log('[offscreen] total events:', events.length);
|
||||
console.log('[offscreen] events:', JSON.stringify(events));
|
||||
// const blob = new Blob([JSON.stringify(events)], {
|
||||
// type: 'application/json',
|
||||
// });
|
||||
// reader.onload = () => {
|
||||
// chrome.runtime.sendMessage({
|
||||
// type: 'DOWNLOAD',
|
||||
// dataUrl: reader.result,
|
||||
// });
|
||||
// };
|
||||
// reader.readAsDataURL(blob);
|
||||
}
|
||||
});
|
||||
@@ -311,7 +311,6 @@ body {
|
||||
|
||||
.nav-list {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { HashRouter as Router, Routes, Route, NavLink } from 'react-router-dom';
|
||||
import { useState, useEffect } from 'react';
|
||||
import TimestampPage from '../../pages/TimestampPage';
|
||||
// import {RecordeReplayPage} from '../../pages/RecordeReplayPage';
|
||||
import RecordeReplayPage from '../../pages/RecordeReplayPage';
|
||||
import './App.css';
|
||||
|
||||
const navItems = [
|
||||
{ path: '/', label: '时间戳', element: <TimestampPage /> },
|
||||
// {path: '/dzmy', label: '电子木鱼', element: <ElectronicWoodenFishPage/>},
|
||||
// { path: '/recorde-replay', label: '录制与回放', element: <RecordeReplayPage /> },
|
||||
{ path: '/recorde-replay', label: '录制与回放', element: <RecordeReplayPage /> },
|
||||
];
|
||||
|
||||
function App() {
|
||||
@@ -43,7 +43,7 @@ function App() {
|
||||
const collapsedNavItems = navItems.slice(visibleItems);
|
||||
|
||||
return (
|
||||
<Router future={{v7_startTransition: true, v7_relativeSplatPath: true}}>
|
||||
<Router future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
||||
<div className="app">
|
||||
<nav className="nav">
|
||||
<div className="nav-content">
|
||||
|
||||
+22
-17
@@ -1,42 +1,47 @@
|
||||
import {useRef, useState} from "react";
|
||||
import {record} from "rrweb";
|
||||
import { useRef, useState } from 'react';
|
||||
import { record } from 'rrweb';
|
||||
|
||||
export const useRecorder = () => {
|
||||
// 存储录制事件
|
||||
const eventRef = useRef([]);
|
||||
// 存储停止录制函数
|
||||
const stopFnRef = useRef();
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
|
||||
|
||||
const startRecord = async () => {
|
||||
if (isRecording) return;
|
||||
|
||||
|
||||
await chrome.runtime.sendMessage({ type: 'CREATE_OFFSCREEN' });
|
||||
setIsRecording(true);
|
||||
|
||||
|
||||
// 启动录制
|
||||
stopFnRef.current = record({
|
||||
emit(event) {
|
||||
// 存储数据
|
||||
eventRef.current.push(event);
|
||||
}
|
||||
// eventRef.current.push(event);
|
||||
chrome.runtime.sendMessage({ type: 'SAVE_EVENT', event });
|
||||
},
|
||||
});
|
||||
|
||||
console.log('[content] rrweb started');
|
||||
};
|
||||
|
||||
|
||||
const stopRecord = () => {
|
||||
if (!isRecording) return;
|
||||
|
||||
|
||||
setIsRecording(false);
|
||||
|
||||
|
||||
// 停止录制
|
||||
if (stopFnRef.current) {
|
||||
stopFnRef.current();
|
||||
chrome.runtime.sendMessage({ type: 'SAVE_EVENTS' });
|
||||
stopFnRef.current?.();
|
||||
|
||||
console.log('[content] rrweb stopped');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
startRecord,
|
||||
stopRecord,
|
||||
isRecording,
|
||||
getEvents: () => eventRef.current
|
||||
}
|
||||
};
|
||||
getEvents: () => eventRef.current,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import React from 'react';
|
||||
// import {sendRecordCommand} from '../services/bridge'
|
||||
// import {storage} from "../services/storage";
|
||||
import { useRecorder } from '../hooks/useRecorder';
|
||||
import { downloadHtml } from '../utils/recordUtils';
|
||||
|
||||
@@ -32,7 +29,7 @@ const RecordeReplayPage = () => {
|
||||
// setIsRecording(false);
|
||||
// await storage.remove('isRecording');
|
||||
stopRecord();
|
||||
downloadHtml(getEvents());
|
||||
// downloadHtml(getEvents());
|
||||
// console.log('events', getEvents());
|
||||
} catch (e) {
|
||||
console.error('Error stopping recording:', e);
|
||||
|
||||
+2
-8
@@ -7,13 +7,7 @@ export default defineConfig({
|
||||
name: 'Testing Tools',
|
||||
version: '1.0',
|
||||
description: '测试工具',
|
||||
permissions: [
|
||||
'storage',
|
||||
'clipboardWrite',
|
||||
'activeTab',
|
||||
'scripting',
|
||||
'tabs',
|
||||
],
|
||||
permissions: ['storage', 'clipboardWrite', 'activeTab', 'scripting', 'tabs', 'offscreen'],
|
||||
action: {
|
||||
default_title: 'Testing Tools',
|
||||
},
|
||||
@@ -28,4 +22,4 @@ export default defineConfig({
|
||||
128: 'favicon.ico',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user