feat(extension): 添加调试器功能并优化界面布局

- 集成 Chrome 调试器 API 支持 attach/detach 功能
- 新增 tabUtils 工具模块用于获取活动标签页 ID
- 在背景脚本中添加标签页激活和更新事件监听
- 使用 Material-UI 容器和堆叠组件重构页面布局
- 移除 Chromium 启动参数中的自动打开开发者工具选项
- 更新权限配置添加调试器相关权限
- 在测试页面添加多个调试功能按钮和状态管理
This commit is contained in:
雨霖铃
2026-02-15 01:35:23 +08:00
parent c01659c9f9
commit 19b7ab141a
8 changed files with 129 additions and 44 deletions
+2 -2
View File
@@ -40,7 +40,7 @@ export function TimestampExecution(): JSX.Element {
const toggleButtonText = isRunningTimestamp ? '停止' : '开始'; const toggleButtonText = isRunningTimestamp ? '停止' : '开始';
return ( return (
<Paper elevation={3} sx={{ p: 2, my: 2, borderRadius: 2, minWidth: 320, textAlign: 'center' }}> <Paper elevation={3} sx={{ p: 2, my: 2, borderRadius: 2 }}>
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
@@ -53,7 +53,7 @@ export function TimestampExecution(): JSX.Element {
}} }}
> >
<Typography <Typography
variant="h4" variant="h5"
component="span" component="span"
sx={{ sx={{
fontFamily: 'monospace', fontFamily: 'monospace',
+9 -31
View File
@@ -2,6 +2,7 @@ import '../.wxt/types/imports.d.ts';
import { browser } from 'wxt/browser'; import { browser } from 'wxt/browser';
import { downloadHtmlInBackground } from '@/utils/recordUtils.tsx'; import { downloadHtmlInBackground } from '@/utils/recordUtils.tsx';
import { sendMessage, onMessage } from '@/utils/messages'; import { sendMessage, onMessage } from '@/utils/messages';
import { getActiveTabId } from '@/utils/tabUtils';
const events: unknown[] = []; const events: unknown[] = [];
@@ -52,6 +53,14 @@ export default defineBackground(() => {
); );
}); });
chrome.tabs.onActivated.addListener((activeInfo) => {
console.log('当前活跃的 Tab ID:', activeInfo.tabId);
});
chrome.tabs.onUpdated.addListener((tabId) => {
console.log('加载完成的 Tab ID:', tabId);
});
onMessage('popup:check-status', async (message) => { onMessage('popup:check-status', async (message) => {
console.log(`[background] popup:check-status: ${message}`); console.log(`[background] popup:check-status: ${message}`);
const obj = { const obj = {
@@ -109,35 +118,4 @@ export default defineBackground(() => {
events.push(eventsList); events.push(eventsList);
return true; return true;
}); });
async function getActiveTabId() {
const activeTabs = await chrome.tabs.query({
active: true,
currentWindow: true,
});
const activeTab = activeTabs[0];
if (!activeTab) {
throw new Error('No active tab found.');
}
// 检查URL是否受限
if (activeTab?.url) {
const restrictedProtocols = [
'chrome:',
'chrome-extension:',
'about:',
'edge:',
'view-source:',
'data:',
'file:',
];
if (restrictedProtocols.some((protocol) => activeTab.url!.startsWith(protocol))) {
throw new Error(`Cannot send message to a restricted URL: ${activeTab.url}`);
}
}
const sendTo = activeTab.id;
return sendTo!;
}
}); });
@@ -1,7 +1,7 @@
import { useEffect, useState, useMemo } from 'react'; import { useEffect, useState, useMemo } from 'react';
import { AppState } from '../types'; import { AppState } from '../types';
import { sendMessage, onMessage } from '@/utils/messages'; import { sendMessage, onMessage } from '@/utils/messages';
import { Button } from '@mui/material'; import { Button, Container, Stack } from '@mui/material';
const RecordeReplayPage = () => { const RecordeReplayPage = () => {
const [status, setStatus] = useState<AppState>(AppState.READ); const [status, setStatus] = useState<AppState>(AppState.READ);
@@ -54,8 +54,8 @@ const RecordeReplayPage = () => {
}; };
return ( return (
<div style={{ padding: '20px' }}> <Container maxWidth={false} sx={{ py: 2.5 }}>
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}> <Stack direction="row" spacing={1.25} sx={{ mb: 2.5 }}>
<Button <Button
variant="contained" variant="contained"
size="medium" size="medium"
@@ -64,8 +64,8 @@ const RecordeReplayPage = () => {
> >
{isRecording ? '停止录制' : '开始录制'} {isRecording ? '停止录制' : '开始录制'}
</Button> </Button>
</div> </Stack>
</div> </Container>
); );
}; };
+68
View File
@@ -1,16 +1,84 @@
import { sendMessage } from '@/utils/messages'; import { sendMessage } from '@/utils/messages';
import { Button } from '@mui/material'; import { Button } from '@mui/material';
import { useState, useEffect } from 'react';
const TestPage = () => { const TestPage = () => {
const [tabId, setTabId] = useState(-1);
useEffect(() => {
(async () => {
const activeTabs = await chrome.tabs.query({
active: true,
currentWindow: true,
});
console.log(`activeTabs:`, activeTabs[0].id);
if (activeTabs[0].id && activeTabs[0].id > 0) setTabId(activeTabs[0].id);
else throw new Error('no active tab');
})();
});
const handleSeedMessage = async () => { const handleSeedMessage = async () => {
const status = await sendMessage('popup:check-status'); const status = await sendMessage('popup:check-status');
console.log(`[popup]status: ${status}`); console.log(`[popup]status: ${status}`);
}; };
const handleAttach = () => {
try {
chrome.debugger.attach({ tabId: tabId }, '1.2', () => {
console.log('[debugger] attached');
});
} catch (err) {
console.error(err);
}
};
const handleDetach = async () => {
try {
chrome.debugger.detach({ tabId: tabId }, () => {
console.log('[debugger] detached');
});
} catch (err) {
console.error(err);
}
};
const handleGetTarget = () => {
chrome.debugger.getTargets((targets) => {
console.log('[debugger] targets:', targets);
});
};
const enableRuntime = () => {
chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.enable', {}, (res) => {
console.log('[debugger] Runtime.enable:', res);
});
};
const disableRuntime = () => {
chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.disable', {}, (res) => {
console.log('[debugger] Runtime.disable:', res);
});
};
return ( return (
<div> <div>
<Button variant="contained" color="primary" onClick={handleSeedMessage}> <Button variant="contained" color="primary" onClick={handleSeedMessage}>
</Button> </Button>
<Button variant="contained" color="primary" onClick={handleAttach}>
attach
</Button>
<Button variant="contained" color="primary" onClick={handleDetach}>
detach
</Button>
<Button variant="contained" color="primary" onClick={handleGetTarget}>
getTargets
</Button>
<Button variant="contained" color="primary" onClick={enableRuntime}>
enableRuntime
</Button>
<Button variant="contained" color="primary" onClick={disableRuntime}>
disableRuntime
</Button>
</div> </div>
); );
}; };
+5 -2
View File
@@ -1,14 +1,17 @@
import { TimestampToDatetime } from '@/components/TimestampToDatetime'; import { TimestampToDatetime } from '@/components/TimestampToDatetime';
import { DatetimeToTimestamp } from '@/components/DatetimeToTimestamp'; import { DatetimeToTimestamp } from '@/components/DatetimeToTimestamp';
import { TimestampExecution } from '@/components/TimestampExecution'; import { TimestampExecution } from '@/components/TimestampExecution';
import { Container, Box } from '@mui/material';
const TimestampPage = () => { const TimestampPage = () => {
return ( return (
<div className="timestamp-utils"> <Container maxWidth="md" sx={{ py: 2 }}>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '100%' }}>
<TimestampExecution /> <TimestampExecution />
<TimestampToDatetime /> <TimestampToDatetime />
<DatetimeToTimestamp /> <DatetimeToTimestamp />
</div> </Box>
</Container>
); );
}; };
+35
View File
@@ -0,0 +1,35 @@
import { browser } from 'wxt/browser';
/**
* 获取当前活动标签页的 ID
* @returns Promise<number | undefined> 返回活动标签页的 ID,如果未找到则返回 undefined
*/
export async function getActiveTabId(): Promise<number | undefined> {
const activeTabs = await browser.tabs.query({
active: true,
currentWindow: true,
});
const activeTab = activeTabs[0];
if (!activeTab) {
throw new Error('No active tab found.');
}
// 检查URL是否受限
if (activeTab?.url) {
const restrictedProtocols = [
'chrome:',
'chrome-extension:',
'about:',
'edge:',
'view-source:',
'data:',
'file:',
];
if (restrictedProtocols.some((protocol) => activeTab.url!.startsWith(protocol))) {
throw new Error(`Cannot send message to a restricted URL: ${activeTab.url}`);
}
}
return activeTab.id;
}
+1 -1
View File
@@ -2,5 +2,5 @@ import { defineWebExtConfig } from 'wxt';
export default defineWebExtConfig({ export default defineWebExtConfig({
startUrls: ['https://www.baidu.com', 'chrome://extensions/'], startUrls: ['https://www.baidu.com', 'chrome://extensions/'],
chromiumArgs: ['chrome://extensions/', '--auto-open-devtools-for-tabs'], chromiumArgs: ['chrome://extensions/'],
}); });
+1
View File
@@ -16,6 +16,7 @@ export default defineConfig({
'tabs', 'tabs',
'offscreen', 'offscreen',
'downloads', 'downloads',
'debugger',
], ],
host_permissions: ['<all_urls>'], host_permissions: ['<all_urls>'],
action: { action: {