// @ts-nocheck import { buildTabBlocks, getLargeOperationThrottle, resolveGroupId, runLargeOperation, tabInfo, throwIfJobCancelled, updateJobProgress, yieldForLargeOperation } from '../core'; export async function groupList() { const groups = await chrome.tabGroups.query({}); const all = await chrome.tabs.query({}); return groups.map(g => ({ id: g.id, title: g.title, color: g.color, collapsed: g.collapsed, windowId: g.windowId, tabCount: all.filter(t => t.groupId === g.id).length, })); } export async function groupTabs({ groupId }) { const all = await chrome.tabs.query({}); return all.filter(t => t.groupId === groupId).map(tabInfo); } export async function groupCount() { const groups = await chrome.tabGroups.query({}); return groups.length; } export async function groupQuery({ search }) { const q = search.toLowerCase(); const groups = await chrome.tabGroups.query({}); return groups.filter(g => g.title && g.title.toLowerCase().includes(q)); } export async function groupClose({ groupId, gentleMode, __job } = {}) { return runLargeOperation("group.close", async () => { const tabs = await chrome.tabs.query({}); const groupTabs = tabs.filter(t => t.groupId === groupId); const tabIds = groupTabs.map(t => t.id); const throttle = await getLargeOperationThrottle(tabIds.length, gentleMode); updateJobProgress(__job, { phase: "ungrouping tabs", current: 0, total: tabIds.length }); for (let i = 0; i < tabIds.length; i += throttle.batchSize) { throwIfJobCancelled(__job); await chrome.tabs.ungroup(tabIds.slice(i, i + throttle.batchSize)); updateJobProgress(__job, { phase: "ungrouping tabs", current: Math.min(i + throttle.batchSize, tabIds.length), total: tabIds.length }); await yieldForLargeOperation(i + throttle.batchSize, throttle.batchSize, throttle.pauseMs); } return { groupId, gentle: throttle.gentle, audible: throttle.audible }; }); } export async function groupOpen({ name }) { const tab = await chrome.tabs.create({ active: true }); const groupId = await chrome.tabs.group({ tabIds: [tab.id] }); await chrome.tabGroups.update(groupId, { title: name }); return { id: groupId, name }; } export async function groupAddTab({ group, url }) { const groupId = await resolveGroupId(group); const existingTabs = await chrome.tabs.query({ groupId }); const tab = await chrome.tabs.create({ url: url || "chrome://newtab/", active: true }); await chrome.tabs.group({ tabIds: [tab.id], groupId }); // If a URL was provided, close any blank placeholder tabs left from group creation if (url) { const placeholders = existingTabs.filter(t => t.url === "chrome://newtab/" || t.url === "about:blank" || t.pendingUrl === "chrome://newtab/" ); if (placeholders.length) await chrome.tabs.remove(placeholders.map(t => t.id)); } return { tabId: tab.id, groupId }; } export async function groupMove({ group, forward, backward }) { const groupId = await resolveGroupId(group); const groupInfo = await chrome.tabGroups.get(groupId); const allTabs = await chrome.tabs.query({ windowId: groupInfo.windowId }); allTabs.sort((a, b) => a.index - b.index); const blocks = buildTabBlocks(allTabs); const currentIdx = blocks.findIndex(block => block.groupId === groupId); if (currentIdx === -1) throw new Error(`No tabs found in group '${group}'`); const currentBlock = blocks[currentIdx]; const currentLength = currentBlock.tabIds.length; if (forward) { const nextBlock = blocks[currentIdx + 1]; if (!nextBlock) return { groupId, moved: false }; const targetIndex = nextBlock.groupId === null ? currentBlock.startIndex + 1 : nextBlock.endIndex - currentLength + 1; await chrome.tabGroups.move(groupId, { index: targetIndex }); } else if (backward) { const previousBlock = blocks[currentIdx - 1]; if (!previousBlock) return { groupId, moved: false }; const targetIndex = previousBlock.groupId === null ? currentBlock.startIndex - 1 : previousBlock.startIndex; await chrome.tabGroups.move(groupId, { index: targetIndex }); } return { groupId, moved: true }; } // ── Windows ───────────────────────────────────────────────────────────────────