trasnlate webchat removed. see #247
This commit is contained in:
parent
c0ebc646b8
commit
83f856d1ae
10 changed files with 21 additions and 211 deletions
|
|
@ -239,10 +239,6 @@
|
|||
"message": "Save as Summary",
|
||||
"description": "Button label in the webchat window to save the AI response as a message summary"
|
||||
},
|
||||
"webchat_save_as_translation": {
|
||||
"message": "Save as Translation",
|
||||
"description": "Button label in the webchat window to save the AI response as a message translation"
|
||||
},
|
||||
"chatgpt_textarea_not_found_error": {
|
||||
"message": "It seems that the ChatGPT page is taking too long to load. If it finishes loading, click the button on the right. If the problem persists, please check the service status.",
|
||||
"description": ""
|
||||
|
|
@ -2101,10 +2097,6 @@
|
|||
"message": "Choose when to translate messages: disabled, only when clicking the button, or automatically when opening a message.",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_translate_display_mode": {
|
||||
"message": "Display mode for translations",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_display_mode_inline": {
|
||||
"message": "Message pane (inline)",
|
||||
"description": ""
|
||||
|
|
@ -2113,10 +2105,6 @@
|
|||
"message": "Chat window",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_translate_display_mode_Info": {
|
||||
"message": "Choose where to display translations. Note: automatic mode always uses inline display.",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_translate_max_display_length": {
|
||||
"message": "Maximum length of displayed translation",
|
||||
"description": ""
|
||||
|
|
|
|||
|
|
@ -481,29 +481,6 @@ class MessagesArea extends HTMLElement {
|
|||
selectionInfo.style.display = "block";
|
||||
}
|
||||
|
||||
// Save as Translation button (only shown for translation webchat sessions)
|
||||
if(promptData.prompt_info?.headerMessageId && promptData.prompt_info?.translationTabId) {
|
||||
const saveTranslationButton = document.createElement('button');
|
||||
saveTranslationButton.textContent = browser.i18n.getMessage("webchat_save_as_translation");
|
||||
saveTranslationButton.classList.add('action_btn');
|
||||
saveTranslationButton.addEventListener('click', async () => {
|
||||
let finalText = removeAloneBRs(fullTextHTMLAtAssignment);
|
||||
const selectedHTML = this.getCurrentSelectionHTML();
|
||||
if(selectedHTML != "") {
|
||||
finalText = removeAloneBRs(selectedHTML);
|
||||
}
|
||||
await browser.runtime.sendMessage({
|
||||
command: "chatgpt_saveTranslation",
|
||||
text: finalText,
|
||||
headerMessageId: promptData.prompt_info.headerMessageId,
|
||||
tabId: promptData.prompt_info.translationTabId || promptData.tabId,
|
||||
});
|
||||
browser.runtime.sendMessage({command: "chatgpt_close", window_id: (await browser.windows.getCurrent()).id});
|
||||
});
|
||||
actionButtons.appendChild(saveTranslationButton);
|
||||
selectionInfo.style.display = "block";
|
||||
}
|
||||
|
||||
// diff viewer button
|
||||
if(promptData.prompt_info?.use_diff_viewer == "1") {
|
||||
const diffvButton = document.createElement('button');
|
||||
|
|
|
|||
|
|
@ -82,13 +82,8 @@ mzta-background.js (checks summarize_auto + summarize_display_mode prefs)
|
|||
|
||||
### Data Flow: Inline Translation on Message Display
|
||||
|
||||
The `translate_display_mode` preference (`'inline'` or `'webchat'`) controls where
|
||||
the translation is displayed. The `translate_auto` preference controls when it is triggered.
|
||||
|
||||
- `translate_auto = 2` (automatic) always generates inline, regardless of `translate_display_mode`.
|
||||
- `translate_auto = 1` (manual button) respects `translate_display_mode`:
|
||||
- `'inline'` → button click triggers inline generation
|
||||
- `'webchat'` → button click opens the AI chat window via `_openTranslationWebchat()`
|
||||
The `translate_auto` preference controls when translation is triggered.
|
||||
Translation always renders inline (webchat mode has been removed).
|
||||
|
||||
The target language is determined by `translate_lang` (fallback on `default_chatgpt_lang`).
|
||||
|
||||
|
|
@ -97,16 +92,14 @@ User opens/selects a message in Thunderbird
|
|||
↓
|
||||
mzta-compose-script.js (sends "initTranslation" to background)
|
||||
↓
|
||||
mzta-background.js (checks translate + translate_auto + translate_display_mode prefs)
|
||||
mzta-background.js (checks translate + translate_auto prefs)
|
||||
↓
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ translate_auto = 0 → do nothing │
|
||||
│ translate_auto = 1 → show "click to translate" button │
|
||||
│ display_mode = inline → click triggers inline gen │
|
||||
│ display_mode = webchat → click opens chat window │
|
||||
│ translate_auto = 2 → generate immediately (always inline)│
|
||||
│ translate_auto = 2 → generate immediately │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
↓ (if generating inline)
|
||||
↓
|
||||
taTranslationStore (check cache / set processing)
|
||||
↓ (cache miss)
|
||||
mzta-special-commands (via Web Worker, NOT chatgpt_web)
|
||||
|
|
|
|||
|
|
@ -88,24 +88,24 @@ The summarize feature uses two distinct prompt pathways:
|
|||
|
||||
### Translate: Inline-Only Prompt System
|
||||
|
||||
The translate feature uses a single special prompt (`prompt_translate_this`) for translating emails. It supports both inline display and webchat mode, but has no context menu entry.
|
||||
The translate feature uses a single special prompt (`prompt_translate_this`) for translating emails. Translation always renders inline (no webchat mode).
|
||||
|
||||
**Inline Translation on Message Display** (controlled by `translate_auto` and `translate_display_mode` prefs):
|
||||
**Inline Translation on Message Display** (controlled by `translate_auto` pref):
|
||||
- Uses a single special prompt: `prompt_translate_this`
|
||||
- The prompt text is appended with the target language and the email body: `prompt_text + " " + lang + ". \"" + body_text + "\""`
|
||||
- The prompt uses placeholders (`{%mail_subject%}`, `{%mail_html_body%}`, `{%thunderai_translate_lang%}`, `{%thunderai_translate_exclude_lang%}`) resolved via the standard placeholder system
|
||||
- The AI response is a JSON object: `{ "subject": "...", "body": "...", "status": "1"|"-1" }`
|
||||
- `status = "1"`: translation completed, subject and body are displayed
|
||||
- `status = "-1"`: translation skipped (excluded/target language), a "skipped" message is shown
|
||||
- Target language is determined by `translate_lang` pref, falling back to `default_chatgpt_lang`
|
||||
- Does **not** support `chatgpt_web` connection type (shows error if configured)
|
||||
- `translate_display_mode = 'inline'`: result is rendered as a styled banner (green/teal theme) in the message body via `mzta-compose-script.js`
|
||||
- `translate_display_mode = 'webchat'`: opens AI chat window; webchat shows a "Save as Translation" button to persist the result inline
|
||||
- `translate_auto = 2` (automatic) always generates inline regardless of `translate_display_mode`
|
||||
- Result is rendered as a styled banner (green/teal theme) in the message body via `mzta-compose-script.js`
|
||||
- Banner includes refresh (↻) and delete (×) buttons
|
||||
- Cached per-message via `taTranslationStore` / `taStorage` (max 100 entries)
|
||||
- The prompt was originally a regular prompt (`defaultPrompts`) and was moved to `specialPrompts` with `is_special: "1"` and `type: "1"` (reading email only)
|
||||
|
||||
**Prompt Building** — `taPromptUtils.buildTranslationPrompt(fullMessage, lang)`:
|
||||
**Prompt Building** — `taPromptUtils.buildTranslationPrompt(fullMessage)`:
|
||||
- Retrieves the `prompt_translate_this` special prompt text
|
||||
- Extracts the email body from the full message
|
||||
- Combines prompt + language + body text
|
||||
- Resolves placeholders via `placeholdersUtils.getPlaceholdersValues()` + `replacePlaceholders()`
|
||||
- Returns `{ promptText, promptInfo }`
|
||||
|
||||
## Prompt Types Reference
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ These are generated programmatically at the bottom of `mzta-options-default.js`
|
|||
| `summarize_max_display_length` | `0` | Maximum characters shown in inline summary before truncation. `0` = no limit (show full text). When set, text is truncated at a word boundary and a "See more"/"See less" toggle link is shown. |
|
||||
| `translate` | `true` | Enable email translation |
|
||||
| `translate_auto` | `0` | Auto-translate mode: `0` = disabled, `1` = manual (show button), `2` = automatic (translate on message open), `3` = generate on email receive (background pre-cache via `onNewMailReceived`, no UI during generation) |
|
||||
| `translate_display_mode` | `'inline'` | Where to display translations: `'inline'` = message pane banner, `'webchat'` = AI chat window. Note: `translate_auto = 2` and `translate_auto = 3` always use inline regardless of this setting. |
|
||||
| `translate_max_display_length` | `0` | Maximum characters shown in inline translation before truncation. `0` = no limit (show full text). When set, text is truncated at a word boundary and a "See more"/"See less" toggle link is shown. |
|
||||
| `translate_lang` | `''` | Target language for translation. Falls back to `default_chatgpt_lang` if empty. |
|
||||
|
||||
|
|
@ -134,13 +133,9 @@ The translate settings page provides:
|
|||
- `0` (Disabled) — no inline translations
|
||||
- `1` (Manual) — shows a "Get AI Translation" button in message display
|
||||
- `2` (Automatic) — generates translation immediately when message is opened
|
||||
3. **Display mode dropdown** (`translate_display_mode`) — controls where translations are shown:
|
||||
- `'inline'` — translation banner in the message pane (default)
|
||||
- `'webchat'` — opens the AI chat window with a "Save as Translation" button
|
||||
- Note: `translate_auto = 2` always generates inline regardless of this setting.
|
||||
4. **Max display length** (`translate_max_display_length`) — number input, limits inline translation text to N characters. `0` = no limit. When truncated, a "See more"/"See less" toggle link is appended.
|
||||
5. **Target language** (`translate_lang`) — text input for the destination language. If empty, falls back to `default_chatgpt_lang`.
|
||||
6. **One editable prompt** — the translation instruction prompt (`prompt_translate_this`) with Save/Reset buttons and placeholder autocomplete. Default text comes from i18n string `prompt_translate_this_full_text`.
|
||||
3. **Max display length** (`translate_max_display_length`) — number input, limits inline translation text to N characters. `0` = no limit. When truncated, a "See more"/"See less" toggle link is appended.
|
||||
4. **Target language** (`translate_lang`) — text input for the destination language. If empty, falls back to `default_chatgpt_lang`.
|
||||
5. **One editable prompt** — the translation instruction prompt (`prompt_translate_this`) with Save/Reset buttons and placeholder autocomplete. Default text comes from i18n string `prompt_translate_this_full_text`.
|
||||
|
||||
## Adding a New Preference
|
||||
|
||||
|
|
|
|||
|
|
@ -1423,7 +1423,7 @@ switch (message.command) {
|
|||
triggerBtn.onclick = () => {
|
||||
_removeToolbarItem('mzta-toolbar-translation');
|
||||
browser.runtime.sendMessage({
|
||||
command: message.webchat ? "triggerTranslationWebchat" : "triggerTranslationGeneration",
|
||||
command: "triggerTranslationGeneration",
|
||||
headerMessageId: message.headerMessageId
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
async function _initTranslation() {
|
||||
try {
|
||||
let tabId = sender.tab.id;
|
||||
let prefs = await browser.storage.sync.get({ translate: prefs_default.translate, translate_auto: prefs_default.translate_auto, translate_display_mode: prefs_default.translate_display_mode, translate_max_display_length: prefs_default.translate_max_display_length });
|
||||
let prefs = await browser.storage.sync.get({ translate: prefs_default.translate, translate_auto: prefs_default.translate_auto, translate_max_display_length: prefs_default.translate_max_display_length });
|
||||
|
||||
if (!prefs.translate) return;
|
||||
|
||||
|
|
@ -364,11 +364,7 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
}
|
||||
|
||||
// Manual button mode (translate_auto === 1)
|
||||
if (prefs.translate_display_mode === 'inline') {
|
||||
browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId });
|
||||
} else {
|
||||
browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId, webchat: true });
|
||||
}
|
||||
browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId });
|
||||
} catch (e) {
|
||||
taLog.error("Error in initTranslation: " + e);
|
||||
}
|
||||
|
|
@ -393,83 +389,17 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
}
|
||||
_triggerTranslationGeneration(message);
|
||||
break;
|
||||
case 'triggerTranslationWebchat':
|
||||
async function _triggerTranslationWebchat(message) {
|
||||
let tabId = sender.tab.id;
|
||||
let prefs_tw = await browser.storage.sync.get({
|
||||
translate_lang: prefs_default.translate_lang,
|
||||
default_chatgpt_lang: prefs_default.default_chatgpt_lang
|
||||
});
|
||||
const lang_tw = prefs_tw.translate_lang || prefs_tw.default_chatgpt_lang || '';
|
||||
if (!lang_tw) {
|
||||
let tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
browser.tabs.sendMessage(tabId, { command: "sendAlert", curr_tab_type: tabs[0].type, message: browser.i18n.getMessage('translate_no_language_configured') });
|
||||
browser.tabs.sendMessage(tabId, { command: "showTranslationButton", headerMessageId: message.headerMessageId, webchat: true });
|
||||
return;
|
||||
}
|
||||
await _openTranslationWebchat(message.headerMessageId, tabId);
|
||||
}
|
||||
_triggerTranslationWebchat(message);
|
||||
break;
|
||||
case 'refreshTranslation':
|
||||
async function _refreshTranslation(message) {
|
||||
let tabId = sender.tab.id;
|
||||
await translationStore.removeTranslation(message.headerMessageId);
|
||||
let prefs_refresh_tr = await browser.storage.sync.get({ translate_display_mode: prefs_default.translate_display_mode });
|
||||
if (prefs_refresh_tr.translate_display_mode === 'webchat') {
|
||||
await _openTranslationWebchat(message.headerMessageId, tabId);
|
||||
} else {
|
||||
await _generateTranslationForMessage(message.headerMessageId, tabId);
|
||||
}
|
||||
await _generateTranslationForMessage(message.headerMessageId, tabId);
|
||||
}
|
||||
_refreshTranslation(message);
|
||||
break;
|
||||
case 'removeTranslation':
|
||||
translationStore.removeTranslation(message.headerMessageId);
|
||||
break;
|
||||
case 'chatgpt_saveTranslation':
|
||||
async function _saveTranslationFromWebchat(msg) {
|
||||
try {
|
||||
let rawText = msg.text.trim();
|
||||
let translatedBody = '';
|
||||
let translatedSubject = '';
|
||||
let translationStatus = '';
|
||||
try {
|
||||
const parsed = JSON.parse(rawText);
|
||||
translatedBody = parsed.body || '';
|
||||
translatedSubject = parsed.subject || '';
|
||||
translationStatus = String(parsed.status || '');
|
||||
} catch (e) {
|
||||
translatedBody = rawText;
|
||||
}
|
||||
let prefs_tr = await browser.storage.sync.get({
|
||||
translate_lang: prefs_default.translate_lang,
|
||||
default_chatgpt_lang: prefs_default.default_chatgpt_lang,
|
||||
translate_max_display_length: prefs_default.translate_max_display_length
|
||||
});
|
||||
let lang = prefs_tr.translate_lang || prefs_tr.default_chatgpt_lang || '';
|
||||
const translationData = {
|
||||
translated_text: translatedBody,
|
||||
translated_subject: translatedSubject,
|
||||
translation_status: translationStatus,
|
||||
lang: lang,
|
||||
headerMessageId: msg.headerMessageId
|
||||
};
|
||||
await translationStore.saveTranslation(translationData, msg.headerMessageId);
|
||||
try {
|
||||
browser.tabs.sendMessage(msg.tabId, {
|
||||
command: "showTranslation",
|
||||
data: { ...translationData, maxDisplayLength: prefs_tr.translate_max_display_length }
|
||||
});
|
||||
} catch (e) {
|
||||
taLog.error("Error sending showTranslation to tab: " + e);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[ThunderAI] Error saving translation from webchat:", error);
|
||||
}
|
||||
}
|
||||
_saveTranslationFromWebchat(message);
|
||||
break;
|
||||
case 'chatgpt_close':
|
||||
async function _closeChatGptWindow(window_id) {
|
||||
let prefs_close = await browser.storage.sync.get({chatgpt_win_save_position: prefs_default.chatgpt_win_save_position});
|
||||
|
|
@ -980,45 +910,6 @@ async function _openSummaryWebchat(headerMessageId, tabId) {
|
|||
}
|
||||
}
|
||||
|
||||
async function _openTranslationWebchat(headerMessageId, tabId) {
|
||||
try {
|
||||
const messageResult = await browser.messages.query({ headerMessageId: headerMessageId });
|
||||
if (!messageResult || messageResult.messages.length === 0) {
|
||||
console.error("[ThunderAI] _openTranslationWebchat: Message not found for headerMessageId:", headerMessageId);
|
||||
return;
|
||||
}
|
||||
|
||||
const curr_message = messageResult.messages[0];
|
||||
const curr_message_full = await browser.messages.getFull(curr_message.id);
|
||||
|
||||
const prefs = await browser.storage.sync.get({
|
||||
...prefs_default,
|
||||
translate_lang: prefs_default.translate_lang,
|
||||
default_chatgpt_lang: prefs_default.default_chatgpt_lang
|
||||
});
|
||||
const connectionType = getConnectionType(prefs, {}, 'translate');
|
||||
if (connectionType === 'chatgpt_web') {
|
||||
const errorMsg = browser.i18n.getMessage('translate_chatgpt_web_not_supported');
|
||||
await translationStore.saveError(headerMessageId, errorMsg);
|
||||
browser.tabs.sendMessage(tabId, { command: "showTranslation", data: { error: true, message: errorMsg } });
|
||||
return;
|
||||
}
|
||||
|
||||
const lang = prefs.translate_lang || prefs.default_chatgpt_lang || '';
|
||||
if (!lang) {
|
||||
taLog.warn("Translation skipped: no language configured (translate_lang and default_chatgpt_lang are both empty).");
|
||||
return;
|
||||
}
|
||||
const { promptText, promptInfo } = await taPromptUtils.buildTranslationPrompt(curr_message_full);
|
||||
promptInfo.headerMessageId = headerMessageId;
|
||||
promptInfo.translationTabId = tabId;
|
||||
|
||||
openChatGPT(promptText, promptInfo.action, tabId, promptInfo.name, promptInfo.need_custom_text, promptInfo);
|
||||
} catch (error) {
|
||||
console.error("[ThunderAI] Error opening translation webchat:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for messages from ThunderAI-Sparks
|
||||
browser.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
|
||||
switch (message.action) {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ export const prefs_default = {
|
|||
summarize_max_display_length: 0, // 0 = no limit, otherwise max chars shown inline
|
||||
translate: true,
|
||||
translate_auto: 0, // 0: disabled, 1: manual button, 2: automatic on message open, 3: generate on email receive
|
||||
translate_display_mode: 'inline', // 'inline' or 'webchat'
|
||||
translate_max_display_length: 0, // 0 = no limit, otherwise max chars shown inline
|
||||
translate_lang: '', // target language, fallback on default_chatgpt_lang
|
||||
translate_exclude_lang: '', // languages to do not translate
|
||||
|
|
|
|||
|
|
@ -40,18 +40,6 @@
|
|||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="translate_tr">
|
||||
<td><span class="opt_title">__MSG_prefs_OptionText_translate_display_mode__</span></td>
|
||||
<td>
|
||||
<label>
|
||||
<select id="translate_display_mode" name="translate_display_mode" class="option-input">
|
||||
<option value="inline">__MSG_prefs_OptionText_display_mode_inline__</option>
|
||||
<option value="webchat">__MSG_prefs_OptionText_display_mode_webchat__</option>
|
||||
</select>
|
||||
<br>__MSG_prefs_OptionText_translate_display_mode_Info__
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="translate_tr">
|
||||
<td><span class="opt_title">__MSG_prefs_OptionText_translate_max_display_length__</span></td>
|
||||
<td>
|
||||
|
|
|
|||
|
|
@ -74,8 +74,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||
document.querySelectorAll(".option-input").forEach(element => {
|
||||
element.addEventListener("change", saveOptions);
|
||||
});
|
||||
document.getElementById('translate_auto').addEventListener('change', updateDisplayModeConstraint);
|
||||
|
||||
let translate_textarea = document.getElementById("translate_prompt_text");
|
||||
let translate_save_btn = document.getElementById("btn_save_prompt");
|
||||
let translate_reset_btn = document.getElementById("btn_reset_prompt");
|
||||
|
|
@ -119,21 +117,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||
|
||||
// Methods to manage options, derived from: /options/mzta-options.js
|
||||
|
||||
function updateDisplayModeConstraint() {
|
||||
const translate_auto_el = document.getElementById('translate_auto');
|
||||
const display_mode_el = document.getElementById('translate_display_mode');
|
||||
const autoVal = String(translate_auto_el.value);
|
||||
if (autoVal === '2' || autoVal === '3') {
|
||||
display_mode_el.value = 'inline';
|
||||
display_mode_el.disabled = true;
|
||||
browser.storage.sync.set({ translate_display_mode: 'inline' });
|
||||
} else if (autoVal === '0') {
|
||||
display_mode_el.disabled = true;
|
||||
} else {
|
||||
display_mode_el.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
let options = {};
|
||||
|
|
@ -191,9 +174,6 @@ async function restoreOptions() {
|
|||
if (element.id === 'translate_auto') {
|
||||
default_select_value = prefs_default.translate_auto;
|
||||
}
|
||||
if (element.id === 'translate_display_mode') {
|
||||
default_select_value = prefs_default.translate_display_mode;
|
||||
}
|
||||
const restoreValue = result[element.id] ?? default_select_value;
|
||||
let optionExists = Array.from(element.options).some(opt => opt.value === String(restoreValue));
|
||||
if (element.tomselect) {
|
||||
|
|
@ -248,5 +228,4 @@ async function restoreOptions() {
|
|||
}
|
||||
|
||||
setCurrentChoice(getting);
|
||||
updateDisplayModeConstraint();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue