-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsidepanel.js
More file actions
92 lines (84 loc) · 5.98 KB
/
Copy pathsidepanel.js
File metadata and controls
92 lines (84 loc) · 5.98 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
// sidepanel.js — 浏览器扩展完整工作台。
(function () {
const SETTINGS_KEY = 'floatingSettings';
const HISTORY_KEY = 'translateHistory';
const directions = [['auto', '自'], ['zh2en', '中英'], ['en2zh', '英中']];
let directionIndex = 0;
const input = document.getElementById('input');
const output = document.getElementById('output');
const status = document.getElementById('status');
const translateButton = document.getElementById('translate');
const directionButton = document.getElementById('direction');
const visibility = document.getElementById('visibility');
const theme = document.getElementById('theme');
const ocrFile = document.getElementById('ocrFile');
function setStatus(text, error) { status.textContent = text || ''; status.classList.toggle('error', Boolean(error)); }
function applyTheme(value) { document.body.dataset.theme = value === 'system' ? (matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : value; }
function setDirection() { directionButton.textContent = directions[directionIndex][1]; }
async function translate() {
const text = input.value.trim();
if (!text) return setStatus('请先输入文字', true);
translateButton.disabled = true;
setStatus('翻译中…');
try {
const response = await chrome.runtime.sendMessage({ type: 'TRANSLATE_TEXT', text, direction: directions[directionIndex][0] });
if (!response || response.error) throw new Error(response?.error || '后台服务无响应');
output.value = response.translated || '';
setStatus('翻译完成');
saveHistory(text, output.value);
} catch (error) { setStatus(error.message, true); }
finally { translateButton.disabled = false; }
}
async function saveHistory(source, translated) {
const data = await chrome.storage.local.get(HISTORY_KEY);
const history = data[HISTORY_KEY] || [];
if (history[0]?.source === source) history[0] = { source, translated };
else history.unshift({ source, translated });
history.length = Math.min(history.length, 20);
await chrome.storage.local.set({ [HISTORY_KEY]: history });
renderHistory(history);
}
function escapeHtml(value) { return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
function renderHistory(history) {
const element = document.getElementById('history');
element.innerHTML = history.map((item, index) => `<button class="history-item" data-index="${index}"><strong>${escapeHtml(item.source)}</strong><span>${escapeHtml(item.translated)}</span></button>`).join('');
element.querySelectorAll('.history-item').forEach((item) => item.addEventListener('click', () => {
const value = history[Number(item.dataset.index)];
input.value = value.source; output.value = value.translated; setStatus('已载入历史记录');
}));
}
async function saveSettings() {
const settings = { visibility: visibility.value, theme: theme.value };
await chrome.storage.local.set({ [SETTINGS_KEY]: settings });
applyTheme(settings.theme);
chrome.runtime.sendMessage({ type: 'FLOATING_SETTINGS_CHANGED', settings });
}
async function runOcr(file) {
if (!file) return;
setStatus('识别图片文字中…');
try {
const image = await new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = () => reject(new Error('图片读取失败')); reader.readAsDataURL(file); });
const response = await chrome.runtime.sendMessage({ type: 'TRANSLATE_IMAGE_OCR', image });
if (!response || response.error) throw new Error(response?.error || '后台服务无响应');
input.value = (response.results || []).map((item) => item.text).join('\n');
output.value = (response.results || []).map((item) => item.translated).join('\n');
setStatus(response.results?.length ? `已翻译 ${response.results.length} 段文字` : '未识别到文字');
} catch (error) { setStatus(error.message, true); }
}
translateButton.addEventListener('click', translate);
directionButton.addEventListener('click', () => { directionIndex = (directionIndex + 1) % directions.length; setDirection(); });
document.getElementById('copy').addEventListener('click', async () => { if (!output.value) return setStatus('没有可复制的译文', true); await navigator.clipboard.writeText(output.value); setStatus('已复制'); });
document.getElementById('clear').addEventListener('click', () => { input.value = ''; output.value = ''; setStatus(''); input.focus(); });
input.addEventListener('keydown', (event) => { if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) { event.preventDefault(); translate(); } });
document.getElementById('page').addEventListener('click', async () => { const result = await chrome.runtime.sendMessage({ type: 'RUN_PAGE_TRANSLATE' }); setStatus(result?.error || '已开始翻译整页', Boolean(result?.error)); });
document.getElementById('restore').addEventListener('click', async () => { const result = await chrome.runtime.sendMessage({ type: 'RESTORE_PAGE' }); setStatus(result?.error || `已还原 ${result?.removed || 0} 条译文`, Boolean(result?.error)); });
document.getElementById('ocr').addEventListener('click', () => ocrFile.click());
ocrFile.addEventListener('change', (event) => { runOcr(event.target.files[0]); event.target.value = ''; });
visibility.addEventListener('change', saveSettings); theme.addEventListener('change', saveSettings);
matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { if (theme.value === 'system') applyTheme('system'); });
chrome.storage.local.get([SETTINGS_KEY, HISTORY_KEY]).then((data) => {
const settings = data[SETTINGS_KEY] || { visibility: 'manual', theme: 'system' };
visibility.value = settings.visibility; theme.value = settings.theme; applyTheme(theme.value); renderHistory(data[HISTORY_KEY] || []);
});
setDirection();
})();