diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000..bcc2f45 --- /dev/null +++ b/DEVELOPMENT.md @@ -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 会自动刷新浏览器页面 +- 无需手动刷新浏览器来查看更改效果 \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..a22fda8 --- /dev/null +++ b/index.html @@ -0,0 +1,32 @@ + + + + + + Testing Tool - Development + + + +
+

Testing Tool - Development Preview

+

这是一个用于开发预览的页面,下方是sidepanel的实时预览。

+ +
+ + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index df1ad3c..b6d5064 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,20 @@ { "name": "Chrome Extensions Gemini Demo", - "version": "1.0", + "version": "1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "Chrome Extensions Gemini Demo", - "version": "1.0", + "version": "1.1", "devDependencies": { "@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": { @@ -21,6 +26,121 @@ "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": { "version": "4.22.4", "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==", "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": { "version": "2.3.3", "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_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": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", @@ -283,6 +847,85 @@ "@rollup/rollup-win32-x64-msvc": "4.22.4", "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 + } + } } } } diff --git a/package.json b/package.json index 33275e5..10e21cd 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,17 @@ "version": "1.1", "type": "module", "scripts": { - "build": "rollup -c" + "build": "rollup -c", + "dev": "rollup -c -w" }, "private": true, "devDependencies": { "@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" } -} \ No newline at end of file +} diff --git a/rollup.config.js b/rollup.config.js index a143c7a..b5848dd 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -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', output: { 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 } -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/sidepanel/index.css b/sidepanel/index.css index 9bb10f7..4b9b99a 100644 --- a/sidepanel/index.css +++ b/sidepanel/index.css @@ -2,7 +2,7 @@ #app { display: flex; flex-direction: column; - height: 100vh; + height: 100%; } .navbar { diff --git a/sidepanel/index.html b/sidepanel/index.html index b0c4e1a..27fe613 100644 --- a/sidepanel/index.html +++ b/sidepanel/index.html @@ -61,8 +61,7 @@ placeholder="转换结果" class="input-text" /> - @@ -79,8 +78,7 @@ placeholder="输入日期时间" class="input-text" /> - diff --git a/sidepanel/index.js b/sidepanel/index.js index f2322d9..56ad68a 100644 --- a/sidepanel/index.js +++ b/sidepanel/index.js @@ -12,7 +12,9 @@ import { handleConvertTimestamp, handleConvertDateToTimestamp, handleCopyTimestamp, - handleClosePanel + handleClosePanel, + handleTimezoneResult, + handleTimezoneInput } from './modules/eventHandlers.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('close-panel-btn', 'click', handleClosePanel); +// 绑定切换时区事件 +addEventListenerById('timezone-result', 'change', handleTimezoneResult) +// 绑定输入时区事件 +addEventListenerById('timezone-input', 'change', handleTimezoneInput) diff --git a/sidepanel/modules/eventHandlers.js b/sidepanel/modules/eventHandlers.js index ce690e2..dde6bb2 100644 --- a/sidepanel/modules/eventHandlers.js +++ b/sidepanel/modules/eventHandlers.js @@ -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'; // 定义一个全局变量来保存是否显示毫秒 let showMilliseconds = true; // 定义一个全局变量来保存计时器 let timestampInterval; +// 定义时区列表 +const timeZoneList = Intl.supportedValuesOf('timeZone'); +// 获取本地时区 +const localTimeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone; /** * 初始化时间戳显示 @@ -12,6 +20,36 @@ let timestampInterval; export function initTimestampDisplay() { updateTimestamp(showMilliseconds); // 初始化时更新一次时间戳 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'); // 使用适合的格式 } /** @@ -53,6 +91,7 @@ export function handleConvertTimestamp() { const timestampUnitSelect = document.querySelector( '#page-timestamp #timestamp-input-unit-select' ); + const timezoneResultSelect = getElementById('timezone-result'); // 获取时区选择器 const timestamp = inputTimestamp.value.trim(); console.log('输入时间戳:', timestamp); @@ -70,7 +109,12 @@ export function handleConvertTimestamp() { // 获取选择的时间戳单位(秒或毫秒) 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; // 添加成功反馈动画 @@ -95,6 +139,7 @@ export function handleConvertDateToTimestamp() { const inputDate = getElementById('datetime-input'); const dateInputResult = getElementById('date-conversion-result'); const timestampUnitSelect = document.querySelector('#page-timestamp #date-result-unit-select'); + const timezoneInputSelect = getElementById('timezone-input'); // 获取时区选择器 const dateStr = inputDate.value.trim(); console.log('输入日期字符串:', dateStr); @@ -109,14 +154,23 @@ export function handleConvertDateToTimestamp() { return; } + // 获取选择的时区 + const selectedTimezone = timeZoneList[timezoneInputSelect.value]; + + console.log('选择的时区:', selectedTimezone); const date = new Date(dateStr); - if (isNaN(date.getTime())) { + + // 转换为时间戳 + const timestamp = convertDateToTimestamp(date, selectedTimezone); + + console.log('转换后的时间戳:', timestamp); + + if (isNaN(timestamp)) { dateInputResult.value = '无效的日期字符串'; dateInputResult.style.borderColor = '#dc3545'; return; } - const timestamp = date.getTime(); // 根据选择的单位决定显示秒还是毫秒 const isSeconds = timestampUnitSelect.value === 'seconds'; const finalTimestamp = isSeconds ? Math.floor(timestamp / 1000) : timestamp; @@ -174,3 +228,55 @@ export function handleClosePanel() { // 向父页面发送消息请求关闭 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; + } + } +} diff --git a/sidepanel/utils/timestampUtils.js b/sidepanel/utils/timestampUtils.js index ef7d9b3..6d43927 100644 --- a/sidepanel/utils/timestampUtils.js +++ b/sidepanel/utils/timestampUtils.js @@ -19,9 +19,14 @@ export function updateTimestamp(showMilliseconds = true) { * 转换时间戳为日期格式 * @param {string} timestamp - 输入的时间戳 * @param {boolean} isSeconds - 是否为秒格式 + * @param {string} timeZone - 时区,默认为本地时区 * @returns {string} 格式化后的日期字符串 */ -export function convertTimestampToDate(timestamp, isSeconds) { +export function convertTimestampToDate( + timestamp, + isSeconds, + timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone +) { // 转换为数字 let timestampNum = Number(timestamp); @@ -43,22 +48,34 @@ export function convertTimestampToDate(timestamp, isSeconds) { return '无效的日期'; } - const year = date.getFullYear(); - const month = date.getMonth() + 1; - const day = date.getDate(); - const hours = date.getHours(); - const minutes = date.getMinutes(); - const seconds = date.getSeconds(); + // 使用Intl.DateTimeFormat来根据时区格式化日期 + const formatter = new Intl.DateTimeFormat('zh-CN', { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + timeZone: timeZone, + }); - return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + return formatter.format(date); } /** * 转换日期格式为时间戳 * @param {string} date - 输入的日期字符串 + * @param {string} timeZone - 时区,默认为本地时区 * @returns {number|string} 时间戳或错误信息 */ -export function convertDateToTimestamp(date) { - const timestamp = Date.parse(date); - return isNaN(timestamp) ? '无效的日期' : timestamp; +export function convertDateToTimestamp( + date, + timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone +) { + return new Date( + Intl.DateTimeFormat('en-US', { + timeZone: timeZone, + hour12: false, + }).format(date) + ).getTime(); }