diff --git a/background_scripts/all_commands.js b/background_scripts/all_commands.js index 607fb73a7..f8579a695 100644 --- a/background_scripts/all_commands.js +++ b/background_scripts/all_commands.js @@ -493,6 +493,30 @@ const allCommands = [ noRepeat: true, }, + { + name: "collapseTabGroup", + desc: "Collapse current tab's group", + group: "tabs", + background: true, + noRepeat: true, + }, + + { + name: "previousTabGroup", + desc: "Switch to previous tab group", + group: "tabs", + background: true, + noRepeat: true, + }, + + { + name: "nextTabGroup", + desc: "Switch to next tab group", + group: "tabs", + background: true, + noRepeat: true, + }, + { name: "removeTab", desc: "Close current tab", diff --git a/background_scripts/commands.js b/background_scripts/commands.js index faaf58497..5c2679fa9 100644 --- a/background_scripts/commands.js +++ b/background_scripts/commands.js @@ -482,6 +482,9 @@ const defaultKeyMappings = { "zi": "zoomIn", "zo": "zoomOut", "z0": "zoomReset", + "zc": "collapseTabGroup", + "zj": "previousTabGroup", + "zk": "nextTabGroup", // Marks "m": "Marks.activateCreateMode", diff --git a/background_scripts/main.js b/background_scripts/main.js index 7d37e6b1b..0a790f1a1 100644 --- a/background_scripts/main.js +++ b/background_scripts/main.js @@ -340,6 +340,23 @@ const BackgroundCommands = { }); }, toggleMuteTab, + async collapseTabGroup({ tab }) { + if (!chrome.tabGroups || tab.groupId == -1) return; + const tabs = await chrome.tabs.query({ currentWindow: true }); + let nextTab = tabs.find((t) => t.index > tab.index && t.groupId != tab.groupId) || + tabs.findLast((t) => t.index < tab.index && t.groupId != tab.groupId); + if (!nextTab && !bgUtils.isFirefox()) { + nextTab = await chrome.tabs.create({}); + } + if (nextTab) await chrome.tabs.update(nextTab.id, { active: true }); + chrome.tabGroups.update(tab.groupId, { collapsed: true }); + }, + previousTabGroup({ tab }) { + return goToTabGroup(tab, -1); + }, + nextTabGroup({ tab }) { + return goToTabGroup(tab, 1); + }, moveTabLeft: moveTab, moveTabRight: moveTab, @@ -464,11 +481,33 @@ async function removeTabsRelative(direction, { count, tab }) { await chrome.tabs.remove(toRemove.map((t) => t.id)); } +// Jump to the next (direction=1) or previous (direction=-1) tab group. +async function goToTabGroup(tab, direction) { + if (!chrome.tabGroups) return; + const tabs = await chrome.tabs.query({ currentWindow: true }); + const inDifferentGroup = (t) => t.groupId != -1 && t.groupId != tab.groupId; + const target = direction > 0 + ? tabs.find((t) => t.index > tab.index && inDifferentGroup(t)) + : tabs.findLast((t) => t.index < tab.index && inDifferentGroup(t)); + if (target) { + await chrome.tabGroups.update(target.groupId, { collapsed: false }); + chrome.tabs.update(target.id, { active: true }); + } +} + // Selects a tab before or after the currently selected tab. // - direction: "next", "previous", "first" or "last". function selectTab(direction, { count, tab }) { - chrome.tabs.query(visibleTabsQueryArgs, function (tabs) { + chrome.tabs.query(visibleTabsQueryArgs, async function (tabs) { if (tabs.length > 1) { + // Skip tabs in collapsed tab groups. + if (chrome.tabGroups) { + const groups = await chrome.tabGroups.query({ windowId: tab.windowId, collapsed: true }); + const collapsedGroupIds = new Set(groups.map((g) => g.id)); + tabs = tabs.filter((t) => t.id === tab.id || !collapsedGroupIds.has(t.groupId)); + if (tabs.length <= 1) return; + } + const toSelect = (() => { switch (direction) { case "next": diff --git a/manifest.json b/manifest.json index 783852cbb..754d7a3f3 100644 --- a/manifest.json +++ b/manifest.json @@ -25,6 +25,7 @@ ], "permissions": [ "tabs", + "tabGroups", "bookmarks", "history", "storage",