const groupByDomain = (tabs) => { return tabs.reduce((groups, tab) => { try { const domain = new URL(tab.url).hostname; if (!groups[domain]) groups[domain] = []; groups[domain].push(tab); } catch (_) {} return groups; }, {}); }; const render = async () => { const tabs = await chrome.tabs.query({ currentWindow: true }); const groups = groupByDomain(tabs); const sorted = Object.entries(groups).sort(([a], [b]) => a.localeCompare(b)); const container = document.getElementById('groups'); container.innerHTML = ''; for (const [domain, domainTabs] of sorted) { const section = document.createElement('section'); const header = document.createElement('h2'); header.textContent = `${domain} (${domainTabs.length})`; const closeBtn = document.createElement('button'); closeBtn.textContent = 'close all'; closeBtn.onclick = () => { chrome.tabs.remove(domainTabs.map(t => t.id)).then(render); }; header.appendChild(closeBtn); section.appendChild(header); for (const tab of domainTabs) { const item = document.createElement('div'); item.className = 'tab-item'; item.textContent = tab.title || tab.url; item.title = tab.url; item.onclick = () => chrome.tabs.update(tab.id, { active: true }); section.appendChild(item); } container.appendChild(section); } }; document.addEventListener('DOMContentLoaded', render); chrome.runtime.onMessage.addListener((msg) => { if (msg.type === 'TABS_CHANGED') render(); });