(function () { 'use strict'; var API_DEFAULT = 'https://chatbox.npgia-himlam.workers.dev/'; var ID_PREFIX = 'avchat'; var MODELS = { nivip: 'nivip', zerta: 'zerta' }; var MODEL_LABELS = { nivip: 'Nivip', zerta: 'Zerta' }; function baseUrl() { try { var v = localStorage.getItem('anivietdb-chat-api'); if (v) return v; } catch (e) {} return API_DEFAULT; } function injectCss() { if (document.getElementById(ID_PREFIX + '-style')) return; var st = document.createElement('style'); st.id = ID_PREFIX + '-style'; st.textContent = [ '.' + ID_PREFIX + '-fab {', ' position: fixed; right: 16px; bottom: 16px; width: 56px; height: 56px;', ' border-radius: 50%; border: none; cursor: pointer; font-size: 24px; z-index: 9998;', ' background: linear-gradient(135deg, #e84393, #a29bfe); color: #fff;', ' box-shadow: 0 8px 24px rgba(232,67,147,.5); display: flex; align-items: center; justify-content: center;', ' transition: transform .2s;', '}', '.' + ID_PREFIX + '-fab:hover { transform: scale(1.08); }', '.' + ID_PREFIX + '-box {', ' position: fixed; right: 16px; bottom: 16px; width: 340px; max-width: calc(100vw - 32px);', ' background: #14141f; color: #fff; border-radius: 12px; overflow: hidden;', ' box-shadow: 0 8px 30px rgba(0,0,0,.5); z-index: 9999;', ' font-family: Roboto, Arial, sans-serif; display: none;', '}', '.' + ID_PREFIX + '-head { background: #1d1d2e; padding: 12px 16px; border-bottom: 1px solid #333; display: flex; justify-content: space-between; align-items: center; }', '.' + ID_PREFIX + '-head h3 { margin: 0; font-size: 15px; color: #fff; }', '.' + ID_PREFIX + '-model { background: #14141f; color: #fff; border: 1px solid #444; border-radius: 6px; font-size: 12.5px; padding: 4px 6px; outline: none; cursor: pointer; }', '.' + ID_PREFIX + '-close { background: none; border: none; color: #aaa; font-size: 15px; cursor: pointer; padding: 0 4px; }', '.' + ID_PREFIX + '-close:hover { color: #fff; }', '.' + ID_PREFIX + '-msgs { padding: 14px; max-height: 340px; overflow-y: auto; font-size: 14px; line-height: 1.5; background: #fff; color: #222; }', '.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-user { text-align: right; margin: 6px 0; }', '.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-user::before { content: "Bạn: "; color: #007aff; font-weight: 700; }', '.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-ai { margin: 6px 0; white-space: pre-wrap; word-break: break-word; }', '.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-ai::before { content: "AI: "; color: #e84393; font-weight: 700; }', '.' + ID_PREFIX + '-msgs .' + ID_PREFIX + '-err { margin: 6px 0; color: #c0392b; white-space: pre-wrap; word-break: break-word; }', '.' + ID_PREFIX + '-input { display: flex; border-top: 1px solid #333; background: #1d1d2e; }', '.' + ID_PREFIX + '-input input { flex: 1; padding: 10px 12px; border: none; outline: none; background: #1d1d2e; color: #fff; font-size: 14px; }', '.' + ID_PREFIX + '-input button { background: #e84393; color: #fff; border: none; padding: 0 16px; cursor: pointer; font-size: 14px; }', '.' + ID_PREFIX + '-input button:disabled { opacity: .5; cursor: wait; }', '.' + ID_PREFIX + '-input button:hover:not(:disabled) { background: #d63384; }' ].join('\n'); document.head.appendChild(st); } function el(tag, className, html) { var n = document.createElement(tag); if (className) n.className = className; if (html !== undefined) n.innerHTML = html; return n; } function escapeHtml(s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } function build() { injectCss(); var fab = el('button', ID_PREFIX + '-fab', '💬'); fab.title = 'Chat với trợ lý gợi ý anime/manga'; var box = el('div', ID_PREFIX + '-box'); box.innerHTML = '
' + '

Chatbox ANIVIETDB

' + '' + '' + '
'; var msgs = el('div', ID_PREFIX + '-msgs'); msgs.innerHTML = '
Chào bạn! Mình là trợ lý gợi ý anime, manga và light novel của ANIVIETDB. Bạn muốn xem gì hôm nay?
'; box.appendChild(msgs); var inputRow = el('div', ID_PREFIX + '-input'); var inp = el('input'); inp.type = 'text'; inp.placeholder = 'Nhập tin nhắn...'; inp.autocomplete = 'off'; var sendBtn = el('button', null, 'Gửi'); inputRow.appendChild(inp); inputRow.appendChild(sendBtn); box.appendChild(inputRow); document.body.appendChild(box); document.body.appendChild(fab); var closeBtn = box.querySelector('#' + ID_PREFIX + '-close'); var modelSel = box.querySelector('#' + ID_PREFIX + '-model'); function currentModel() { var saved = null; try { saved = localStorage.getItem('anivietdb-chat-model'); } catch (e) {} return MODELS[saved] ? saved : 'nivip'; } modelSel.value = currentModel(); modelSel.addEventListener('change', function () { try { localStorage.setItem('anivietdb-chat-model', modelSel.value); } catch (e) {} }); function toggle(show) { box.style.display = show ? 'block' : 'none'; fab.style.display = show ? 'none' : 'block'; if (show) inp.focus(); } function send() { var msg = inp.value.trim(); if (!msg) return; inp.value = ''; msgs.innerHTML += '
' + escapeHtml(msg) + '
'; msgs.scrollTop = msgs.scrollHeight; var p = el('div', ID_PREFIX + '-ai', '...'); msgs.appendChild(p); msgs.scrollTop = msgs.scrollHeight; sendBtn.disabled = true; inp.disabled = true; fetch(baseUrl().replace(/\/+$/, '') + '/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: msg, model: modelSel.value }) }).then(function (r) { return r.json().catch(function () { return {}; }).then(function (d) { if (!r.ok) { var e = new Error((d && d.error) || ('HTTP ' + r.status)); e.status = r.status; throw e; } return d; }); }).then(function (d) { p.className = ID_PREFIX + '-ai'; p.textContent = d.reply || '...'; msgs.scrollTop = msgs.scrollHeight; }).catch(function (e) { p.className = ID_PREFIX + '-err'; p.textContent = 'Lỗi: ' + (e.message || 'không rõ'); msgs.scrollTop = msgs.scrollHeight; }).finally(function () { sendBtn.disabled = false; inp.disabled = false; inp.focus(); }); } fab.addEventListener('click', function () { toggle(true); }); closeBtn.addEventListener('click', function () { toggle(false); }); sendBtn.addEventListener('click', send); inp.addEventListener('keydown', function (e) { if (e.key === 'Enter') send(); }); } function init() { if (document.getElementById(ID_PREFIX + '-fab')) return; build(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();