{"id":50410929,"url":"https://github.com/knu/tabsquad","last_synced_at":"2026-05-31T03:30:57.549Z","repository":{"id":357661852,"uuid":"1235796458","full_name":"knu/tabsquad","owner":"knu","description":"Keep tab groups organized: route links spawned from a group elsewhere, and snapshot/restore tab groups.","archived":false,"fork":false,"pushed_at":"2026-05-28T16:20:16.000Z","size":481,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T18:10:25.978Z","etag":null,"topics":["browser-extension","chrome-extension","chromium","edge-extension","productivity","tab-groups","tab-management","wxt"],"latest_commit_sha":null,"homepage":"https://chromewebstore.google.com/detail/tabsquad/aljgcpeillkoagglomiijlignhbeagai","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/knu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"knu"}},"created_at":"2026-05-11T17:00:20.000Z","updated_at":"2026-05-28T16:11:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/knu/tabsquad","commit_stats":null,"previous_names":["knu/tab-squad","knu/tabsquad"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/knu/tabsquad","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knu%2Ftabsquad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knu%2Ftabsquad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knu%2Ftabsquad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knu%2Ftabsquad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knu","download_url":"https://codeload.github.com/knu/tabsquad/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knu%2Ftabsquad/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33718446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser-extension","chrome-extension","chromium","edge-extension","productivity","tab-groups","tab-management","wxt"],"created_at":"2026-05-31T03:30:56.765Z","updated_at":"2026-05-31T03:30:57.544Z","avatar_url":"https://github.com/knu.png","language":"TypeScript","funding_links":["https://github.com/sponsors/knu"],"categories":[],"sub_categories":[],"readme":"# TabSquad\n\n\u003e Keep tab groups organized.\n\nA Chromium-based browser extension that does two things for native tab\ngroups:\n\n1. **Routing**: intercept new tabs spawned from inside a tab group and\n   send them somewhere else -- another tab group, another window, or\n   out of the browser entirely via a custom URL scheme handler.\n2. **Saved groups**: snapshot the URLs of a tab group and restore them\n   later, reusing tabs that still point at the same URL.\n\nThe intended use case is to recreate, with native tab groups, the\nkind of service isolation that all-in-one messenger apps (Ferdium,\nRambox, Franz, ...) provide: keep dedicated tabs (Slack, GitHub,\nyour CRM) together as a \"squad\", route every link they spawn\nelsewhere, and reload the whole squad on demand.\n\n## What TabSquad does not do\n\nA dedicated all-in-one messenger app does more than just hold tabs\ntogether.  TabSquad is just a browser extension, so a handful of those\nfeatures are out of reach -- here is what it does not do and what to\nuse instead:\n\n- **Confirm before closing a tab.**  TabSquad cannot reliably gate\n  every close: `beforeunload` only fires for tabs the user has\n  interacted with, the dialog text is browser-controlled, and\n  session-restored tabs are not covered.  Capture the squad as a\n  saved group beforehand so you can restore it if it disappears, or\n  fall back to the browser's Cmd/Ctrl+Shift+T to reopen the last\n  closed tab.\n- **Keep tabs always loaded.**  TabSquad does nothing to stop the\n  browser from discarding a sleeping tab.  Use the browser's\n  memory-saver / sleeping-tabs settings and add per-site \"always\n  keep this site active\" exceptions there.\n- **Per-group storage / sessions.**  All tabs share the same\n  cookies, logins, and extensions of the current browser profile.\n  If you need a fully separate identity for a squad, use a separate\n  browser profile (or container) for it.\n- **Dedicated taskbar icon or unread-count badge.**  TabSquad shows\n  one toolbar icon for itself, not one per group.  For OS-level\n  presence per service, the dedicated messenger apps still win.\n\n## Status\n\nEarly development. Not yet published to any extension store.\n\n## Features\n\n### Routing rules\n\n- Watches `chrome.webNavigation.onCreatedNavigationTarget` so it\n  catches every new tab created from a link click (left / middle /\n  Cmd/Ctrl+click) or `window.open`.\n- Per-rule action chosen from:\n  1. **Default** -- do nothing.\n  2. **Rewrite URL** -- rewrite the URL via a template and update the\n     tab. The rewritten URL is intended to be a custom scheme handled\n     by an external tool (Hammerspoon, Choosy, Finicky, ...) which\n     then opens the original URL wherever it belongs.\n  3. **Move to the same group's tail** -- keep the tab in the source\n     group but push it to the end.\n  4. **Move to the window's tail** -- eject the tab from the group and\n     pin it to the end of the current window.\n  5. **Move to the next window's tail** -- push the tab into a\n     different normal window. Creates one if none exists.\n  6. **Move to a named tab group** -- find an existing group by title\n     (current window first, then others), or create one in the current\n     window.\n- Rules are matched by tab group title and an optional URL regex\n  (case-insensitive).\n\n### Saved groups\n\n- Pick a tab group in the current window and click **Save** to record\n  its URLs as a snapshot. Saving with the same group title overwrites\n  the previous snapshot.\n- Each snapshot can be expanded to edit, reorder, or remove individual\n  URLs.\n- **Restore** rebuilds the group in the current window using a\n  diff-style update: tabs whose URL already matches a snapshot entry\n  are kept; missing URLs are opened as new tabs; tabs not in the\n  snapshot are closed.\n- **Re-save** overwrites the snapshot from the group's current state.\n\n### Storage\n\nSettings are stored in `chrome.storage.sync` (with `chrome.storage.local`\nas a fallback) and can be exported and imported as JSON.\n\n### Open settings from the toolbar\n\nClicking the extension's toolbar icon opens the options page.\n\n## Permissions\n\n| Permission      | Purpose                                       |\n| --------------- | --------------------------------------------- |\n| `tabs`          | Read tab URLs and create / move / close tabs. |\n| `tabGroups`     | Read and update tab group titles.             |\n| `webNavigation` | Catch new tabs created from links.            |\n| `storage`       | Persist rules and saved groups.               |\n| `history`       | Remove TabSquad's own handoff page from history after it fires an external scheme handler. |\n\nNo host permissions are requested. All URL matching and rewriting\nhappens inside the extension.\n\n## Develop\n\n```sh\npnpm install\npnpm dev          # Chrome\npnpm dev:edge\npnpm build        # Chrome\npnpm build:edge\npnpm zip\n```\n\nWXT generates the manifest and bundles entrypoints. See\n`wxt.config.ts` for manifest details.\n\n## URL handler recipes\n\nWhen using the **Rewrite URL** action, you'll typically point the\ntemplate at a custom scheme handled outside the browser. Examples:\n\n### Hammerspoon (macOS)\n\n```lua\n-- ~/.hammerspoon/init.lua\n-- TabSquad (running in Chrome) sends links here to open them in Edge.\nhs.urlevent.bind(\"open-in-edge\", function(_, params)\n  local url = params and params.url\n  if not url or url == \"\" then return end\n  hs.task.new(\"/usr/bin/open\", nil, { \"-g\", \"-a\", \"Microsoft Edge\", url }):start()\nend)\n\n-- Reverse direction: TabSquad in Edge -\u003e Chrome.\nhs.urlevent.bind(\"open-in-chrome\", function(_, params)\n  local url = params and params.url\n  if not url or url == \"\" then return end\n  hs.task.new(\"/usr/bin/open\", nil, { \"-g\", \"-a\", \"Google Chrome\", url }):start()\nend)\n```\n\nDefault URL templates pre-filled by TabSquad:\n\n- Running in Chrome: `hammerspoon://open-in-edge?url={urlencoded}`\n- Running in Edge: `hammerspoon://open-in-chrome?url={urlencoded}`\n\n### Choosy / Finicky\n\nIf Choosy or Finicky is your default browser, you can use the raw\n`{url}` template -- they will dispatch based on their own rules.\n\n## License\n\n[MIT](LICENSE) (c) 2026 Akinori Musha\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknu%2Ftabsquad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknu%2Ftabsquad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknu%2Ftabsquad/lists"}