新增備份、恢復、右鍵替換詞語功能
This commit is contained in:
parent
db0a715fc8
commit
dc6c364f29
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
12
readme.md
12
readme.md
|
|
@ -1,2 +1,14 @@
|
|||
# 支語寶
|
||||
將所有網頁中的指定詞語替換為其他詞語
|
||||
|
||||
## 設定畫面
|
||||
提供新增刪除,以及匯出匯入功能
|
||||
|
||||
## 使用方式
|
||||
1. 可進入設定畫面新增詞彙
|
||||

|
||||
2. 反白想替換的文字,並選擇「替換詞語」
|
||||

|
||||
|
||||
## 其他
|
||||
所有程式碼皆為Chatgpt撰寫,並手動修改部分用詞
|
||||
|
|
@ -8,4 +8,39 @@ 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}」`);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -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);
|
||||
|
||||
// 監控動態內容
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化替換
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
}
|
||||
|
||||
// 監聽 storage 更新,動態更新替換內容
|
||||
chrome.storage.onChanged.addListener((changes) => {
|
||||
if (changes.replacements) {
|
||||
initializeReplacements();
|
||||
}
|
||||
|
||||
initializeReplacements();
|
||||
});
|
||||
|
||||
initializeReplacements();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
159
src/options.js
159
src/options.js
|
|
@ -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");
|
||||
|
||||
// 載入詞語對
|
||||
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 = "";
|
||||
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 deleteReplacement(original) {
|
||||
chrome.storage.local.get("replacements", (data) => {
|
||||
const replacements = data.replacements || {};
|
||||
delete replacements[original];
|
||||
chrome.storage.local.set({ replacements }, loadReplacements);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 新增詞語對
|
||||
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();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue