|
@@ -57,9 +57,15 @@
|
|
|
#${PANEL_ID} .eks-error { color: #b91c1c; white-space: pre-wrap; }
|
|
#${PANEL_ID} .eks-error { color: #b91c1c; white-space: pre-wrap; }
|
|
|
#${PANEL_ID} .eks-result { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; white-space: pre-wrap; min-height: 44px; }
|
|
#${PANEL_ID} .eks-result { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 10px; white-space: pre-wrap; min-height: 44px; }
|
|
|
.eks-inline-translate {
|
|
.eks-inline-translate {
|
|
|
- margin-left: 6px !important; border: 0 !important; border-radius: 999px !important;
|
|
|
|
|
- padding: 3px 7px !important; background: #e0f2fe !important; color: #075985 !important;
|
|
|
|
|
- font: 600 11px system-ui, sans-serif !important; cursor: pointer !important;
|
|
|
|
|
|
|
+ margin-left: 4px !important; border: 0 !important; border-radius: 999px !important;
|
|
|
|
|
+ padding: 1px 5px !important; background: #e0f2fe !important; color: #075985 !important;
|
|
|
|
|
+ font: 600 9px system-ui, sans-serif !important; line-height: 14px !important; cursor: pointer !important;
|
|
|
|
|
+ opacity: .82 !important; vertical-align: middle !important;
|
|
|
|
|
+ }
|
|
|
|
|
+ .eks-composer-translate {
|
|
|
|
|
+ border: 0 !important; border-radius: 999px !important; padding: 5px 9px !important;
|
|
|
|
|
+ background: #1f6feb !important; color: white !important; font: 600 12px system-ui, sans-serif !important;
|
|
|
|
|
+ cursor: pointer !important; margin: 4px !important; box-shadow: 0 2px 8px rgba(15,23,42,.16) !important;
|
|
|
}
|
|
}
|
|
|
`;
|
|
`;
|
|
|
document.head.appendChild(style);
|
|
document.head.appendChild(style);
|
|
@@ -150,6 +156,44 @@
|
|
|
return candidate ? (candidate.value || candidate.innerText || candidate.textContent || '') : '';
|
|
return candidate ? (candidate.value || candidate.innerText || candidate.textContent || '') : '';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function getDraftElement() {
|
|
|
|
|
+ const active = document.activeElement;
|
|
|
|
|
+ if (active && (active.tagName === 'TEXTAREA' || active.tagName === 'INPUT' || active.isContentEditable)) return active;
|
|
|
|
|
+ return document.querySelector('[contenteditable="true"][role="textbox"], textarea');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function setDraftText(el, text) {
|
|
|
|
|
+ if (!el) return false;
|
|
|
|
|
+ if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {
|
|
|
|
|
+ el.value = text;
|
|
|
|
|
+ el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
|
|
|
+ el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
|
|
|
+ el.focus();
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (el.isContentEditable) {
|
|
|
|
|
+ el.focus();
|
|
|
|
|
+ el.textContent = text;
|
|
|
|
|
+ el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertText', data: text }));
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async function translateAndReplaceDraft() {
|
|
|
|
|
+ const el = getDraftElement();
|
|
|
|
|
+ const text = (el ? (el.value || el.innerText || el.textContent || '') : '').trim();
|
|
|
|
|
+ const panel = document.getElementById(PANEL_ID);
|
|
|
|
|
+ panel.hidden = false;
|
|
|
|
|
+ panelField('source').value = 'pl';
|
|
|
|
|
+ panelField('text').value = text;
|
|
|
|
|
+ if (!panelField('target').value.trim() || panelField('target').value.trim() === 'pl') {
|
|
|
|
|
+ panelField('target').value = localStorage.getItem(LANG_KEY) || 'auto';
|
|
|
|
|
+ }
|
|
|
|
|
+ const translated = await translate('draft');
|
|
|
|
|
+ if (translated) setDraftText(el, translated);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async function translate(mode) {
|
|
async function translate(mode) {
|
|
|
const token = panelField('token').value.trim();
|
|
const token = panelField('token').value.trim();
|
|
|
const targetLanguage = panelField('target').value.trim() || 'pl';
|
|
const targetLanguage = panelField('target').value.trim() || 'pl';
|
|
@@ -181,6 +225,7 @@
|
|
|
if (!res.ok || !json?.ok) throw new Error(json?.message || `HTTP ${res.status}`);
|
|
if (!res.ok || !json?.ok) throw new Error(json?.message || `HTTP ${res.status}`);
|
|
|
state.result = json.translatedText || '';
|
|
state.result = json.translatedText || '';
|
|
|
panelField('result').textContent = state.result + (json.cached ? '\n\n[cached]' : '');
|
|
panelField('result').textContent = state.result + (json.cached ? '\n\n[cached]' : '');
|
|
|
|
|
+ return state.result;
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
showError(err instanceof Error ? err.message : String(err));
|
|
showError(err instanceof Error ? err.message : String(err));
|
|
|
} finally {
|
|
} finally {
|
|
@@ -223,7 +268,8 @@
|
|
|
const btn = document.createElement('button');
|
|
const btn = document.createElement('button');
|
|
|
btn.type = 'button';
|
|
btn.type = 'button';
|
|
|
btn.className = 'eks-inline-translate';
|
|
btn.className = 'eks-inline-translate';
|
|
|
- btn.textContent = 'AI→PL';
|
|
|
|
|
|
|
+ btn.textContent = 'AI';
|
|
|
|
|
+ btn.title = 'Przetłumacz tę wiadomość na polski';
|
|
|
btn.addEventListener('click', (e) => {
|
|
btn.addEventListener('click', (e) => {
|
|
|
e.preventDefault(); e.stopPropagation();
|
|
e.preventDefault(); e.stopPropagation();
|
|
|
const panel = document.getElementById(PANEL_ID);
|
|
const panel = document.getElementById(PANEL_ID);
|
|
@@ -235,11 +281,32 @@
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function addComposerButtons() {
|
|
|
|
|
+ const editors = document.querySelectorAll('[contenteditable="true"][role="textbox"], textarea');
|
|
|
|
|
+ editors.forEach((el) => {
|
|
|
|
|
+ if (el.dataset?.eksComposerBound === '1') return;
|
|
|
|
|
+ if (el.closest(`#${PANEL_ID}`)) return;
|
|
|
|
|
+ if (el.dataset) el.dataset.eksComposerBound = '1';
|
|
|
|
|
+ const btn = document.createElement('button');
|
|
|
|
|
+ btn.type = 'button';
|
|
|
|
|
+ btn.className = 'eks-composer-translate';
|
|
|
|
|
+ btn.textContent = 'AI: PL → język klienta';
|
|
|
|
|
+ btn.title = 'Przetłumacz wpisaną odpowiedź po polsku na język klienta i wstaw do edytora';
|
|
|
|
|
+ btn.addEventListener('click', (e) => {
|
|
|
|
|
+ e.preventDefault(); e.stopPropagation();
|
|
|
|
|
+ void translateAndReplaceDraft();
|
|
|
|
|
+ });
|
|
|
|
|
+ const host = el.parentElement;
|
|
|
|
|
+ if (host) host.appendChild(btn);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function boot() {
|
|
function boot() {
|
|
|
if (!document.body) return setTimeout(boot, 200);
|
|
if (!document.body) return setTimeout(boot, 200);
|
|
|
makeUi();
|
|
makeUi();
|
|
|
addInlineButtons();
|
|
addInlineButtons();
|
|
|
- const obs = new MutationObserver(() => addInlineButtons());
|
|
|
|
|
|
|
+ addComposerButtons();
|
|
|
|
|
+ const obs = new MutationObserver(() => { addInlineButtons(); addComposerButtons(); });
|
|
|
obs.observe(document.body, { childList: true, subtree: true });
|
|
obs.observe(document.body, { childList: true, subtree: true });
|
|
|
}
|
|
}
|
|
|
|
|
|