(() => { 'use strict'; if (window.__EKS_CHATWOOT_AI_TOOLS__) return; window.__EKS_CHATWOOT_AI_TOOLS__ = true; const RELAY_ORIGIN = new URL(document.currentScript?.src || 'https://eks-relay.easyklima.com/chatwoot-ai-tools.js').origin; const STORE_KEY = 'eksAgentUiToken'; const LANG_KEY = 'eksAgentUiTargetLanguage'; const PANEL_ID = 'eks-ai-translate-panel'; const BUTTON_ID = 'eks-ai-translate-toggle'; const state = { busy: false, result: '', error: '', }; function t(s) { return s; } function ensureStyles() { if (document.getElementById('eks-ai-translate-styles')) return; const style = document.createElement('style'); style.id = 'eks-ai-translate-styles'; style.textContent = ` #${BUTTON_ID} { position: fixed; right: 18px; bottom: 18px; z-index: 2147483000; border: 0; border-radius: 999px; padding: 10px 14px; background: #1f6feb; color: white; font: 600 13px system-ui, sans-serif; box-shadow: 0 8px 22px rgba(15,23,42,.25); cursor: pointer; } #${PANEL_ID} { position: fixed; right: 18px; bottom: 66px; z-index: 2147483000; width: min(420px, calc(100vw - 36px)); max-height: min(720px, calc(100vh - 92px)); overflow: auto; background: #fff; color: #0f172a; border: 1px solid #d7dee8; border-radius: 14px; box-shadow: 0 18px 48px rgba(15,23,42,.28); padding: 14px; font: 13px system-ui, sans-serif; } #${PANEL_ID}[hidden] { display: none; } #${PANEL_ID} h3 { margin: 0 0 10px; font-size: 15px; } #${PANEL_ID} label { display: block; margin: 10px 0 4px; font-weight: 600; } #${PANEL_ID} input, #${PANEL_ID} textarea, #${PANEL_ID} select { width: 100%; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 8px; padding: 8px; font: 13px system-ui, sans-serif; background: #fff; color: #0f172a; } #${PANEL_ID} textarea { min-height: 88px; resize: vertical; } #${PANEL_ID} .eks-row { display: flex; gap: 8px; align-items: center; } #${PANEL_ID} .eks-row > * { flex: 1; } #${PANEL_ID} button { border: 0; border-radius: 8px; padding: 8px 10px; background: #1f6feb; color: white; font: 600 13px system-ui, sans-serif; cursor: pointer; } #${PANEL_ID} button.secondary { background: #e2e8f0; color: #0f172a; } #${PANEL_ID} button.danger { background: #b91c1c; } #${PANEL_ID} button:disabled { opacity: .55; cursor: wait; } #${PANEL_ID} .eks-muted { color: #64748b; font-size: 12px; } #${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; } .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; } `; document.head.appendChild(style); } function makeUi() { ensureStyles(); if (!document.getElementById(BUTTON_ID)) { const toggle = document.createElement('button'); toggle.id = BUTTON_ID; toggle.type = 'button'; toggle.textContent = 'AI tłumacz'; toggle.addEventListener('click', () => { const panel = document.getElementById(PANEL_ID); panel.hidden = !panel.hidden; if (!panel.hidden) fillSelectedText(); }); document.body.appendChild(toggle); } if (document.getElementById(PANEL_ID)) return; const panel = document.createElement('section'); panel.id = PANEL_ID; panel.hidden = true; panel.innerHTML = `

${t('EKS AI tłumaczenia')}

Token operatora jest zapisany tylko w tej przeglądarce. OpenAI/Flowise sekrety zostają po stronie EKSRelay.
`; document.body.appendChild(panel); const token = panel.querySelector('[data-eks="token"]'); const target = panel.querySelector('[data-eks="target"]'); token.value = localStorage.getItem(STORE_KEY) || ''; target.value = localStorage.getItem(LANG_KEY) || 'pl'; token.addEventListener('change', () => localStorage.setItem(STORE_KEY, token.value.trim())); target.addEventListener('change', () => localStorage.setItem(LANG_KEY, target.value.trim() || 'pl')); panel.querySelector('[data-eks="fill-selected"]').addEventListener('click', fillSelectedText); panel.querySelector('[data-eks="selected"]').addEventListener('click', () => translate('message')); panel.querySelector('[data-eks="draft"]').addEventListener('click', () => translate('draft')); panel.querySelector('[data-eks="copy"]').addEventListener('click', copyResult); panel.querySelector('[data-eks="close"]').addEventListener('click', () => { panel.hidden = true; }); } function panelField(name) { return document.querySelector(`#${PANEL_ID} [data-eks="${name}"]`); } function fillSelectedText() { const selected = String(window.getSelection?.() || '').trim(); if (selected) panelField('text').value = selected; else { const draft = getDraftText(); if (draft) panelField('text').value = draft; } } function getDraftText() { const active = document.activeElement; if (active && (active.tagName === 'TEXTAREA' || active.tagName === 'INPUT')) return active.value || ''; if (active?.isContentEditable) return active.innerText || active.textContent || ''; const candidate = document.querySelector('[contenteditable="true"][role="textbox"], textarea'); return candidate ? (candidate.value || candidate.innerText || candidate.textContent || '') : ''; } async function translate(mode) { const token = panelField('token').value.trim(); const targetLanguage = panelField('target').value.trim() || 'pl'; const sourceLanguageRaw = panelField('source').value.trim(); let text = panelField('text').value.trim(); if (mode === 'draft' && !text) text = getDraftText().trim(); if (!token) return showError('Brak Agent UI token.'); if (!text) return showError('Brak tekstu do tłumaczenia.'); localStorage.setItem(STORE_KEY, token); localStorage.setItem(LANG_KEY, targetLanguage); setBusy(true); showError(''); panelField('result').textContent = ''; try { const endpoint = mode === 'draft' ? '/agent-tools/translate-draft' : '/agent-tools/translate-message'; const payload = { targetLanguage, sourceLanguage: sourceLanguageRaw && sourceLanguageRaw !== 'auto' ? sourceLanguageRaw : undefined, conversationId: detectConversationId(), ...(mode === 'draft' ? { draftText: text } : { sourceText: text, messageId: detectMessageId() }), }; const res = await fetch(`${RELAY_ORIGIN}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: JSON.stringify(payload), }); const json = await res.json().catch(() => null); if (!res.ok || !json?.ok) throw new Error(json?.message || `HTTP ${res.status}`); state.result = json.translatedText || ''; panelField('result').textContent = state.result + (json.cached ? '\n\n[cached]' : ''); } catch (err) { showError(err instanceof Error ? err.message : String(err)); } finally { setBusy(false); } } function detectConversationId() { const m = location.pathname.match(/conversations\/(\d+)/) || location.hash.match(/conversations\/(\d+)/); return m ? Number(m[1]) : undefined; } function detectMessageId() { const el = window.getSelection?.()?.anchorNode?.parentElement?.closest?.('[data-message-id], [data-id]'); return el?.getAttribute('data-message-id') || el?.getAttribute('data-id') || undefined; } async function copyResult() { if (!state.result) return; await navigator.clipboard.writeText(state.result); } function showError(msg) { state.error = msg; const el = panelField('error'); if (el) el.textContent = msg; } function setBusy(busy) { state.busy = busy; document.querySelectorAll(`#${PANEL_ID} button`).forEach((btn) => { btn.disabled = busy; }); } function addInlineButtons() { const candidates = document.querySelectorAll('[data-message-id]:not([data-eks-ai-bound]), .message-bubble:not([data-eks-ai-bound]), [class*="message"]:not([data-eks-ai-bound])'); candidates.forEach((el) => { const text = (el.innerText || '').trim(); if (text.length < 12 || text.length > 5000) return; el.setAttribute('data-eks-ai-bound', '1'); const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'eks-inline-translate'; btn.textContent = 'AI→PL'; btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); const panel = document.getElementById(PANEL_ID); panel.hidden = false; panelField('target').value = localStorage.getItem(LANG_KEY) || 'pl'; panelField('text').value = text; }); el.appendChild(btn); }); } function boot() { if (!document.body) return setTimeout(boot, 200); makeUi(); addInlineButtons(); const obs = new MutationObserver(() => addInlineButtons()); obs.observe(document.body, { childList: true, subtree: true }); } boot(); })();