新增備份、恢復、右鍵替換詞語功能

This commit is contained in:
tangsong 2025-01-16 18:24:44 +08:00
parent db0a715fc8
commit dc6c364f29
8 changed files with 194 additions and 87 deletions

BIN
img/config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

View File

@ -1,2 +1,14 @@
# 支語寶
將所有網頁中的指定詞語替換為其他詞語
## 設定畫面
提供新增刪除,以及匯出匯入功能
## 使用方式
1. 可進入設定畫面新增詞彙
![設定畫面](./img/config.png)
2. 反白想替換的文字,並選擇「替換詞語」
![替換詞語](./img/edit.png)
## 其他
所有程式碼皆為Chatgpt撰寫並手動修改部分用詞

View File

@ -9,3 +9,38 @@ chrome.runtime.onInstalled.addListener(() => {
}
});
});
// 創建右鍵選單
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "replaceWord",
title: "替換詞語",
contexts: ["selection"] // 僅在選取文字時顯示
});
});
// 處理選單點擊事件
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "replaceWord" && info.selectionText) {
const selectedText = info.selectionText;
// 提示用戶輸入替換詞語
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: (selectedText) => {
const newWord = prompt(`將「${selectedText}」替換為:`, "");
return { selectedText, newWord };
},
args: [selectedText]
}, (results) => {
const { selectedText, newWord } = results[0].result;
if (newWord && newWord !== selectedText) {
// 儲存到 replacements 中
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
replacements[selectedText] = newWord;
chrome.storage.local.set({ replacements });
alert(`已將「${selectedText}」替換為「${newWord}`);
});
}
});
}
});

View File

@ -1,35 +1,42 @@
// 替換文字的函數
function replaceText(node, replacements) {
if (node.nodeType === Node.TEXT_NODE) {
let text = node.nodeValue;
for (let [key, value] of Object.entries(replacements)) {
const regex = new RegExp(key, "g");
text = text.replace(regex, value);
}
node.nodeValue = text;
} else if (node.nodeType === Node.ELEMENT_NODE) {
node.childNodes.forEach((child) => replaceText(child, replacements));
if (node.nodeType === Node.TEXT_NODE) {
let text = node.nodeValue;
for (let [key, value] of Object.entries(replacements)) {
const regex = new RegExp(key, "g");
text = text.replace(regex, value);
}
node.nodeValue = text;
} else if (node.nodeType === Node.ELEMENT_NODE) {
node.childNodes.forEach((child) => replaceText(child, replacements));
}
}
// 初始化替換
function initializeReplacements() {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
replaceText(document.body, replacements);
// 初始化替換
function initializeReplacements() {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
replaceText(document.body, replacements);
// 監控動態內容
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE) {
replaceText(node, replacements);
}
});
// 監控動態內容
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE) {
replaceText(node, replacements);
}
});
});
observer.observe(document.body, { childList: true, subtree: true });
});
}
observer.observe(document.body, { childList: true, subtree: true });
});
}
initializeReplacements();
// 監聽 storage 更新,動態更新替換內容
chrome.storage.onChanged.addListener((changes) => {
if (changes.replacements) {
initializeReplacements();
}
});
initializeReplacements();

View File

@ -1,9 +1,9 @@
{
"manifest_version": 3,
"name": "支語寶",
"version": "0.1.0 alpha",
"version": "0.1.0",
"description": "將所有網頁中的指定詞語替換為其他詞語。",
"permissions": ["storage", "activeTab", "scripting"],
"permissions": ["storage", "activeTab", "scripting", "contextMenus"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js"

View File

@ -6,7 +6,7 @@
<title>支語寶</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
table { width: 600px; border-collapse: collapse; margin-bottom: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f4f4f4; }
button { margin-right: 10px; }
@ -14,6 +14,7 @@
</head>
<body>
<h1>支語寶</h1>
<h2>設定詞語</h2>
<table id="replacementsTable">
<thead>
<tr>
@ -27,6 +28,11 @@
<input type="text" id="original" placeholder="原始詞語">
<input type="text" id="replacement" placeholder="替換詞語">
<button id="addButton">新增</button>
<br /><br />
<h2>備份/恢復</h2>
<button id="exportButton">匯出</button>
<input type="file" id="importFile" style="display: none;" />
<button id="importButton">匯入</button>
<script src="options.js"></script>
</body>

View File

@ -1,58 +1,105 @@
document.addEventListener("DOMContentLoaded", () => {
const tableBody = document.querySelector("#replacementsTable tbody");
const originalInput = document.getElementById("original");
const replacementInput = document.getElementById("replacement");
const addButton = document.getElementById("addButton");
const tableBody = document.querySelector("#replacementsTable tbody");
const originalInput = document.getElementById("original");
const replacementInput = document.getElementById("replacement");
const addButton = document.getElementById("addButton");
const exportButton = document.getElementById("exportButton");
const importButton = document.getElementById("importButton");
const importFileInput = document.getElementById("importFile");
// 載入詞語對
function loadReplacements() {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
tableBody.innerHTML = "";
for (const [key, value] of Object.entries(replacements)) {
addRow(key, value);
}
});
}
// 新增表格行
function addRow(original, replacement) {
const row = document.createElement("tr");
row.innerHTML = `
<td>${original}</td>
<td>${replacement}</td>
<td>
<button class="deleteButton">刪除</button>
</td>
`;
row.querySelector(".deleteButton").addEventListener("click", () => {
deleteReplacement(original);
});
tableBody.appendChild(row);
}
// 新增詞語對
addButton.addEventListener("click", () => {
const original = originalInput.value.trim();
const replacement = replacementInput.value.trim();
if (!original || !replacement) return alert("請輸入有效的詞語!");
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
replacements[original] = replacement;
chrome.storage.local.set({ replacements }, loadReplacements);
});
originalInput.value = "";
replacementInput.value = "";
// 載入詞語對
function loadReplacements() {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
tableBody.innerHTML = "";
for (const [key, value] of Object.entries(replacements)) {
addRow(key, value);
}
});
}
// 刪除詞語對
function deleteReplacement(original) {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
delete replacements[original];
chrome.storage.local.set({ replacements }, loadReplacements);
});
}
// 新增表格行
function addRow(original, replacement) {
const row = document.createElement("tr");
row.innerHTML = `
<td>${original}</td>
<td>${replacement}</td>
<td>
<button class="deleteButton">刪除</button>
</td>
`;
row.querySelector(".deleteButton").addEventListener("click", () => {
deleteReplacement(original);
});
tableBody.appendChild(row);
}
loadReplacements();
// 新增詞語對
addButton.addEventListener("click", () => {
const original = originalInput.value.trim();
const replacement = replacementInput.value.trim();
if (!original || !replacement) return alert("請輸入有效的詞語!");
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
replacements[original] = replacement;
chrome.storage.local.set({ replacements }, loadReplacements);
});
originalInput.value = "";
replacementInput.value = "";
});
// 刪除詞語對
function deleteReplacement(original) {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
delete replacements[original];
chrome.storage.local.set({ replacements }, loadReplacements);
});
}
// 匯出詞語對
exportButton.addEventListener("click", () => {
chrome.storage.local.get("replacements", (data) => {
const replacements = data.replacements || {};
const blob = new Blob([JSON.stringify(replacements, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "replacements.json";
a.click();
URL.revokeObjectURL(url);
});
});
// 匯入詞語對
importButton.addEventListener("click", () => {
importFileInput.click();
});
importFileInput.addEventListener("change", (event) => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try {
const importedReplacements = JSON.parse(e.target.result);
if (typeof importedReplacements !== "object") throw new Error();
chrome.storage.local.get("replacements", (data) => {
const currentReplacements = data.replacements || {};
const mergedReplacements = { ...currentReplacements, ...importedReplacements };
chrome.storage.local.set({ replacements: mergedReplacements }, loadReplacements);
});
} catch (error) {
alert("匯入失敗,請確保檔案格式正確!");
}
};
reader.readAsText(file);
importFileInput.value = ""; // 重置 input允許重複上傳同一檔案
});
loadReplacements();
});