feat(extension): 初始化Chrome扩展项目结构与基础功能
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Package files
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Using the Gemini API in a Chrome Extension.
|
||||||
|
|
||||||
|
This sample demonstrates how to use the Gemini Cloud API in a Chrome Extension.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The extension provides a chat interface for the Gemini API. To learn more about the API head over to [https://ai.google.dev/](https://ai.google.dev/).
|
||||||
|
|
||||||
|
## Running this extension
|
||||||
|
|
||||||
|
1. Clone this repository.
|
||||||
|
2. Download the Gemini API client by running:
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
3. [Retrieve an API key](https://ai.google.dev/gemini-api/docs/api-key) and update [functional-samples/ai.gemini-in-the-cloud/sidepanel/index.js](functional-samples/ai.gemini-in-the-cloud/sidepanel/index.js) (only for testing).
|
||||||
|
4. Compile the JS bundle for the sidepanel implementation by running:
|
||||||
|
```sh
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
5. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked).
|
||||||
|
6. Click the extension icon.
|
||||||
|
7. Interact with the prompt API in the sidebar.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
chrome.action.onClicked.addListener(async (tab) => {
|
||||||
|
await chrome.scripting.executeScript({
|
||||||
|
target: { tabId: tab.id },
|
||||||
|
files: ["content.js"]
|
||||||
|
});
|
||||||
|
});
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
(() => {
|
||||||
|
const SIDEBAR_ID = 'floating-sidebar';
|
||||||
|
const TOGGLE_ID = 'floating-toggle-btn';
|
||||||
|
|
||||||
|
// 若侧边栏已存在则移除
|
||||||
|
const existing = document.getElementById(SIDEBAR_ID);
|
||||||
|
const existingBtn = document.getElementById(TOGGLE_ID);
|
||||||
|
if (existing && existingBtn) {
|
||||||
|
existing.remove();
|
||||||
|
existingBtn.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建 iframe
|
||||||
|
const iframe = document.createElement('iframe');
|
||||||
|
iframe.id = SIDEBAR_ID;
|
||||||
|
iframe.src = chrome.runtime.getURL('sidepanel/index.html');
|
||||||
|
|
||||||
|
Object.assign(iframe.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
top: '0',
|
||||||
|
right: '0',
|
||||||
|
width: '360px',
|
||||||
|
height: '100%',
|
||||||
|
border: 'none',
|
||||||
|
zIndex: '2147483647',
|
||||||
|
boxShadow: '-4px 0 8px rgba(0,0,0,0.15)',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
transition: 'transform 0.3s ease',
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(iframe);
|
||||||
|
|
||||||
|
// 创建控制按钮
|
||||||
|
const toggleBtn = document.createElement('div');
|
||||||
|
toggleBtn.id = TOGGLE_ID;
|
||||||
|
toggleBtn.textContent = '≡';
|
||||||
|
Object.assign(toggleBtn.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: '20px',
|
||||||
|
right: '20px',
|
||||||
|
width: '48px',
|
||||||
|
height: '48px',
|
||||||
|
backgroundColor: '#0078d7',
|
||||||
|
color: 'white',
|
||||||
|
borderRadius: '50%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
fontSize: '22px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
||||||
|
zIndex: '2147483648',
|
||||||
|
transition: 'background-color 0.2s ease',
|
||||||
|
});
|
||||||
|
|
||||||
|
toggleBtn.addEventListener('mouseenter', () => {
|
||||||
|
toggleBtn.style.backgroundColor = '#005fa3';
|
||||||
|
});
|
||||||
|
toggleBtn.addEventListener('mouseleave', () => {
|
||||||
|
toggleBtn.style.backgroundColor = '#0078d7';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(toggleBtn);
|
||||||
|
|
||||||
|
// 折叠 / 展开逻辑
|
||||||
|
let visible = true;
|
||||||
|
toggleBtn.addEventListener('click', () => {
|
||||||
|
visible = !visible;
|
||||||
|
iframe.style.transform = visible ? 'translateX(0)' : 'translateX(100%)';
|
||||||
|
});
|
||||||
|
// 如果使用 window.postMessage
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
// 检查消息来源是否可信 (非常重要!)
|
||||||
|
if (event.origin !== 'chrome-extension://njacfkelcmfnpdbnbkpenogmokmglkcn') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.data.action === 'closeSidebar') {
|
||||||
|
console.log('Received closeSidebar message');
|
||||||
|
visible = !visible;
|
||||||
|
iframe.style.transform = visible ? 'translateX(0)' : 'translateX(100%)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 267 B |
Binary file not shown.
|
After Width: | Height: | Size: 458 B |
Binary file not shown.
|
After Width: | Height: | Size: 890 B |
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Floating Sidebar with Toggle Button",
|
||||||
|
"version": "1.1",
|
||||||
|
"description": "带有可折叠隐藏按钮的浮动侧边栏",
|
||||||
|
"permissions": ["activeTab", "scripting"],
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"default_title": "打开侧边栏"
|
||||||
|
},
|
||||||
|
"web_accessible_resources": [
|
||||||
|
{
|
||||||
|
"resources": ["sidepanel/*"],
|
||||||
|
"matches": ["<all_urls>"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"js": ["content.js"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+288
@@ -0,0 +1,288 @@
|
|||||||
|
{
|
||||||
|
"name": "Chrome Extensions Gemini Demo",
|
||||||
|
"version": "1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "Chrome Extensions Gemini Demo",
|
||||||
|
"version": "1.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@google/generative-ai": "0.15.0",
|
||||||
|
"rollup": "4.22.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@google/generative-ai": {
|
||||||
|
"version": "0.15.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.15.0.tgz",
|
||||||
|
"integrity": "sha512-zs37judcTYFJf1U7tnuqnh7gdzF6dcWj9pNRxjA5JTONRoiQ0htrRdbefRFiewOIfXwhun5t9hbd2ray7812eQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
|
||||||
|
"cpu": [
|
||||||
|
"ppc64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
|
||||||
|
"cpu": [
|
||||||
|
"riscv64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@types/estree": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/fsevents": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "4.22.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
|
||||||
|
"integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "1.0.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-android-arm-eabi": "4.22.4",
|
||||||
|
"@rollup/rollup-android-arm64": "4.22.4",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.22.4",
|
||||||
|
"@rollup/rollup-darwin-x64": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-arm64-musl": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.22.4",
|
||||||
|
"@rollup/rollup-linux-x64-musl": "4.22.4",
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": "4.22.4",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.22.4",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.22.4",
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "Chrome Extensions Gemini Demo",
|
||||||
|
"version": "1.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup sidepanel/index.js --file dist/sidepanel.bundle.js --format iife"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"devDependencies": {
|
||||||
|
"@google/generative-ai": "0.15.0",
|
||||||
|
"rollup": "4.22.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/* sidepanel/style.css */
|
||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:hover {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button.active {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
<!-- sidepanel/index.html -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>测试工具</title>
|
||||||
|
<link rel="stylesheet" href="index.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<!-- 导航栏 -->
|
||||||
|
<nav class="navbar">
|
||||||
|
<button id="nav-timestamp" class="nav-button active">时间戳转换</button>
|
||||||
|
<button id="nav-other" class="nav-button">其他工具</button>
|
||||||
|
<button id="close" class="close-button">X</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- 页面内容容器 -->
|
||||||
|
<div class="content">
|
||||||
|
<!-- 时间戳转换页面 -->
|
||||||
|
<div id="page-timestamp" class="page active">
|
||||||
|
<div class="bg-white border rounded-lg p-2 mb-4">
|
||||||
|
<h2 class="text-lg font-bold mb-3">当前时间戳</h2>
|
||||||
|
<p class="text-3xl font-mono mb-3">
|
||||||
|
<a href="javascript:;" class="hover:text-blue-500">1762793622638</a>
|
||||||
|
<span class="text-sm ml-2">毫秒</span>
|
||||||
|
</p>
|
||||||
|
<div class="flex space-x-3">
|
||||||
|
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-white text-gray-700 shadow waves-effect">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="h-4 w-4 mr-2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z"></path>
|
||||||
|
</svg>
|
||||||
|
开始
|
||||||
|
</button>
|
||||||
|
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors disabled:opacity-50 bg-white text-gray-700 shadow waves-effect">
|
||||||
|
单个转换
|
||||||
|
</button>
|
||||||
|
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md px-2 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 waves-effect">
|
||||||
|
批量转换
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="text-lg text-gray-600 font-bold mb-4 flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="h-5 w-5 mr-2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"></path>
|
||||||
|
</svg>
|
||||||
|
时间戳转换
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mb-3">
|
||||||
|
<input type="text" placeholder="输入时间戳" id="jsTimestampI" class="flex-1 border border-gray-300 rounded-md px-2 py-1 w-0">
|
||||||
|
<select id="timestampUnit" class="ml-2 flex-1 border border-gray-300 rounded-md px-2 py-1">
|
||||||
|
<option value="s">秒(s)</option>
|
||||||
|
<option value="ms">毫秒(ms)</option>
|
||||||
|
</select>
|
||||||
|
<button id="convertTimestamp" class="inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-gray-800 text-gray-100 shadow hover:bg-gray-800/90 h-8 px-3 py-1 rounded-md waves-effect ml-2">
|
||||||
|
转换
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-3 flex-grow items-center">
|
||||||
|
<input type="text" placeholder="转换结果" id="jsTimestampO" class="flex-[2] border border-gray-300 rounded-md px-2 py-1 w-0">
|
||||||
|
<div tabindex="-1" class="multiselect flex-1" role="combobox" aria-owns="listbox-null">
|
||||||
|
<div class="multiselect__select"></div>
|
||||||
|
<div class="multiselect__tags">
|
||||||
|
<div class="multiselect__tags-wrap" style="display: none;"></div>
|
||||||
|
<div class="multiselect__spinner" style="display: none;"></div>
|
||||||
|
<input name="" type="text" autocomplete="off" spellcheck="false" placeholder="选择时区" tabindex="0" class="multiselect__input" aria-controls="listbox-null" style="width: 0px; position: absolute; padding: 0px;">
|
||||||
|
<span class="multiselect__single">Asia/Shanghai</span>
|
||||||
|
</div>
|
||||||
|
<div class="multiselect__content-wrapper" tabindex="-1" style="max-height: 300px; display: none;">
|
||||||
|
<ul class="multiselect__content" role="listbox" id="listbox-null" style="display: inline-block;">
|
||||||
|
<li class="multiselect__element" id="null-0" role="option">
|
||||||
|
<span class="multiselect__option--highlight multiselect__option--selected multiselect__option" data-select="Press enter to select" data-selected="Selected" data-deselect="Press enter to remove">
|
||||||
|
<span>Asia/Shanghai</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<!-- 更多时区选项... -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground h-8 px-3 py-1 waves-effect">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="h-4 w-4 mr-2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"></path>
|
||||||
|
</svg>
|
||||||
|
切换单位
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6">
|
||||||
|
<div class="text-lg text-gray-600 font-bold mb-4 flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="h-5 w-5 mr-2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 7.5h19.5m-19.5 0A3.75 3.75 0 0 1 6 3.75m0 0h12a3.75 3.75 0 0 1 3.75 3.75m0 0H6m0 0v12.75A3.75 3.75 0 0 1 6 15.75m0 0v-12Zm0 12h12m-12 0a3.75 3.75 0 0 0 3.75 3.75m0 0h6.75a3.75 3.75 0 0 0 3.75-3.75m0 0H9.75"></path>
|
||||||
|
</svg>
|
||||||
|
日期时间转换
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mb-3">
|
||||||
|
<input type="text" placeholder="输入日期时间 (如: 2023-10-27 10:30:00)" id="jsDatetimeI" class="flex-1 border border-gray-300 rounded-md px-2 py-1 w-0">
|
||||||
|
<div tabindex="-1" class="multiselect flex-1 ml-2" role="combobox" aria-owns="listbox-null">
|
||||||
|
<div class="multiselect__select"></div>
|
||||||
|
<div class="multiselect__tags">
|
||||||
|
<div class="multiselect__tags-wrap" style="display: none;"></div>
|
||||||
|
<div class="multiselect__spinner" style="display: none;"></div>
|
||||||
|
<input name="" type="text" autocomplete="off" spellcheck="false" placeholder="选择时区" tabindex="0" class="multiselect__input" aria-controls="listbox-null" style="width: 0px; position: absolute; padding: 0px;">
|
||||||
|
<span class="multiselect__single">Asia/Shanghai</span>
|
||||||
|
</div>
|
||||||
|
<div class="multiselect__content-wrapper" tabindex="-1" style="max-height: 300px; display: none;">
|
||||||
|
<ul class="multiselect__content" role="listbox" id="listbox-null" style="display: inline-block;">
|
||||||
|
<li class="multiselect__element" id="null-0" role="option">
|
||||||
|
<span class="multiselect__option--highlight multiselect__option--selected multiselect__option" data-select="Press enter to select" data-selected="Selected" data-deselect="Press enter to remove">
|
||||||
|
<span>Asia/Shanghai</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<!-- 更多时区选项... -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="convertDatetime" class="inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-gray-800 text-gray-100 shadow hover:bg-gray-800/90 h-8 px-3 py-1 rounded-md waves-effect ml-2">
|
||||||
|
转换
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-3 flex-grow items-center">
|
||||||
|
<input type="text" placeholder="转换结果" id="jsDatetimeO" class="flex-[2] border border-gray-300 rounded-md px-2 py-1 w-0">
|
||||||
|
<select id="datetimeUnit" class="ml-2 flex-1 border border-gray-300 rounded-md px-2 py-1">
|
||||||
|
<option value="s">秒(s)</option>
|
||||||
|
<option value="ms">毫秒(ms)</option>
|
||||||
|
</select>
|
||||||
|
<button class="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground h-8 px-3 py-1 waves-effect">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="h-4 w-4 mr-2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"></path>
|
||||||
|
</svg>
|
||||||
|
切换单位
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 其他工具页面 -->
|
||||||
|
<div id="page-other" class="page">
|
||||||
|
<h2>其他工具页面</h2>
|
||||||
|
<p>这里可以放置其他工具的内容。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
document.getElementById('close').addEventListener('click', () => {
|
||||||
|
window.parent.postMessage({ action: 'indexind' }, '*');
|
||||||
|
});
|
||||||
|
|
||||||
|
// sidepanel/index.js
|
||||||
|
// 页面切换逻辑
|
||||||
|
document.getElementById("nav-timestamp").addEventListener("click", () => {
|
||||||
|
switchPage("timestamp");
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("nav-other").addEventListener("click", () => {
|
||||||
|
switchPage("other");
|
||||||
|
});
|
||||||
|
|
||||||
|
function switchPage(pageName) {
|
||||||
|
// 隐藏所有页面
|
||||||
|
document.querySelectorAll(".page").forEach(page => {
|
||||||
|
page.classList.remove("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 移除所有导航按钮的 active 状态
|
||||||
|
document.querySelectorAll(".nav-button").forEach(button => {
|
||||||
|
button.classList.remove("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 显示目标页面
|
||||||
|
document.getElementById(`page-${pageName}`).classList.add("active");
|
||||||
|
|
||||||
|
// 激活对应的导航按钮
|
||||||
|
document.getElementById(`nav-${pageName}`).classList.add("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭按钮逻辑 (使用消息传递)
|
||||||
|
document.getElementById("close").addEventListener("click", () => {
|
||||||
|
// 向父页面发送消息请求关闭
|
||||||
|
window.parent.postMessage({ action: "closeSidebar" }, "*");
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user