feat: 添加 husky 和 lint-staged 支持,配置 prettier 进行代码格式化
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
npx lint-staged
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"jsxSingleQuote": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"endOfLine": "lf"
|
||||||
|
}
|
||||||
+32
-46
@@ -1,5 +1,4 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useEffect, useState, useCallback } from 'react';
|
||||||
import { copyToClipboard } from '@/utils/chromeUtils';
|
|
||||||
|
|
||||||
const CopyButton = ({
|
const CopyButton = ({
|
||||||
text = '要复制的文本',
|
text = '要复制的文本',
|
||||||
@@ -7,14 +6,13 @@ const CopyButton = ({
|
|||||||
className = 'action-btn',
|
className = 'action-btn',
|
||||||
successMessage = '复制成功!',
|
successMessage = '复制成功!',
|
||||||
errorMessage = '复制失败,请手动复制。',
|
errorMessage = '复制失败,请手动复制。',
|
||||||
onCopyOverride = null,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error'
|
const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timer;
|
let timer: number;
|
||||||
if (status === 'success' || status === 'error') {
|
if (status === 'success' || status === 'error') {
|
||||||
timer = setTimeout(() => {
|
timer = window.setTimeout(() => {
|
||||||
setStatus('idle');
|
setStatus('idle');
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
@@ -30,29 +28,15 @@ const CopyButton = ({
|
|||||||
|
|
||||||
const safeText = String(text);
|
const safeText = String(text);
|
||||||
|
|
||||||
// 优先使用传入的复制函数
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
if (onCopyOverride) {
|
try {
|
||||||
return await onCopyOverride(safeText);
|
await navigator.clipboard.writeText(safeText);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('使用 Clipboard API 复制失败:', err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const textArea = document.createElement("textarea");
|
|
||||||
textArea.value = safeText;
|
|
||||||
|
|
||||||
// 确保元素存在但不可见,且不影响布局
|
|
||||||
textArea.style.position = "fixed";
|
|
||||||
textArea.style.left = "-9999px";
|
|
||||||
textArea.style.top = "0";
|
|
||||||
textArea.style.opacity = "0";
|
|
||||||
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
textArea.focus();
|
|
||||||
textArea.select();
|
|
||||||
|
|
||||||
const successful = document.execCommand('copy');
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
|
|
||||||
// 使用通用的复制函数
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = useCallback(async () => {
|
const handleClick = useCallback(async () => {
|
||||||
@@ -65,35 +49,37 @@ const CopyButton = ({
|
|||||||
console.error('复制时出错:', error);
|
console.error('复制时出错:', error);
|
||||||
setStatus('error');
|
setStatus('error');
|
||||||
}
|
}
|
||||||
}, [text, onCopyOverride]);
|
}, [text]);
|
||||||
|
|
||||||
// 根据状态计算当前显示的文本
|
// 根据状态计算当前显示的文本
|
||||||
const currentText = status === 'success' ? successMessage
|
const currentText =
|
||||||
: status === 'error' ? errorMessage
|
status === 'success' ? successMessage : status === 'error' ? errorMessage : buttonText;
|
||||||
: buttonText;
|
|
||||||
|
|
||||||
// 根据状态计算样式 (建议使用 CSS Module 或 Tailwind,这里为了演示保留内联)
|
// 动态样式:只在非默认状态下覆盖颜色,平时让 className 控制
|
||||||
const getBackgroundColor = () => {
|
const getStyle = () => {
|
||||||
switch (status) {
|
const baseStyle = {
|
||||||
case 'success': return '#4CAF50';
|
transition: 'all 0.3s ease',
|
||||||
case 'error': return '#f44336';
|
cursor: 'pointer', // 确保有手型光标
|
||||||
default: return '#4CAF50'; // 让 CSS 类控制默认颜色
|
// 这里去掉了 padding/border/radius 的硬编码,建议在 CSS 类中定义
|
||||||
|
// 除非你想强制覆盖
|
||||||
|
};
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
return { ...baseStyle, backgroundColor: '#4CAF50', color: 'white' };
|
||||||
}
|
}
|
||||||
|
if (status === 'error') {
|
||||||
|
return { ...baseStyle, backgroundColor: '#f44336', color: 'white' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseStyle;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={`${className} ${status} copy-button`}
|
className={`${className} ${status} copy-button`}
|
||||||
style={{
|
style={getStyle()}
|
||||||
backgroundColor: getBackgroundColor(),
|
disabled={status === 'copying'}
|
||||||
color: status !== 'idle' ? 'white' : undefined,
|
|
||||||
transition: 'all 0.3s ease',
|
|
||||||
padding: '8px 16px',
|
|
||||||
border: 'none',
|
|
||||||
borderRadius: '8px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{currentText}
|
{currentText}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,30 +1,3 @@
|
|||||||
export default defineBackground(() => {
|
export default defineBackground(() => {
|
||||||
console.log('Hello background!', { id: browser.runtime.id });
|
console.log('Hello background!', { id: browser.runtime.id });
|
||||||
|
|
||||||
console.log('Background script loaded');
|
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|
||||||
console.log('收到消息:', request);
|
|
||||||
|
|
||||||
if (request.action === 'copy') {
|
|
||||||
console.log('开始复制文本:', request.text);
|
|
||||||
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(request.text)
|
|
||||||
.then(() => {
|
|
||||||
console.log('复制成功');
|
|
||||||
sendResponse({ success: true });
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error('复制失败:', err);
|
|
||||||
sendResponse({ success: false, error: err.message });
|
|
||||||
});
|
|
||||||
|
|
||||||
return true; // 保持消息端口开放以支持异步响应
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对于未知的操作,也返回响应
|
|
||||||
sendResponse({ success: false, error: '未知操作' });
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Document</title>
|
<title>Document</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
Hello Options Page
|
Hello Options Page
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Generated
+207
@@ -33,6 +33,9 @@
|
|||||||
"@types/react": "^19.2.7",
|
"@types/react": "^19.2.7",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@wxt-dev/module-react": "^1.1.5",
|
"@wxt-dev/module-react": "^1.1.5",
|
||||||
|
"husky": "^9.1.7",
|
||||||
|
"lint-staged": "^16.2.7",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"wxt": "^0.20.6"
|
"wxt": "^0.20.6"
|
||||||
}
|
}
|
||||||
@@ -3634,6 +3637,22 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/husky": {
|
||||||
|
"version": "9.1.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/husky/-/husky-9.1.7.tgz",
|
||||||
|
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"husky": "bin.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/typicode"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/immediate": {
|
"node_modules/immediate": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
|
||||||
@@ -4198,6 +4217,139 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lint-staged": {
|
||||||
|
"version": "16.2.7",
|
||||||
|
"resolved": "https://registry.npmmirror.com/lint-staged/-/lint-staged-16.2.7.tgz",
|
||||||
|
"integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^14.0.2",
|
||||||
|
"listr2": "^9.0.5",
|
||||||
|
"micromatch": "^4.0.8",
|
||||||
|
"nano-spawn": "^2.0.0",
|
||||||
|
"pidtree": "^0.6.0",
|
||||||
|
"string-argv": "^0.3.2",
|
||||||
|
"yaml": "^2.8.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"lint-staged": "bin/lint-staged.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/cli-truncate": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"slice-ansi": "^7.1.0",
|
||||||
|
"string-width": "^8.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/commander": {
|
||||||
|
"version": "14.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/commander/-/commander-14.0.2.tgz",
|
||||||
|
"integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-east-asian-width": "^1.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/listr2": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmmirror.com/listr2/-/listr2-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cli-truncate": "^5.0.0",
|
||||||
|
"colorette": "^2.0.20",
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
|
"log-update": "^6.1.0",
|
||||||
|
"rfdc": "^1.4.1",
|
||||||
|
"wrap-ansi": "^9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/nano-spawn": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/nano-spawn/-/nano-spawn-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/nano-spawn?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/slice-ansi": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^6.2.1",
|
||||||
|
"is-fullwidth-code-point": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lint-staged/node_modules/string-width": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"get-east-asian-width": "^1.3.0",
|
||||||
|
"strip-ansi": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/listr2": {
|
"node_modules/listr2": {
|
||||||
"version": "8.3.3",
|
"version": "8.3.3",
|
||||||
"resolved": "https://registry.npmmirror.com/listr2/-/listr2-8.3.3.tgz",
|
"resolved": "https://registry.npmmirror.com/listr2/-/listr2-8.3.3.tgz",
|
||||||
@@ -5278,6 +5430,19 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pidtree": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"pidtree": "bin/pidtree.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pino": {
|
"node_modules/pino": {
|
||||||
"version": "9.7.0",
|
"version": "9.7.0",
|
||||||
"resolved": "https://registry.npmmirror.com/pino/-/pino-9.7.0.tgz",
|
"resolved": "https://registry.npmmirror.com/pino/-/pino-9.7.0.tgz",
|
||||||
@@ -5358,6 +5523,22 @@
|
|||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.8.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.8.1.tgz",
|
||||||
|
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pretty-format": {
|
"node_modules/pretty-format": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-27.5.1.tgz",
|
"resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-27.5.1.tgz",
|
||||||
@@ -6251,6 +6432,16 @@
|
|||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-argv": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "7.2.0",
|
"version": "7.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz",
|
||||||
@@ -7160,6 +7351,22 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/yaml": {
|
||||||
|
"version": "2.8.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.2.tgz",
|
||||||
|
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"bin": {
|
||||||
|
"yaml": "bin.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/eemeli"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yargs": {
|
"node_modules/yargs": {
|
||||||
"version": "17.7.2",
|
"version": "17.7.2",
|
||||||
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
|
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
|
||||||
|
|||||||
+12
-3
@@ -4,6 +4,11 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
||||||
|
"prettier --write"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "wxt",
|
"dev": "wxt",
|
||||||
"dev:firefox": "wxt -b firefox",
|
"dev:firefox": "wxt -b firefox",
|
||||||
@@ -12,11 +17,10 @@
|
|||||||
"zip": "wxt zip",
|
"zip": "wxt zip",
|
||||||
"zip:firefox": "wxt zip -b firefox",
|
"zip:firefox": "wxt zip -b firefox",
|
||||||
"compile": "tsc --noEmit",
|
"compile": "tsc --noEmit",
|
||||||
"postinstall": "wxt prepare"
|
"postinstall": "wxt prepare",
|
||||||
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.2.3",
|
|
||||||
"react-dom": "^19.2.3",
|
|
||||||
"@rrweb/all": "^2.0.0-alpha.18",
|
"@rrweb/all": "^2.0.0-alpha.18",
|
||||||
"@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.18",
|
"@rrweb/rrweb-plugin-console-record": "^2.0.0-alpha.18",
|
||||||
"@rrweb/rrweb-plugin-console-replay": "^2.0.0-alpha.18",
|
"@rrweb/rrweb-plugin-console-replay": "^2.0.0-alpha.18",
|
||||||
@@ -27,6 +31,8 @@
|
|||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"dexie": "^4.2.1",
|
"dexie": "^4.2.1",
|
||||||
"dexie-react-hooks": "^4.2.0",
|
"dexie-react-hooks": "^4.2.0",
|
||||||
|
"react": "^19.2.3",
|
||||||
|
"react-dom": "^19.2.3",
|
||||||
"react-markdown": "^6.0.3",
|
"react-markdown": "^6.0.3",
|
||||||
"react-router-dom": "^6.30.2",
|
"react-router-dom": "^6.30.2",
|
||||||
"remark-gfm": "^1.0.0",
|
"remark-gfm": "^1.0.0",
|
||||||
@@ -39,6 +45,9 @@
|
|||||||
"@types/react": "^19.2.7",
|
"@types/react": "^19.2.7",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@wxt-dev/module-react": "^1.1.5",
|
"@wxt-dev/module-react": "^1.1.5",
|
||||||
|
"husky": "^9.1.7",
|
||||||
|
"lint-staged": "^16.2.7",
|
||||||
|
"prettier": "^3.8.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"wxt": "^0.20.6"
|
"wxt": "^0.20.6"
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-11
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
// import {sendRecordCommand} from '../services/bridge'
|
// import {sendRecordCommand} from '../services/bridge'
|
||||||
// import {storage} from "../services/storage";
|
// import {storage} from "../services/storage";
|
||||||
import {useRecorder} from "../hooks/useRecorder";
|
import { useRecorder } from '../hooks/useRecorder';
|
||||||
import {downloadHtml} from '../utils/recordUtils';
|
import { downloadHtml } from '../utils/recordUtils';
|
||||||
|
|
||||||
const RecordeReplayPage = () => {
|
const RecordeReplayPage = () => {
|
||||||
// const [isRecording, setIsRecording] = useState(false);
|
// const [isRecording, setIsRecording] = useState(false);
|
||||||
|
|
||||||
const {startRecord, stopRecord, getEvents, isRecording} = useRecorder();
|
const { startRecord, stopRecord, getEvents, isRecording } = useRecorder();
|
||||||
|
|
||||||
const handleStart = async () => {
|
const handleStart = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -20,9 +20,9 @@ const RecordeReplayPage = () => {
|
|||||||
// await storage.set({isRecording: true, recordingStartTime: Date.now()});
|
// await storage.set({isRecording: true, recordingStartTime: Date.now()});
|
||||||
// console.log('events', events);
|
// console.log('events', events);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error starting recording:", e);
|
console.error('Error starting recording:', e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleStop = async () => {
|
const handleStop = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -35,24 +35,25 @@ const RecordeReplayPage = () => {
|
|||||||
downloadHtml(getEvents());
|
downloadHtml(getEvents());
|
||||||
// console.log('events', getEvents());
|
// console.log('events', getEvents());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error stopping recording:", e)
|
console.error('Error stopping recording:', e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{width: "300px", padding: "16px"}}>
|
<div style={{ width: '300px', padding: '16px' }}>
|
||||||
<h3>RRWeb Recorder</h3>
|
<h3>RRWeb Recorder</h3>
|
||||||
|
|
||||||
{/* 3. 选择标签页逻辑:默认为当前页,如果需要跨页,需先列出 chrome.tabs.query */}
|
{/* 3. 选择标签页逻辑:默认为当前页,如果需要跨页,需先列出 chrome.tabs.query */}
|
||||||
|
|
||||||
<div style={{display: "flex", gap: "10px", marginBottom: "20px"}}>
|
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
|
||||||
{!isRecording ? (
|
{!isRecording ? (
|
||||||
<button onClick={handleStart} className={"action-btn"}>
|
<button onClick={handleStart} className={'action-btn'}>
|
||||||
开始录制
|
开始录制
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button className={"action-btn stop-btn"}
|
<button className={'action-btn stop-btn'} onClick={handleStop}>
|
||||||
onClick={handleStop}>停止录制</button>
|
停止录制
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+10
-10
@@ -1,15 +1,15 @@
|
|||||||
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';
|
||||||
|
|
||||||
|
|
||||||
const TimestampPage = () => {
|
const TimestampPage = () => {
|
||||||
|
return (
|
||||||
return (<div className="timestamp-utils">
|
<div className="timestamp-utils">
|
||||||
<TimestampExecution/>
|
<TimestampExecution />
|
||||||
<TimestampToDatetime/>
|
<TimestampToDatetime />
|
||||||
<DatetimeToTimestamp/>
|
<DatetimeToTimestamp />
|
||||||
</div>);
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TimestampPage;
|
export default TimestampPage;
|
||||||
|
|||||||
+39
-2
@@ -1,7 +1,44 @@
|
|||||||
{
|
{
|
||||||
"extends": "./.wxt/tsconfig.json",
|
"extends": "./.wxt/tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
/* --- 原有配置保持 --- */
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx",
|
||||||
}
|
|
||||||
|
/* --- 1. 严格类型检查 (关键) --- */
|
||||||
|
// 开启所有严格检查,包括 noImplicitAny。
|
||||||
|
// 这能帮你捕获 "timer" 隐式 any 等错误,强制你写出更高质量的代码。
|
||||||
|
"strict": true,
|
||||||
|
|
||||||
|
/* --- 2. 代码质量检查 --- */
|
||||||
|
// 声明了但没使用的变量报错(防止代码冗余)
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
// 函数参数没使用报错
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
// switch 语句没有 break 时报错
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
/* --- 3. 路径与环境 --- */
|
||||||
|
// 设置基础目录,方便解析相对路径
|
||||||
|
"baseUrl": ".",
|
||||||
|
// 确保包含 DOM 类型(解决 setTimeout、document 等报错)
|
||||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
|
// 编译目标设置为最新,WXT 底层 Vite 会处理降级兼容
|
||||||
|
"target": "ESNext",
|
||||||
|
|
||||||
|
/* --- 4. 路径别名 (可选) --- */
|
||||||
|
// 如果你的 @/utils/... 爆红,可以手动添加这个映射。
|
||||||
|
// WXT 通常会自动处理,但在这里显式声明有助于 VS Code 智能提示。
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./entrypoints/*", "./components/*", "./utils/*", "./assets/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 确保包含你的源代码目录
|
||||||
|
"include": [
|
||||||
|
"entrypoints/**/*",
|
||||||
|
"components/**/*",
|
||||||
|
"utils/**/*",
|
||||||
|
"assets/**/*",
|
||||||
|
".wxt/types/**/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user