Bulk operations on multi-selected tabs, from the tab strip right-click menu.
Select several tabs (Cmd/Ctrl+click, or Shift+click for a range), right-click any tab in the selection, and pick an operation from the TabWright submenu:
| Operation | Effect |
|---|---|
| Sort selected by domain | Gathers the selected tabs at the leftmost selected position and orders them by hostname, ties broken by title. |
| Group selected by domain | Creates a labelled tab group per hostname. Domains with one tab are left alone. |
| Reverse selected order | Gathers the selected tabs at the leftmost selected position in reverse strip order. |
| Close duplicate URLs in selection | Closes tabs whose exact URL already appears further left, including tabs in a group. |
Right-clicking a tab outside the selection acts on that tab alone, matching the rest of Chrome's tab strip menu. Pinned tabs are always excluded.
Sort and Reverse refuse a selection containing tabs that belong to a tab group, and say so in a notification.
This is not caution. Chrome keeps tab groups contiguous in the strip and enforces that during a move, which means a move that crosses a group boundary gets rewritten underneath the extension: the resulting order differs from the one it computed, and a group can lose a member without anything reporting an error. Both were measured on Chrome 151 — see test/EVIDENCE.md.
Group by domain and Close duplicate URLs are allowed on grouped selections, but neither leaves groups untouched. Group by domain reassigns selected tabs into new groups and Chrome repositions them to keep each group contiguous, so a tab's former group can shrink. Dedupe closes the later duplicate whether or not it belongs to a group. Both are consequences of what you asked for rather than the extension losing track of its own plan, which is the distinction sort and reverse fail.
To sort a group's contents, ungroup first.
Chrome 151 or later. The "tab" context for chrome.contextMenus landed
in Chrome 150, but every measurement behind this release was taken on
151.0.7922.138, so 151 is the floor the manifest declares. Given that the
Chrome 150 release note turned out to be wrong about what that context provides
(see below), declaring support for a version nobody has run seemed unwise. If
150 is ever tested, the floor can drop.
Chrome 150's release note says items "receive all selected tabs when multiple are
selected." Observed on 151.0.7922.138, that is menu placement only — the click
carries no selection data. OnClickData contains exactly editable,
menuItemId, and pageUrl, the handler fires once per click rather than once per
selected tab, and the selection still has to be read with
chrome.tabs.query({ highlighted: true }) as it always did.
That is worth knowing before you build on the context expecting new plumbing.
contextMenus— the menu items.tabs— reading tab URLs, which sorting, grouping, and deduping all require. Shown at install as "Read your browsing history."tabGroups— labelling created groups. Shown as "View and manage your tab groups."contextMenus— the menu items. No warning.notifications— surfacing refusals and failures that would otherwise be silent. Shown as "Display notifications."
All four warnings are listed here rather than only the alarming one.
No host permissions, no content scripts, no network access. Nothing is read from page content and nothing leaves the browser.
extension/ what ships -- load this directory unpacked
manifest.json
background.js glue: queries Chrome, executes the plan
src/ops.js pure logic, no chrome.* access
icons/
test/
selftest.js pure layer, runs under plain Node, no dependencies
gluetest.js chrome adapter, against a fake chrome global
EVIDENCE.md measured Chrome behaviour, raw states
SMOKE.md manual procedure and outstanding scenarios
docs/
SCOPE.md
Everything inside extension/ is the extension; everything outside it is
development scaffolding and is excluded from the packaged zip.
npm test # both suites
npm run test:pure # ops.js, 50 checks
npm run test:glue # background.js against a fake chrome, 24 checks
npm run package # zips extension/ for the Chrome Web Store
To load unpacked: chrome://extensions -> Load unpacked -> select the
extension/ directory, not the repository root.
Chrome-dependent behaviour is verified by hand and recorded with raw before/after states in test/EVIDENCE.md; the procedure and the scenarios still outstanding are in test/SMOKE.md. See docs/SCOPE.md for what is and isn't covered.
MIT. See LICENSE.