-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbackground.js
More file actions
151 lines (143 loc) · 6.04 KB
/
Copy pathbackground.js
File metadata and controls
151 lines (143 loc) · 6.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// background.js
import { createContextMenus } from './core/utils.js';
import { handleTranslate, handleVisionTranslate } from './core/router.js';
import { getProvider } from './providers/index.js';
import { api } from './core/browser.js';
import { getSettings, setSettings } from './core/settings.js';
import { initI18n, t } from './core/i18n.js';
import { createLogger } from './core/log.js';
const L = createLogger('bg');
L.info('Background (static) loaded');
// ---- helpers ----
async function isForbiddenUrl(url = '') {
try {
const u = new URL(url);
if (u.protocol === 'chrome:' || u.protocol === 'edge:' || u.protocol === 'about:') return true;
if (u.protocol === 'chrome-extension:' || u.hostname === 'chromewebstore.google.com') return true;
return false;
} catch { return false; }
}
async function ensureContentScript(tabId, tabUrl) {
if (await isForbiddenUrl(tabUrl)) throw new Error('This page forbids content scripts.');
if (!api.scripting?.executeScript) throw new Error('Dynamic content script injection is not supported in this browser.');
await api.scripting.executeScript({ target: { tabId }, files: ['content/content.js'] });
}
async function sendToTabSafe(tabId, msg, tabUrl) {
try { return await api.tabs.sendMessage(tabId, msg); }
catch (e) {
if (String(e).includes('Receiving end does not exist')) {
await ensureContentScript(tabId, tabUrl);
return await api.tabs.sendMessage(tabId, msg);
}
throw e;
}
}
// ---- menus ----
async function buildMenus() {
const s = await getSettings();
await initI18n(s.uiLang || 'en');
await createContextMenus([
{ id: 'translate-selection', title: t('menuTranslate'), contexts: ['selection'] },
{ id: 'translate-page', title: t('menuTranslatePage'), contexts: ['page'] },
{ id: 'translate-image', title: t('menuTranslateImage'), contexts: ['image'] },
{ id: 'translate-pdf', title: t('menuTranslatePDF'), contexts: ['link', 'page'] }
]);
}
buildMenus().catch(e => L.error('buildMenus error', e));
// ---- context menus ----
api.contextMenus.onClicked.addListener(async (info, tab) => {
const tabId = tab?.id;
const tabUrl = tab?.url || '';
L.info('menu click', { id: info.menuItemId });
try {
if (info.menuItemId === 'translate-selection' && info.selectionText) {
L.debug('selection text', info.selectionText.slice(0, 160));
const result = await handleTranslate({ text: info.selectionText, intent: 'selection' });
L.info('selection done', { mode: result.mode, provider: result.provider });
await sendToTabSafe(tabId, { action: 'showTranslation', result }, tabUrl);
}
if (info.menuItemId === 'translate-page') {
await sendToTabSafe(tabId, { action: 'translatePage' }, tabUrl);
}
if (info.menuItemId === 'translate-image' && info.srcUrl) {
await sendToTabSafe(tabId, { action: 'translateImageAtUrl', srcUrl: info.srcUrl }, tabUrl);
}
if (info.menuItemId === 'translate-pdf') {
let pdfUrl = info.linkUrl;
if (!pdfUrl && tabUrl?.toLowerCase().includes('.pdf')) pdfUrl = tabUrl;
if (pdfUrl) {
const url = api.runtime.getURL('pages/pdf_viewer.html') + '?src=' + encodeURIComponent(pdfUrl);
api.tabs.create({ url });
}
}
} catch (e) {
L.error('menu action error', e);
try { await sendToTabSafe(tabId, { action: 'showTranslation', result: { error: e.message || String(e) } }, tabUrl); } catch { }
}
});
// ---- RPC ----
api.runtime.onMessage.addListener((msg, sender, sendResponse) => {
(async () => {
if (msg?.action === 'log') {
const lvl = msg.level || 'info';
const S = createLogger(msg.scope || 'page');
S[lvl]?.(msg.msg || '', msg.error || {});
return sendResponse?.({ ok: true });
}
if (msg?.action === 'ping') {
L.info('ping from', sender?.url || sender?.tab?.url);
return sendResponse({ pong: true });
}
if (msg?.action === 'selfTest') {
try {
const s = await getSettings();
const provider = getProvider(s.provider, s);
const out = await provider.translate({ text: 'Hello', sourceLang: 'auto', targetLang: s.targetLang || 'zh' });
return sendResponse({ ok: true, result: { provider: s.provider, sample: out.translated } });
} catch (err) {
L.error('selfTest error', err);
return sendResponse({ ok: false, error: err.message || String(err) });
}
}
if (msg?.action === 'translateText') {
try {
const result = await handleTranslate({
text: msg.text,
sourceLang: msg.sourceLang,
targetLang: msg.targetLang,
intent: msg.intent
});
return sendResponse({ ok: true, result });
} catch (err) {
L.error('translateText error', err);
return sendResponse({ ok: false, error: err.message || String(err) });
}
}
if (msg?.action === 'visionTranslate') {
try {
const result = await handleVisionTranslate(msg.details);
return sendResponse({ ok: true, result });
} catch (err) {
L.error('visionTranslate error', err);
return sendResponse({ ok: false, error: err.message || String(err) });
}
}
return sendResponse({ ok: false, error: 'Unknown background action' });
})().catch(err => {
L.error('message handler error', err);
sendResponse({ ok: false, error: err?.message || String(err) });
});
return true;
});
// ---- react to settings / lifecycle ----
api.storage.onChanged.addListener(async (changes, area) => {
if (area !== 'sync') return;
if (changes.uiLang) await buildMenus().catch(e => L.error('rebuild menus error', e));
});
api.runtime.onInstalled.addListener(async ({ reason }) => {
if (reason === 'install') {
await setSettings({ onboardingComplete: false });
api.tabs.create({ url: api.runtime.getURL('options/options.html') });
}
});
api.runtime.onStartup?.addListener?.(() => buildMenus().catch(e => L.error('startup menus error', e)));