feat(dev): 添加开发服务器与热重载支持

- 新增 `npm run dev` 命令,启动带热重载的开发服务器
- 集成 rollup watch 模式与 LiveReload 实现自动刷新
- 添加开发指南 DEVELOPMENT.md,说明项目结构、预览方式及构建流程
- 新增开发预览页面 index.html,用于嵌入展示 sidepanel 内容
- 更新 package.json 和 package-lock.json,引入相关开发依赖
```
This commit is contained in:
雨霖铃
2025-11-18 00:26:41 +08:00
parent 8b608341e7
commit bc1943dc16
10 changed files with 918 additions and 30 deletions
+51
View File
@@ -0,0 +1,51 @@
# 开发指南
## 启动开发服务器(带热重载)
要启动带有热重载功能的开发服务器,请运行以下命令:
```bash
npm run dev
```
这将会:
1. 启动 Rollup 的监视模式
2. 自动重新编译更改的文件
3. 通过 LiveReload 刷新浏览器
4. 在 http://localhost:8082 启动开发服务器
## 项目结构
```
project/
├── sidepanel/ # Chrome扩展侧边栏面板
│ ├── index.html # 侧边栏主页面
│ ├── index.js # 侧边栏主脚本
│ ├── index.css # 侧边栏样式
│ └── ... # 其他相关文件
├── dist/ # 构建输出目录
└── ... # 其他配置和文档文件
```
## 预览方式
有两种方式可以预览您的更改:
1. 直接访问 `http://localhost:8082/sidepanel/index.html` 查看sidepanel的实际效果
2. 访问 `http://localhost:8082/index.html` 查看包含sidepanel预览的开发页面
## 构建生产版本
要构建生产版本,请运行:
```bash
npm run build
```
这将生成优化后的 bundle 文件到 `dist/sidepanel.bundle.js`
## 热重载工作原理
- 当你修改 [sidepanel](file:///D:/Work/testing-tool/sidepanel) 目录下的任何文件时,Rollup 会自动重新编译
- 编译完成后,LiveReload 会自动刷新浏览器页面
- 无需手动刷新浏览器来查看更改效果
+32
View File
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Testing Tool - Development</title>
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
iframe {
width: 100%;
height: 600px;
border: 1px solid #ccc;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>Testing Tool - Development Preview</h1>
<p>这是一个用于开发预览的页面,下方是sidepanel的实时预览。</p>
<iframe src="/sidepanel/index.html" title="Sidepanel Preview"></iframe>
</div>
</body>
</html>
+646 -3
View File
@@ -1,15 +1,20 @@
{ {
"name": "Chrome Extensions Gemini Demo", "name": "Chrome Extensions Gemini Demo",
"version": "1.0", "version": "1.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "Chrome Extensions Gemini Demo", "name": "Chrome Extensions Gemini Demo",
"version": "1.0", "version": "1.1",
"devDependencies": { "devDependencies": {
"@google/generative-ai": "0.15.0", "@google/generative-ai": "0.15.0",
"rollup": "4.22.4" "@rollup/plugin-commonjs": "^25.0.8",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.1",
"rollup": "4.22.4",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^3.0.0"
} }
}, },
"node_modules/@google/generative-ai": { "node_modules/@google/generative-ai": {
@@ -21,6 +26,121 @@
"node": ">=18.0.0" "node": ">=18.0.0"
} }
}, },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/plugin-commonjs": {
"version": "25.0.8",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz",
"integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"glob": "^8.0.3",
"is-reference": "1.2.1",
"magic-string": "^0.30.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.68.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-json": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.1.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/plugin-node-resolve": {
"version": "15.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz",
"integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
"@types/resolve": "1.20.2",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.22.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^2.78.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
}
},
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.22.4", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
@@ -235,6 +355,139 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true "dev": true
}, },
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
"license": "MIT"
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true,
"license": "MIT"
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -249,6 +502,317 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0" "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
} }
}, },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^5.0.1",
"once": "^1.3.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dev": true,
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true,
"license": "ISC"
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"dev": true,
"license": "MIT"
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*"
}
},
"node_modules/livereload": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz",
"integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.0",
"livereload-js": "^3.3.1",
"opts": ">= 1.2.0",
"ws": "^7.4.3"
},
"bin": {
"livereload": "bin/livereload.js"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/livereload-js": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz",
"integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==",
"dev": true,
"license": "MIT"
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/mime": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
"integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
"dev": true,
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/opener": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
"integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
"dev": true,
"license": "(WTFPL OR MIT)",
"bin": {
"opener": "bin/opener-bin.js"
}
},
"node_modules/opts": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
"integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"license": "MIT"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.22.4", "version": "4.22.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
@@ -283,6 +847,85 @@
"@rollup/rollup-win32-x64-msvc": "4.22.4", "@rollup/rollup-win32-x64-msvc": "4.22.4",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
},
"node_modules/rollup-plugin-livereload": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz",
"integrity": "sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==",
"dev": true,
"license": "MIT",
"dependencies": {
"livereload": "^0.9.1"
},
"engines": {
"node": ">=8.3"
}
},
"node_modules/rollup-plugin-serve": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-serve/-/rollup-plugin-serve-3.0.0.tgz",
"integrity": "sha512-DjVRhbwC0OgP1Q1sj8Lvx12ee60UTZM767kkjT61sYKHw/wLpANAw3VZN5ZMa5NlvO8bYpfTaqiUrW+icAjXFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"mime": "^4",
"opener": "1"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
},
"node_modules/ws": {
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
} }
} }
} }
+8 -2
View File
@@ -3,11 +3,17 @@
"version": "1.1", "version": "1.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "rollup -c" "build": "rollup -c",
"dev": "rollup -c -w"
}, },
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@google/generative-ai": "0.15.0", "@google/generative-ai": "0.15.0",
"rollup": "4.22.4" "@rollup/plugin-commonjs": "^25.0.8",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.1",
"rollup": "4.22.4",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^3.0.0"
} }
} }
+32 -3
View File
@@ -1,7 +1,36 @@
export default { import { defineConfig } from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';
export default defineConfig({
input: 'sidepanel/index.js', input: 'sidepanel/index.js',
output: { output: {
file: 'dist/sidepanel.bundle.js', file: 'dist/sidepanel.bundle.js',
format: 'iife' format: 'iife',
},
plugins: [
resolve(),
commonjs(),
json(),
livereload({
watch: ['dist', 'sidepanel'],
verbose: false
}),
serve({
open: true,
port: 8082,
contentBase: ['.', 'sidepanel'],
headers: {
'Access-Control-Allow-Origin': '*'
}
}),
],
watch: {
include: ['sidepanel/**/*'],
exclude: ['node_modules/**/*'],
clearScreen: false
} }
}; });
+1 -1
View File
@@ -2,7 +2,7 @@
#app { #app {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh; height: 100%;
} }
.navbar { .navbar {
+2 -4
View File
@@ -61,8 +61,7 @@
placeholder="转换结果" placeholder="转换结果"
class="input-text" class="input-text"
/> />
<select class="select-box"> <select class="select-box" id="timezone-result">
<option>Asia/Shanghai</option>
</select> </select>
</div> </div>
</div> </div>
@@ -79,8 +78,7 @@
placeholder="输入日期时间" placeholder="输入日期时间"
class="input-text" class="input-text"
/> />
<select class="select-box"> <select class="select-box" id="timezone-input">
<option>Asia/Shanghai</option>
</select> </select>
</div> </div>
+7 -1
View File
@@ -12,7 +12,9 @@ import {
handleConvertTimestamp, handleConvertTimestamp,
handleConvertDateToTimestamp, handleConvertDateToTimestamp,
handleCopyTimestamp, handleCopyTimestamp,
handleClosePanel handleClosePanel,
handleTimezoneResult,
handleTimezoneInput
} from './modules/eventHandlers.js'; } from './modules/eventHandlers.js';
import { addEventListenerById } from './utils/domUtils.js'; import { addEventListenerById } from './utils/domUtils.js';
@@ -43,3 +45,7 @@ addEventListenerById('convert-date-to-timestamp-btn', 'click', handleConvertDate
addEventListenerById('copy-timestamp-btn', 'click', handleCopyTimestamp); addEventListenerById('copy-timestamp-btn', 'click', handleCopyTimestamp);
// 绑定关闭面板按钮事件 // 绑定关闭面板按钮事件
addEventListenerById('close-panel-btn', 'click', handleClosePanel); addEventListenerById('close-panel-btn', 'click', handleClosePanel);
// 绑定切换时区事件
addEventListenerById('timezone-result', 'change', handleTimezoneResult)
// 绑定输入时区事件
addEventListenerById('timezone-input', 'change', handleTimezoneInput)
+110 -4
View File
@@ -1,10 +1,18 @@
import { updateTimestamp, convertTimestampToDate } from '../utils/timestampUtils.js'; import {
updateTimestamp,
convertTimestampToDate,
convertDateToTimestamp,
} from '../utils/timestampUtils.js';
import { getElementById } from '../utils/domUtils.js'; import { getElementById } from '../utils/domUtils.js';
// 定义一个全局变量来保存是否显示毫秒 // 定义一个全局变量来保存是否显示毫秒
let showMilliseconds = true; let showMilliseconds = true;
// 定义一个全局变量来保存计时器 // 定义一个全局变量来保存计时器
let timestampInterval; let timestampInterval;
// 定义时区列表
const timeZoneList = Intl.supportedValuesOf('timeZone');
// 获取本地时区
const localTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
/** /**
* 初始化时间戳显示 * 初始化时间戳显示
@@ -12,6 +20,36 @@ let timestampInterval;
export function initTimestampDisplay() { export function initTimestampDisplay() {
updateTimestamp(showMilliseconds); // 初始化时更新一次时间戳 updateTimestamp(showMilliseconds); // 初始化时更新一次时间戳
timestampInterval = setInterval(() => updateTimestamp(showMilliseconds), 100); timestampInterval = setInterval(() => updateTimestamp(showMilliseconds), 100);
initTimeZoneList();
initTimestampAndDate();
}
/**
* 初始化时区列表
*/
export function initTimeZoneList() {
const timezoneResultSelect = getElementById('timezone-result');
const timezoneInputSelect = getElementById('timezone-input');
// 获取本地时区在列表中的索引
const localTimeZoneIndex = timeZoneList.indexOf(localTimeZone);
for (let i = 0; i < timeZoneList.length; i++) {
if (i === localTimeZoneIndex) {
timezoneResultSelect.add(new Option(timeZoneList[i], i, true, true));
timezoneInputSelect.add(new Option(timeZoneList[i], i, true, true));
}
timezoneResultSelect.add(new Option(timeZoneList[i], i));
timezoneInputSelect.add(new Option(timeZoneList[i], i));
}
}
export function initTimestampAndDate() {
const timestampInput = getElementById('timestamp-input');
const dateInput = getElementById('datetime-input');
timestampInput.value = Date.now();
dateInput.value = new Date().toLocaleString('sv-SE'); // 使用适合<input type="datetime-local">的格式
} }
/** /**
@@ -53,6 +91,7 @@ export function handleConvertTimestamp() {
const timestampUnitSelect = document.querySelector( const timestampUnitSelect = document.querySelector(
'#page-timestamp #timestamp-input-unit-select' '#page-timestamp #timestamp-input-unit-select'
); );
const timezoneResultSelect = getElementById('timezone-result'); // 获取时区选择器
const timestamp = inputTimestamp.value.trim(); const timestamp = inputTimestamp.value.trim();
console.log('输入时间戳:', timestamp); console.log('输入时间戳:', timestamp);
@@ -70,7 +109,12 @@ export function handleConvertTimestamp() {
// 获取选择的时间戳单位(秒或毫秒) // 获取选择的时间戳单位(秒或毫秒)
const isSeconds = timestampUnitSelect.value === 'seconds'; const isSeconds = timestampUnitSelect.value === 'seconds';
const result = convertTimestampToDate(timestamp, isSeconds); // 获取选择的时区
const selectedTimezone = timeZoneList[timezoneResultSelect.value];
console.log('选择的时区:', selectedTimezone);
const result = convertTimestampToDate(timestamp, isSeconds, selectedTimezone);
timestampInputResult.value = result; timestampInputResult.value = result;
// 添加成功反馈动画 // 添加成功反馈动画
@@ -95,6 +139,7 @@ export function handleConvertDateToTimestamp() {
const inputDate = getElementById('datetime-input'); const inputDate = getElementById('datetime-input');
const dateInputResult = getElementById('date-conversion-result'); const dateInputResult = getElementById('date-conversion-result');
const timestampUnitSelect = document.querySelector('#page-timestamp #date-result-unit-select'); const timestampUnitSelect = document.querySelector('#page-timestamp #date-result-unit-select');
const timezoneInputSelect = getElementById('timezone-input'); // 获取时区选择器
const dateStr = inputDate.value.trim(); const dateStr = inputDate.value.trim();
console.log('输入日期字符串:', dateStr); console.log('输入日期字符串:', dateStr);
@@ -109,14 +154,23 @@ export function handleConvertDateToTimestamp() {
return; return;
} }
// 获取选择的时区
const selectedTimezone = timeZoneList[timezoneInputSelect.value];
console.log('选择的时区:', selectedTimezone);
const date = new Date(dateStr); const date = new Date(dateStr);
if (isNaN(date.getTime())) {
// 转换为时间戳
const timestamp = convertDateToTimestamp(date, selectedTimezone);
console.log('转换后的时间戳:', timestamp);
if (isNaN(timestamp)) {
dateInputResult.value = '无效的日期字符串'; dateInputResult.value = '无效的日期字符串';
dateInputResult.style.borderColor = '#dc3545'; dateInputResult.style.borderColor = '#dc3545';
return; return;
} }
const timestamp = date.getTime();
// 根据选择的单位决定显示秒还是毫秒 // 根据选择的单位决定显示秒还是毫秒
const isSeconds = timestampUnitSelect.value === 'seconds'; const isSeconds = timestampUnitSelect.value === 'seconds';
const finalTimestamp = isSeconds ? Math.floor(timestamp / 1000) : timestamp; const finalTimestamp = isSeconds ? Math.floor(timestamp / 1000) : timestamp;
@@ -174,3 +228,55 @@ export function handleClosePanel() {
// 向父页面发送消息请求关闭 // 向父页面发送消息请求关闭
window.parent.postMessage({ action: 'closeSidebar' }, '*'); window.parent.postMessage({ action: 'closeSidebar' }, '*');
} }
/**
* 时间区域选择事件处理器
*/
export function handleTimezoneResult() {
const timezoneResult = getElementById('timezone-result');
console.log(timeZoneList[timezoneResult.value]);
// 获取当前时间戳输入框的值
const inputTimestamp = getElementById('timestamp-input').value.trim();
const timestampInputResult = getElementById('timestamp-conversion-result');
const timestampUnitSelect = document.querySelector(
'#page-timestamp #timestamp-input-unit-select'
);
// 如果有输入时间戳,则重新计算结果
if (inputTimestamp) {
const isSeconds = timestampUnitSelect.value === 'seconds';
const selectedTimezone = timeZoneList[timezoneResult.value];
const result = convertTimestampToDate(inputTimestamp, isSeconds, selectedTimezone);
timestampInputResult.value = result;
}
}
/**
* 处理时区输入事件
* 该函数获取时区输入元素的值,并在控制台输出对应的时区信息
*/
export function handleTimezoneInput() {
const timezoneInput = getElementById('timezone-input');
const selectedTimezone = timeZoneList[timezoneInput.value];
console.log('选择的时区:', selectedTimezone);
// 获取当前日期输入框的值
const inputDate = getElementById('datetime-input').value.trim();
const dateInputResult = getElementById('date-conversion-result');
const timestampUnitSelect = document.querySelector('#page-timestamp #date-result-unit-select');
// 如果有输入日期,则重新计算结果
if (inputDate) {
console.log('输入的日期:', inputDate);
const date = new Date(inputDate);
// 转换为时间戳
const timestamp = convertDateToTimestamp(date, selectedTimezone);
if (!isNaN(timestamp)) {
const timestamp = date.getTime();
const isSeconds = timestampUnitSelect.value === 'seconds';
const finalTimestamp = isSeconds ? Math.floor(timestamp / 1000) : timestamp;
dateInputResult.value = finalTimestamp;
}
}
}
+28 -11
View File
@@ -19,9 +19,14 @@ export function updateTimestamp(showMilliseconds = true) {
* 转换时间戳为日期格式 * 转换时间戳为日期格式
* @param {string} timestamp - 输入的时间戳 * @param {string} timestamp - 输入的时间戳
* @param {boolean} isSeconds - 是否为秒格式 * @param {boolean} isSeconds - 是否为秒格式
* @param {string} timeZone - 时区,默认为本地时区
* @returns {string} 格式化后的日期字符串 * @returns {string} 格式化后的日期字符串
*/ */
export function convertTimestampToDate(timestamp, isSeconds) { export function convertTimestampToDate(
timestamp,
isSeconds,
timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
) {
// 转换为数字 // 转换为数字
let timestampNum = Number(timestamp); let timestampNum = Number(timestamp);
@@ -43,22 +48,34 @@ export function convertTimestampToDate(timestamp, isSeconds) {
return '无效的日期'; return '无效的日期';
} }
const year = date.getFullYear(); // 使用Intl.DateTimeFormat来根据时区格式化日期
const month = date.getMonth() + 1; const formatter = new Intl.DateTimeFormat('zh-CN', {
const day = date.getDate(); year: 'numeric',
const hours = date.getHours(); month: 'numeric',
const minutes = date.getMinutes(); day: 'numeric',
const seconds = date.getSeconds(); hour: 'numeric',
minute: 'numeric',
second: 'numeric',
timeZone: timeZone,
});
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; return formatter.format(date);
} }
/** /**
* 转换日期格式为时间戳 * 转换日期格式为时间戳
* @param {string} date - 输入的日期字符串 * @param {string} date - 输入的日期字符串
* @param {string} timeZone - 时区,默认为本地时区
* @returns {number|string} 时间戳或错误信息 * @returns {number|string} 时间戳或错误信息
*/ */
export function convertDateToTimestamp(date) { export function convertDateToTimestamp(
const timestamp = Date.parse(date); date,
return isNaN(timestamp) ? '无效的日期' : timestamp; timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
) {
return new Date(
Intl.DateTimeFormat('en-US', {
timeZone: timeZone,
hour12: false,
}).format(date)
).getTime();
} }