545abeb515
- Add throttled large-operation handling for tab, group, and session commands. - Introduce performance profiles, audible-tab aware gentle mode, and job progress tracking. - Support background session restores with status/cancel commands and lazy placeholders. - Expose new perf and extension CLI groups plus matching Python SDK methods. - Preserve pinned tabs during session snapshots and debounce auto-save updates. - Bump browser-cli and extension versions to 0.10.0 and add pytest-cov to dev deps. - Add coverage for performance controls, background jobs, lazy restores, and tab metadata.
106 lines
4.3 KiB
TypeScript
106 lines
4.3 KiB
TypeScript
// @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 ───────────────────────────────────────────────────────────────────
|