From 6dac81dd27de4034c4c3c36c3b64335f59e91d63 Mon Sep 17 00:00:00 2001 From: NAME Date: Fri, 15 May 2026 14:06:58 +0000 Subject: [PATCH] U --- chrom-ext/background.js | 45 ++++--- chrom-ext/content.js | 280 ++++++++++++++++++++++------------------ chrom-ext/manifest.json | 17 +-- 3 files changed, 193 insertions(+), 149 deletions(-) diff --git a/chrom-ext/background.js b/chrom-ext/background.js index 9f2f7dd..d76bb30 100644 --- a/chrom-ext/background.js +++ b/chrom-ext/background.js @@ -1,10 +1,6 @@ console.log('[XAI Background] ✅ Service worker started'); -// ⚠️ SỬA ENDPOINT & KEY -const API_URL = 'https://punch-scientific-electrical-antibodies.trycloudflare.com/content-writer/comment'; -const API_KEY = 'YOUR_API_KEY_HERE'; - -// ===== MENU ===== +// ===== TẠO MENU ===== chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ id: 'writeComment', @@ -23,38 +19,55 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { } }); -// ===== HANDLE GENERATE ===== -chrome.runtime.onMessage.addListener((request, sender) => { +// ===== NHẬN YÊU CẦU TỪ CONTENT ===== +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'GENERATE_COMMENT') { - callYourAPI(request.data, sender.tab.id); - return true; + // Gọi API async riêng, không await ở đây + handleGenerate(request.data, sender.tab.id); + + // Đóng kênh ngay để tránh lỗi "channel closed" + sendResponse({ received: true }); } }); -async function callYourAPI({ text, lang, tone, angle }, tabId) { - const payload = { originalPost: text, language:lang, tone, angle }; - console.log('[XAI Background] ⏳ Gọi API:', API_URL); +// ===== GỌI API: ĐỌC CONFIG TỪ STORAGE ===== +async function handleGenerate({ text, lang, tone, angle }, tabId) { + console.log('[XAI Background] ⏳ Đọc config từ storage...'); + + const config = await chrome.storage.local.get(['apiUrl', 'apiKey']); + + if (!config.apiUrl || !config.apiKey) { + console.error('[XAI Background] ❌ Thiếu API config'); + await chrome.tabs.sendMessage(tabId, { + action: 'SHOW_ERROR', + error: '⚠️ Chưa cấu hình API.\n\n👉 Bấm tab ⚙️ Config để nhập URL và Key.' + }); + return; + } + + const payload = { originalPost: text, language: lang, tone, angle }; console.log('[XAI Background] 📤 Payload:', JSON.stringify(payload, null, 2)); + console.log('[XAI Background] 🌐 URL:', config.apiUrl); try { - const res = await fetch(API_URL, { + const res = await fetch(config.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${API_KEY}` + 'Authorization': `Bearer ${config.apiKey}` }, body: JSON.stringify(payload) }); const data = await res.json(); - console.log('[XAI Background] 📥 Status:', res.status, '| JSON:', data); + console.log('[XAI Background] 📥 Response:', res.status, data); if (!res.ok) throw new Error(`HTTP ${res.status}`); const comment = data.comment || data.text || JSON.stringify(data); await chrome.tabs.sendMessage(tabId, { action: 'SHOW_RESULT', comment }); } catch (err) { - console.error('[XAI Background] ❌ Lỗi:', err.message); + console.error('[XAI Background] ❌ Lỗi fetch:', err.message); await chrome.tabs.sendMessage(tabId, { action: 'SHOW_ERROR', error: err.message }); } } \ No newline at end of file diff --git a/chrom-ext/content.js b/chrom-ext/content.js index 94c7a86..5af031c 100644 --- a/chrom-ext/content.js +++ b/chrom-ext/content.js @@ -6,67 +6,63 @@ // ===== DATA ===== const LANGS = [ + { value: 'ja', label: 'Nhật' }, { value: 'vi', label: 'Việt' }, { value: 'en', label: 'English' }, - { value: 'ja', label: 'Nhat' }, - { value: 'ko', label: 'Han Quoc' }, + { value: 'ko', label: 'Hàn' }, { value: 'cn', label: 'Trung' } ]; - // Tone base (tất cả ngôn ngữ đều có) const TONE_BASE = [ - { value: 'professional', text: 'chuyên nghiệp, rõ ràng, đáng tin cậy' }, - { value: 'casual', text: 'Giản dị, thân thiện' }, - { value: 'hype', text: 'Hype — Hào hứng, tràn đầy năng lượng' }, - { value: 'urgent', text: 'urgent' }, - { value: 'humorous', text: 'Dí dỏm, hài hước' }, - { value: 'informative', text: 'Thông tin, chính xác' }, - { value: 'empathetic', text: 'empathetic — Đồng cảm, thấu hiểu cảm xúc, biết trân trọng người khác.' }, - { value: 'provocative', text: 'provocative — Gợi mở suy nghĩ, hơi gây tranh cãi, thách thức các giả định.' }, - { value: 'authoritative', text: 'authoritative — giọng tự tin, uy quyền, chuyên nghiệp' }, - { value: 'spicy', text: 'spicy — Tự tin, hơi đối đầu. KHÔNG giận dữ — chỉ thẳng.' } + { value: 'PROFESSIONAL', text: 'chuyên nghiệp, rõ ràng, đáng tin cậy' }, + { value: 'CASUAL', text: 'Giản dị, thân thiện' }, + { value: 'HYPE', text: 'Hype — Hào hứng, tràn đầy năng lượng' }, + { value: 'URGENT', text: 'urgent' }, + { value: 'HUMOROUS', text: 'Dí dỏm, hài hước' }, + { value: 'INFORMATIVE', text: 'Thông tin, chính xác' }, + { value: 'EMPATHETIC', text: 'empathetic — Đồng cảm, thấu hiểu cảm xúc' }, + { value: 'PROVOCATIVE', text: 'provocative — Gợi mở suy nghĩ, thách thức giả định' }, + { value: 'AUTHORITATIVE', text: 'authoritative — Tự tin, uy quyền' }, + { value: 'SPICY', text: 'spicy — Tự tin, hơi đối đầu, chỉ thẳng' } ]; - // Tone chỉ dành cho ja const TONE_JA_ONLY = [ - { value: 'aggressive', text: 'aggressive — Cục súc, attack ideas mạnh' }, - { value: 'profane', text: 'profane — Nói tục thoải mái, raw' }, - { value: 'inflammatory', text: 'inflammatory — Kích động cao, controversial takes' }, - { value: 'savage', text: 'savage — Chửi tục OK. Sass tối đa. Vui + ác + thông minh' } + { value: 'AGGRESSIVE', text: 'aggressive — Cục súc, attack ideas mạnh' }, + { value: 'PROFANE', text: 'profane — Nói tục thoải mái, raw' }, + { value: 'INFLAMMATORY', text: 'inflammatory — Kích động cao' }, + { value: 'SAVAGE', text: 'savage — Chửi tục OK, sass tối đa' } ]; const ANGLE_DEFAULT = [ - { value: 'agree', text: 'Đồng ý' }, - { value: 'challenge', text: 'Không đồng ý' }, - { value: 'add_info', text: 'Thêm thông tin liên quan hữu ích' }, - { value: 'funny', text: 'Hóm hỉnh, hài hước nhẹ nhàng, không gây khó chịu' }, - { value: 'question', text: 'Đặt một câu hỏi tiếp theo thông minh' }, - { value: 'relate', text: 'Chia sẻ một trải nghiệm hoặc cảm xúc cá nhân tương tự như bài đăng gốc.' }, - { value: 'devil_advocate', text: 'Hãy đóng vai trò người phản biện. Trình bày quan điểm trái chiều một cách công bằng mà không tỏ ra thù địch.' }, - { value: 'expand', text: 'expand — Chọn 1 điểm phân tích sâu hơn với nhiều sắc thái khác nhau.' }, - { value: 'validate', text: 'validate — Khẳng định luận điểm = bằng chứng hoặc sự đồng tình mạnh mẽ, tăng cường độ tin cậy.' }, - { value: 'cta', text: 'cta — Kết thúc bằng lời kêu gọi hành động nhẹ nhàng' } + { value: 'AGREE', text: 'Đồng ý' }, + { value: 'CHALLENGE', text: 'Không đồng ý' }, + { value: 'ADD_INFO', text: 'Thêm thông tin liên quan hữu ích' }, + { value: 'FUNNY', text: 'Hóm hỉnh, hài hước nhẹ nhàng' }, + { value: 'QUESTION', text: 'Đặt câu hỏi tiếp theo thông minh' }, + { value: 'RELATE', text: 'Chia sẻ trải nghiệm cá nhân tương tự' }, + { value: 'DEVIL_ADVOCATE', text: 'Phản biện công bằng, không thù địch' }, + { value: 'EXPAND', text: 'Phân tích sâu 1 điểm' }, + { value: 'VALIDATE', text: 'Khẳng định bằng chứng mạnh mẽ' }, + { value: 'CTA', text: 'Kêu gọi hành động nhẹ nhàng' } ]; const ANGLE_EMPATHY = [ - { value: 'wish_recovery', text: 'Chúc hồi phục' }, - { value: 'tribute', text: 'Tưởng nhớ / RIP' }, - { value: 'solidarity', text: 'Đồng lòng / Đứng cùng' }, - { value: 'personal_support',text: 'Hỗ trợ cá nhân' }, - { value: 'shared_grief', text: 'Cùng nỗi buồn' } + { value: 'WISH_RECOVERY', text: 'Chúc hồi phục' }, + { value: 'TRIBUTE', text: 'Tưởng nhớ / RIP' }, + { value: 'SOLIDARITY', text: 'Đồng lòng / Đứng cùng' }, + { value: 'PERSONAL_SUPPORT', text: 'Hỗ trợ cá nhân' }, + { value: 'SHARED_GRIEF', text: 'Cùng nỗi buồn' } ]; function getTones(lang) { if (lang === 'ja') return [...TONE_BASE, ...TONE_JA_ONLY]; return [...TONE_BASE]; } - function getAngles(tone) { - if (tone === 'empathetic') return [...ANGLE_EMPATHY]; - return [...ANGLE_DEFAULT]; + return tone === 'EMPATHETIC' ? [...ANGLE_EMPATHY] : [...ANGLE_DEFAULT]; } - // ===== A. BẮT CHUỘT PHẢI ===== + // ===== DOM ===== document.addEventListener('contextmenu', (e) => { const article = e.target.closest('article'); if (!article) { lastTweetText = ''; return; } @@ -79,7 +75,6 @@ : article.innerText.trim().slice(0, 600); }); - // ===== B. LẮNG NGHE BACKGROUND ===== chrome.runtime.onMessage.addListener((req, sender, sendResponse) => { if (req.action === 'OPEN_FORM') { if (!lastTweetText) { @@ -93,7 +88,7 @@ if (req.action === 'SHOW_ERROR') { showError(req.error); return true; } }); - // ===== C. SIDEBAR ===== + // ===== SIDEBAR ===== function openSidebar(tweetText) { removeSidebar(); @@ -109,55 +104,40 @@ const shadow = host.attachShadow({ mode: 'open' }); const style = document.createElement('style'); style.textContent = ` - .fab { - position: fixed; right: 24px; bottom: 24px; - width: 56px; height: 56px; border-radius: 50%; - background: #1d9bf0; color: #fff; font-size: 24px; - display: flex; align-items: center; justify-content: center; - cursor: pointer; box-shadow: 0 4px 16px rgba(0,0,0,0.25); - border: none; pointer-events: auto; z-index: 1; - transition: transform .15s ease; user-select: none; - } + .fab { position: fixed; right: 24px; bottom: 24px; width: 56px; height: 56px; border-radius: 50%; + background: #1d9bf0; color: #fff; font-size: 24px; display: flex; align-items: center; justify-content: center; + cursor: pointer; box-shadow: 0 4px 16px rgba(0,0,0,0.25); border: none; pointer-events: auto; z-index: 1; + transition: transform .15s ease; user-select: none; } .fab:hover { transform: scale(1.08); } - .drawer { - position: fixed; right: 0; top: 0; - width: 400px; max-width: 100vw; height: 100vh; - background: #fff; color: #0f1419; - box-shadow: -5px 0 25px rgba(0,0,0,0.15); - transform: translateX(100%); - transition: transform .25s ease; - display: flex; flex-direction: column; - pointer-events: auto; z-index: 2; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - } + .drawer { position: fixed; right: 0; top: 0; width: 400px; max-width: 100vw; height: 100vh; background: #fff; color: #0f1419; + box-shadow: -5px 0 25px rgba(0,0,0,0.15); transform: translateX(100%); transition: transform .25s ease; + display: flex; flex-direction: column; pointer-events: auto; z-index: 2; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } .drawer.open { transform: translateX(0); } - .header { - padding: 14px 18px; border-bottom: 1px solid #eff3f4; - display: flex; justify-content: space-between; align-items: center; - font-weight: 700; font-size: 16px; - } + .header { padding: 14px 18px; border-bottom: 1px solid #eff3f4; display: flex; justify-content: space-between; align-items: center; + font-weight: 700; font-size: 16px; } .btn-x { background: none; border: none; font-size: 20px; cursor: pointer; color: #536471; padding: 4px; line-height: 1; } - .body { padding: 16px 18px; flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; } - .tweet-box { - background: #f7f9f9; border: 1px solid #cfd9de; border-radius: 10px; - padding: 10px; font-size: 13px; line-height: 1.4; - max-height: 150px; overflow-y: auto; color: #333; - white-space: pre-wrap; word-break: break-word; - } + .tabs { display: flex; border-bottom: 1px solid #eff3f4; } + .tab-btn { flex: 1; padding: 10px; border: none; background: none; cursor: pointer; font-size: 14px; color: #536471; + border-bottom: 2px solid transparent; font-weight: 600; } + .tab-btn.active { color: #1d9bf0; border-bottom-color: #1d9bf0; } + .tab-content { padding: 16px 18px; flex: 1; overflow-y: auto; display: none; flex-direction: column; gap: 10px; } + .tab-content.active { display: flex; } + .tweet-box { background: #f7f9f9; border: 1px solid #cfd9de; border-radius: 10px; padding: 10px; font-size: 13px; + line-height: 1.4; max-height: 150px; overflow-y: auto; color: #333; white-space: pre-wrap; word-break: break-word; } label { font-size: 12px; font-weight: 700; color: #536471; text-transform: uppercase; letter-spacing: .3px; margin-top: 4px; } - select { width: 100%; padding: 9px 10px; border-radius: 8px; border: 1px solid #cfd9de; font-size: 14px; background: #fff; } - .hint-text { font-size: 12px; color: #888; line-height: 1.4; margin-top: 2px; font-style: italic; } - button.primary { - width: 100%; padding: 10px; border-radius: 9999px; border: none; - background: #1d9bf0; color: #fff; font-weight: 700; font-size: 15px; - cursor: pointer; margin-top: 6px; - } + select, input[type="text"], input[type="password"] { width: 100%; padding: 9px 10px; border-radius: 8px; border: 1px solid #cfd9de; + font-size: 14px; background: #fff; box-sizing: border-box; } + .hint-text { font-size: 12px; color: #888; line-height: 1.4; font-style: italic; } + button.primary { width: 100%; padding: 10px; border-radius: 9999px; border: none; background: #1d9bf0; color: #fff; + font-weight: 700; font-size: 15px; cursor: pointer; margin-top: 6px; } button.primary:hover { background: #1a8cd8; } button.primary:disabled { background: #8ecdf7; cursor: default; } .status { display: none; padding: 12px; border-radius: 10px; font-size: 14px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; } - .status.ok { background: #e8f6fe; border: 1px solid #b4dffc; color: #0f1419; } + .status.ok { background: #e8f6fe; border: 1px solid #b4dffc; } .status.err { background: #ffeaea; border: 1px solid #ffc5c5; color: #b00; } - .copy-hint { font-size: 12px; color: #536471; text-align: center; margin-top: 4px; display: none; } + .copy-hint { font-size: 12px; color: #536471; text-align: center; display: none; } + .badge { display: inline-block; padding: 2px 8px; border-radius: 999px; background: #ffad1f; color: #fff; font-size: 11px; font-weight: 700; } `; const fab = document.createElement('button'); @@ -167,36 +147,76 @@ drawer.className = 'drawer'; drawer.innerHTML = `
✍️ AI Comment
-
+
+ + +
+ + +
${escapeHtml(tweetText)}
- + - +
- +
-
📋 Copy kết quả và dán vào ô reply nhé!
+
📋 Copy kết quả và dán vào ô reply!
+
+ + +
+
+ Nhập API của bạn. Dữ liệu được lưu trên trình duyệt này. +
+ + + + + + + +
+ + +
+ +
`; + shadow.appendChild(style); shadow.appendChild(fab); shadow.appendChild(drawer); + // Toggle drawer const toggleDrawer = () => drawer.classList.toggle('open'); fab.addEventListener('click', toggleDrawer); drawer.querySelector('.btn-x').addEventListener('click', () => drawer.classList.remove('open')); requestAnimationFrame(() => drawer.classList.add('open')); - // Refs + // Tab switching + const tabBtns = drawer.querySelectorAll('.tab-btn'); + const tabContents = drawer.querySelectorAll('.tab-content'); + tabBtns.forEach(btn => { + btn.addEventListener('click', () => { + tabBtns.forEach(b => b.classList.remove('active')); + tabContents.forEach(c => c.classList.remove('active')); + btn.classList.add('active'); + drawer.querySelector(`#tab-${btn.dataset.tab}`).classList.add('active'); + }); + }); + + // ===== COMMENT TAB LOGIC ===== const langSel = drawer.querySelector('#ai-lang'); const toneSel = drawer.querySelector('#ai-tone'); const toneHint = drawer.querySelector('#ai-tone-hint'); @@ -206,38 +226,26 @@ const statusEl = drawer.querySelector('#ai-status'); const copyHint = drawer.querySelector('#ai-hint'); - function buildLangOptions() { - return LANGS.map(l => ``).join(''); - } - function populateSelect(sel, items, selectedValue) { - sel.innerHTML = items.map(it => - `` - ).join(''); - if (selectedValue && items.find(i => i.value === selectedValue)) { - sel.value = selectedValue; - } + sel.innerHTML = items.map(it => ``).join(''); + if (selectedValue && items.find(i => i.value === selectedValue)) sel.value = selectedValue; } - function updateTone(lang, keepValueIfValid) { const items = getTones(lang); const old = toneSel.value; populateSelect(toneSel, items, keepValueIfValid && items.find(i => i.value === old) ? old : null); onToneChange(); } - function onToneChange() { const lang = langSel.value; const tone = toneSel.value; const tItem = getTones(lang).find(i => i.value === tone); toneHint.textContent = tItem ? tItem.text : ''; - const oldAngle = angleSel.value; const aItems = getAngles(tone); populateSelect(angleSel, aItems, aItems.find(i => i.value === oldAngle) ? oldAngle : null); onAngleChange(); } - function onAngleChange() { const tone = toneSel.value; const angle = angleSel.value; @@ -245,44 +253,75 @@ angleHint.textContent = aItem ? aItem.text : ''; } - // Events - langSel.addEventListener('change', () => { - // Đổi lang: rebuild tone, nếu tone cũ không tồn tại trong list mới thì reset - updateTone(langSel.value, true); - }); - + langSel.addEventListener('change', () => updateTone(langSel.value, true)); toneSel.addEventListener('change', onToneChange); angleSel.addEventListener('change', onAngleChange); - - // Init updateTone(langSel.value, false); - // Generate runBtn.addEventListener('click', () => { const lang = langSel.value; const tone = toneSel.value; const angle = angleSel.value; - runBtn.disabled = true; - statusEl.style.display = 'block'; - statusEl.className = 'status ok'; - statusEl.textContent = '⏳ Đang gọi API...'; - copyHint.style.display = 'none'; - + statusEl.style.display = 'block'; statusEl.className = 'status ok'; statusEl.textContent = '⏳ Đang gọi API...'; copyHint.style.display = 'none'; chrome.runtime.sendMessage( { action: 'GENERATE_COMMENT', data: { text: tweetText, lang, tone, angle } }, () => { if (chrome.runtime.lastError) { statusEl.className = 'status err'; - statusEl.textContent = 'Lỗi kết nối background: ' + chrome.runtime.lastError.message; + statusEl.textContent = 'Lỗi kết nối: ' + chrome.runtime.lastError.message; runBtn.disabled = false; } } ); }); + + // ===== CONFIG TAB LOGIC ===== + const urlIn = drawer.querySelector('#cfg-url'); + const keyIn = drawer.querySelector('#cfg-key'); + const saveBtn = drawer.querySelector('#cfg-save'); + const testBtn = drawer.querySelector('#cfg-test'); + const cfgStatus= drawer.querySelector('#cfg-status'); + const cfgBadge = drawer.querySelector('#cfg-missing'); + + function showCfgStatus(msg, isErr) { + cfgStatus.style.display = 'block'; + cfgStatus.className = 'status ' + (isErr ? 'err' : 'ok'); + cfgStatus.textContent = msg; + } + + // Đọc config hiện tại + chrome.storage.local.get(['apiUrl', 'apiKey'], (cfg) => { + if (cfg.apiUrl) urlIn.value = cfg.apiUrl; + if (cfg.apiKey) keyIn.value = cfg.apiKey; + cfgBadge.style.display = (!cfg.apiUrl || !cfg.apiKey) ? 'inline-block' : 'none'; + }); + + saveBtn.addEventListener('click', () => { + const url = urlIn.value.trim(); + const key = keyIn.value.trim(); + if (!url || !key) { + showCfgStatus('❌ URL và Key không được để trống!', true); + return; + } + try { new URL(url); } catch { + showCfgStatus('❌ URL không hợp lệ (cần có https://)', true); + return; + } + chrome.storage.local.set({ apiUrl: url, apiKey: key }, () => { + showCfgStatus('✅ Đã lưu! Bạn có thể đóng tab này và bấm Tạo Comment.', false); + cfgBadge.style.display = 'none'; + }); + }); + + testBtn.addEventListener('click', () => { + chrome.storage.local.get(['apiUrl', 'apiKey'], (cfg) => { + showCfgStatus(`URL: ${cfg.apiUrl || '(trống)'}\nKey: ${cfg.apiKey ? cfg.apiKey.slice(0,8)+'...' : '(trống)'}`, !cfg.apiUrl || !cfg.apiKey); + }); + }); } - // ===== D. RESULT / ERROR ===== + // ===== RESULT / ERROR ===== function showResult(comment) { if (!sidebarHost) return; const s = sidebarHost.shadowRoot; @@ -290,29 +329,24 @@ const copyHint = s.querySelector('#ai-hint'); const runBtn = s.querySelector('#ai-run'); const drawer = s.querySelector('.drawer'); - if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'status ok'; statusEl.textContent = comment; } if (copyHint) copyHint.style.display = 'block'; if (runBtn) runBtn.disabled = false; if (drawer) drawer.classList.add('open'); } - function showError(msg) { if (!sidebarHost) return; const s = sidebarHost.shadowRoot; const statusEl = s.querySelector('#ai-status'); const runBtn = s.querySelector('#ai-run'); const drawer = s.querySelector('.drawer'); - - if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'status err'; statusEl.textContent = 'Lỗi: ' + msg; } + if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'status err'; statusEl.textContent = msg; } if (runBtn) runBtn.disabled = false; if (drawer) drawer.classList.add('open'); } - function removeSidebar() { if (sidebarHost) { sidebarHost.remove(); sidebarHost = null; } } - function escapeHtml(text) { const d = document.createElement('div'); d.textContent = text; return d.innerHTML; } diff --git a/chrom-ext/manifest.json b/chrom-ext/manifest.json index 14fcb30..4ea958b 100644 --- a/chrom-ext/manifest.json +++ b/chrom-ext/manifest.json @@ -1,16 +1,14 @@ { "manifest_version": 3, "name": "X Comment AI", - "version": "1.0.0", - "description": "Tạo comment cho X.com bằng AI của bạn", + "version": "1.2.0", + "description": "AI Comment cho X.com — có cấu hình", "permissions": [ "contextMenus", "storage" ], "host_permissions": [ - "https://x.com/*", - "https://twitter.com/*", - "https://api.yoursite.com/*" + "https://x.com/*" ], "background": { "service_worker": "background.js" @@ -18,8 +16,7 @@ "content_scripts": [ { "matches": [ - "https://x.com/*", - "https://twitter.com/*" + "https://x.com/*" ], "js": [ "content.js" @@ -28,8 +25,8 @@ } ], "icons": { - "16": "icons/icon.webp", - "48": "icons/icon.webp", - "128": "icons/icon.webp" + "16": "icons/icon.png", + "48": "icons/icon.png", + "128": "icons/icon.png" } } \ No newline at end of file