对于没有接触过的领域,大模型辅助开发,快速验证想法
目录
-
项目概述
-
项目结构
-
详细代码实现
-
manifest.json
-
popup.html
-
popup.css
-
popup.js
-
background.js
-
-
功能实现步骤
-
保存网页到本地
-
配置 GitHub 信息并保存到本地存储
-
将网页提交到 GitHub 仓库
-
-
常见问题及解决办法
-
prompt 没有弹框
-
弹窗太小文件名显示不下
-
browser.storage is undefined
-
项目概述
开发一个火狐浏览器插件,实现将当前打开的网页保存成单一 HTML 文件的功能。用户可以设置保存目录,还能配置 GitHub 访问令牌、用户名、仓库名和分支名,将网页文件提交到 GitHub 仓库。配置信息会保存到本地,每次打开插件时可获取之前的配置。
项目结构
x-webpage-conserve/├── manifest.json├── popup/│ ├── popup.html│ ├── popup.css│ └── popup.js├── background.js
详细代码实现
manifest.json
{"manifest_version": 2,"name": "Webpage Saver","version": "1.0","description": "Save the current web page as a single HTML file and set the save directory.","browser_action": {"default_icon": {"16": "icon16.png","32": "icon32.png"},"default_title": "Save Webpage","default_popup": "popup/popup.html"},"permissions": ["activeTab","downloads","storage"],"background": {"scripts": ["background.js"]}}
popup.html
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="popup.css"><title>Webpage Saver</title><style>.modal {display: none;position: fixed;z-index: 1;left: 0;top: 0;width: 100%;height: 100%;overflow: auto;background-color: rgba(0, 0, 0, 0.4);}.modal-content {background-color: #fefefe;margin: 15% auto;padding: 20px;border: 1px solid #888;width: 80%;max-width: 600px;}.close {color: #aaa;float: right;font-size: 28px;font-weight: bold;}.close:hover,.close:focus {color: black;text-decoration: none;cursor: pointer;}</style></head><body><button id="saveButton">Save Current Page</button><button id="setDirectoryButton">Set Save Directory</button><button id="configureGithubButton">Configure GitHub</button><!-- 模态框 --><div id="myModal" class="modal"><div class="modal-content"><span class="close">×</span><h2>Confirm File Name</h2><input type="text" id="filenameInput" style="width: 100%;"><button id="confirmButton">Confirm</button></div></div><!-- GitHub 配置模态框 --><div id="githubConfigModal" class="modal"><div class="modal-content"><span class="close" id="closeGithubConfig">×</span><h2>GitHub Configuration</h2><label for="githubToken">Access Token:</label><input type="text" id="githubToken" style="width: 100%;"><br><label for="githubUsername">Username:</label><input type="text" id="githubUsername" style="width: 100%;"><br><label for="githubRepo">Repository Name:</label><input type="text" id="githubRepo" style="width: 100%;"><br><label for="githubBranch">Branch Name:</label><input type="text" id="githubBranch" style="width: 100%;"><br><button id="saveGithubConfigButton">Save Configuration</button></div></div><script src="popup.js"></script></body></html>
popup.css
body {width: 200px;padding: 20px;font-family: Arial, sans-serif;}button {width: 100%;padding: 10px;margin-bottom: 10px;background-color: #007BFF;color: white;border: none;cursor: pointer;}button:hover {background-color: #0056b3;}
popup.js
document.addEventListener('DOMContentLoaded', async function () {const saveButton = document.getElementById('saveButton');const setDirectoryButton = document.getElementById('setDirectoryButton');const configureGithubButton = document.getElementById('configureGithubButton');const modal = document.getElementById('myModal');const closeBtn = document.getElementsByClassName('close')[0];const confirmButton = document.getElementById('confirmButton');const filenameInput = document.getElementById('filenameInput');const githubConfigModal = document.getElementById('githubConfigModal');const closeGithubConfig = document.getElementById('closeGithubConfig');const saveGithubConfigButton = document.getElementById('saveGithubConfigButton');const githubTokenInput = document.getElementById('githubToken');const githubUsernameInput = document.getElementById('githubUsername');const githubRepoInput = document.getElementById('githubRepo');const githubBranchInput = document.getElementById('githubBranch');// 从本地存储中获取之前的 GitHub 配置信息const { githubConfig } = await browser.storage.local.get('githubConfig');if (githubConfig) {githubTokenInput.value = githubConfig.token;githubUsernameInput.value = githubConfig.username;githubRepoInput.value = githubConfig.repo;githubBranchInput.value = githubConfig.branch;}saveButton.addEventListener('click', function () {browser.tabs.query({ active: true, currentWindow: true }).then(function (tabs) {const activeTab = tabs[0];const currentDate = new Date();const timestamp = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(currentDate.getDate()).padStart(2, '0')}_${String(currentDate.getHours()).padStart(2, '0')}-${String(currentDate.getMinutes()).padStart(2, '0')}-${String(currentDate.getSeconds()).padStart(2, '0')}`;const defaultFilename = `${activeTab.title.replace(/[\/:*?"<>|]/g, '_')}_${timestamp}.html`;filenameInput.value = defaultFilename;modal.style.display = "block";}).catch(function (error) {console.error('Tab query error:', error);});});closeBtn.onclick = function () {modal.style.display = "none";}confirmButton.addEventListener('click', function () {const userInput = filenameInput.value;if (userInput) {const githubConfig = {token: githubTokenInput.value,username: githubUsernameInput.value,repo: githubRepoInput.value,branch: githubBranchInput.value};browser.runtime.sendMessage({ action: 'savePage', filename: userInput, githubConfig });modal.style.display = "none";}});setDirectoryButton.addEventListener('click', function () {browser.runtime.sendMessage({ action: 'setSaveDirectory' });});configureGithubButton.addEventListener('click', function () {githubConfigModal.style.display = "block";});closeGithubConfig.onclick = function () {githubConfigModal.style.display = "none";}saveGithubConfigButton.addEventListener('click', async function () {const githubConfig = {token: githubTokenInput.value,username: githubUsernameInput.value,repo: githubRepoInput.value,branch: githubBranchInput.value};// 将 GitHub 配置信息保存到本地存储await browser.storage.local.set({ githubConfig });githubConfigModal.style.display = "none";});});
background.js
let saveDirectory = null;browser.runtime.onMessage.addListener(function (message, sender, sendResponse) {if (message.action === 'savePage') {browser.tabs.query({ active: true, currentWindow: true }).then(function (tabs) {const activeTab = tabs[0];const hasGithubConfig = message.githubConfig && message.githubConfig.token && message.githubConfig.username && message.githubConfig.repo && message.githubConfig.branch;if (hasGithubConfig) {// 获取网页内容browser.tabs.executeScript(activeTab.id, { code: 'document.documentElement.outerHTML' }, function (result) {if (result && result.length > 0) {const content = result[0];const base64Content = btoa(unescape(encodeURIComponent(content)));const apiUrl = `https://api.github.com/repos/${message.githubConfig.username}/${message.githubConfig.repo}/contents/${message.filename}`;const headers = {'Authorization': `token ${message.githubConfig.token}`,'Content-Type': 'application/json'};const body = {message: `Save ${message.filename}`,content: base64Content,branch: message.githubConfig.branch};fetch(apiUrl, {method: 'PUT',headers: headers,body: JSON.stringify(body)}).then(response => response.json()).then(data => console.log('GitHub response:', data)).catch(error => console.error('GitHub error:', error));}});} else {const url = activeTab.url;const options = {url: url,saveAs: false};if (saveDirectory) {options.filename = `${saveDirectory}/${message.filename}`;} else {options.filename = message.filename;}browser.downloads.download(options).then(function (downloadId) {console.log('Download started with ID:', downloadId);}).catch(function (error) {console.error('Download error:', error);});}}).catch(function (error) {console.error('Tab query error:', error);});} else if (message.action === 'setSaveDirectory') {browser.downloads.showDefaultFolder().then(() => {browser.downloads.setShelfEnabled(false);browser.downloads.onDeterminingFilename.addListener(function (item, suggest) {saveDirectory = item.filename.split('/').slice(0, -1).join('/');suggest({ filename: item.filename });browser.downloads.setShelfEnabled(true);return true;});});}});
功能实现步骤
保存网页到本地
-
用户点击 “Save Current Page” 按钮。
-
弹出模态框,显示默认文件名(网页标题 + 时间戳),用户可确认或修改文件名。
-
若未配置 GitHub 信息,将网页文件保存到本地指定目录(若设置了保存目录)或默认下载目录。
配置 GitHub 信息并保存到本地存储
-
用户点击 “Configure GitHub” 按钮,弹出 GitHub 配置模态框。
-
用户输入 GitHub 访问令牌、用户名、仓库名和分支名。
-
点击 “Save Configuration” 按钮,将配置信息保存到本地存储。
将网页提交到 GitHub 仓库
-
用户点击 “Save Current Page” 按钮,弹出文件名确认模态框。
-
若已配置有效的 GitHub 信息,获取当前网页的 HTML 内容,将其转换为 Base64 编码。
-
使用 GitHub API 的
PUT
请求将内容提交到指定的 GitHub 仓库和分支。
常见问题及解决办法
prompt 没有弹框
-
代码执行环境问题:将与用户交互的部分逻辑移到
popup.js
中,因为background.js
是在后台运行的,没有直接的用户界面交互。 -
浏览器安全策略限制:确保
prompt
是在用户点击按钮等明确的交互操作之后被调用的。 -
代码逻辑问题:添加日志输出,确认代码是否执行到了
prompt
这一行。
弹窗太小文件名显示不下
-
使用自定义模态框替代
prompt
:在popup.html
中创建自定义模态框,更好地控制其样式和大小。 -
优化文件名显示:对文件名进行截断处理,并添加提示信息,让用户知道文件名被截断了。
browser.storage is undefined
-
确保
manifest.json
中添加了storage
权限:在permissions
中添加"storage"
。 -
检查代码运行环境:确保代码是在支持
browser
对象的环境中运行。 -
检查代码是否正确引入和加载:确保
popup.js
文件正确引入到popup.html
中。 -
兼容性处理(可选):添加代码判断
window.browser
是否存在,若不存在则使用window.chrome
。
将插件发布到火狐市场主要有以下步骤:
-
注册开发者账号:访问Firefox开发者中心,首次使用会自动打开注册登录页,按照提示完成注册流程。注册过程中可能需要进行两步验证,可根据个人情况选择验证方式,如使用手机令牌工具等。
-
准备插件文件
-
确保代码规范:编写的插件代码要符合Firefox扩展的开发规范,遵循WebExtension API标准等,保证插件功能正常且稳定,没有明显的漏洞和错误。
-
打包文件:将插件代码打包成.zip格式。注意压缩包里面应该是manifest.json这一层文件目录,不能在manifest.json外再包一层目录。
-
-
提交插件
-
登录开发者中心:注册登录后点击“提交你的第一个附加组件”。
-
选择发布形式:接收协议及进行人机身份验证,然后根据个人情况选择发布形式,点击“继续”。
-
上传文件:按要求选择需要发布的.zip文件进行上传。
-
-
填写插件信息:提交成功后进入等待审核状态,可以在编辑产品页面,编辑描述附加组件、图像、附加信息和技术信息等,如插件的功能介绍、使用说明、适用的Firefox版本等,以便用户更好地了解和使用插件。
-
等待审核:提交后,Firefox官方团队会对插件进行审核,审核内容包括插件的功能、安全性、兼容性等方面。审核时间可能会有所不同,需耐心等待。审核通过后,插件就会成功发布到火狐市场。