+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/entrypoints/background.ts b/entrypoints/background.ts
new file mode 100644
index 0000000..e7754cb
--- /dev/null
+++ b/entrypoints/background.ts
@@ -0,0 +1,24 @@
+import {translator} from "./translator/translator";
+import {Config} from "./utils/model";
+
+export default defineBackground(async () => {
+
+ // 1、获得配置并监控变化
+ let config: Config = new Config();
+ await storage.getItem('local:config').then((value) => {
+ if (typeof value === 'string' && value) config = JSON.parse(value);
+ });
+ storage.watch('local:config', (newValue, oldValue) => {
+ if (typeof newValue === 'string' && newValue) config = JSON.parse(newValue);
+ });
+
+ // 2、监听来自 main 的消息
+ browser.runtime.onMessage.addListener(message => {
+ return new Promise((resolve, reject) => {
+ // 翻译
+ translator[config.service](config, message)
+ .then(resp => resolve(resp)) // 成功
+ .catch(error => reject(error)); // 失败
+ });
+ });
+});
\ No newline at end of file
diff --git a/entrypoints/compat/compat.ts b/entrypoints/compat/compat.ts
new file mode 100644
index 0000000..51b6b8c
--- /dev/null
+++ b/entrypoints/compat/compat.ts
@@ -0,0 +1,41 @@
+// 兼容部分网站独特的 DOM 结构
+
+type ReplaceFunction = (node: any, text: any) => any;
+type SelectFunction = (url: any) => any;
+
+interface ReplaceCompatFn {
+ [domain: string]: ReplaceFunction;
+}
+interface SelectCompatFn {
+ [domain: string]: SelectFunction;
+}
+
+// 根据浏览器 url.host 是获取获取主域名
+export function getMainDomain(url: any) {
+ const parts = url.split('.').reverse();
+ return `${parts[1]}.${parts[0]}`
+}
+
+// 文本替换环节的兼容函数,主域名 : 兼容函数
+export const replaceCompatFn: ReplaceCompatFn = {
+ ["youtube.com"]: (node: any, text: any) => {
+ // 替换 innerText
+ let temp = document.createElement('span');
+ temp.innerHTML = text;
+ node.innerHTML = temp.innerText;
+ return node.outerHTML;
+ },
+};
+
+// 元素 node 选择环节的兼容函数
+export const selectCompatFn: SelectCompatFn = {
+ ["mvnrepository.com"]: (node: any) => {
+ if (node.tagName.toLowerCase() === 'div' && node.classList.contains('im-description')) return true
+ },
+ ["www.aozora.gr.jp"]: (node: any) => {
+ if (node.tagName.toLowerCase() === 'div' && node.classList.contains('main_text')) return true
+ },
+ ["youtube.com"]: (node: any) => {
+ if (node.tagName.toLowerCase() === 'yt-formatted-string') return true
+ }
+}
\ No newline at end of file
diff --git a/entrypoints/content.ts b/entrypoints/content.ts
new file mode 100644
index 0000000..3780c2e
--- /dev/null
+++ b/entrypoints/content.ts
@@ -0,0 +1,65 @@
+import {Config} from "./utils/model";
+import {cssInject} from "./main/css";
+import {handler} from "./main/dom";
+import {cache} from "./utils/cache";
+
+export default defineContentScript({
+ matches: ['