Skip to content

feat(profiles): add tab group conditions - #73

Open
aiktb wants to merge 5 commits into
mainfrom
agent/tab-group-conditions
Open

feat(profiles): add tab group conditions#73
aiktb wants to merge 5 commits into
mainfrom
agent/tab-group-conditions

Conversation

@aiktb

@aiktb aiktb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add included and excluded Tab Group conditions with an optional tabGroups permission request
  • persist each selected group with its current tab IDs and translate those bindings into DNR tab conditions
  • keep group membership synchronized across tab/group removal, membership changes, browser startup, extension updates, and service worker restarts
  • show group color and title in the multi-select, deriving unnamed labels from the first tab and remaining-tab count
  • add an at-sign field action and initialize new conditions/fields from the current tab group when available
  • mark Tab Group and Sync Cookie options with a secondary badge when they require an additional optional permission

Why

Chrome DNR rules accept tab IDs but do not accept tab group IDs directly. Headerly needs to retain the selected group identity while maintaining the live tab ID snapshot used to register the rule.

User impact

Users can target or exclude complete Chrome tab groups. The rule follows membership changes during the browser session and removes stale group or tab bindings automatically.

Validation

  • pnpm run lint:fix
  • pnpm run typecheck
  • pnpm run test (59 tests passed)

@aiktb aiktb changed the title feat: add tab group conditions feat(profiles): add tab group conditions Jul 29, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 29, 2026

Copy link
Copy Markdown

Deploying headerly with  Cloudflare Pages  Cloudflare Pages

Latest commit: fa8966e
Status: ✅  Deploy successful!
Preview URL: https://73a8b06d.headerly.pages.dev
Branch Preview URL: https://agent-tab-group-conditions.headerly.pages.dev

View logs

@aiktb
aiktb requested a review from Copilot July 29, 2026 14:19
@aiktb
aiktb marked this pull request as ready for review July 29, 2026 14:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Tab Group–based profile conditions to Headerly, bridging Chrome’s limitation (DNR supports tab IDs but not tab group IDs) by persisting a group identity plus a live snapshot of member tab IDs and keeping that snapshot synchronized in the background.

Changes:

  • Adds new tabGroups / excludedTabGroups filter types to the profile schema and omits them from import/export as session-only bindings.
  • Introduces popup UI for selecting tab groups (with color/title display and unnamed-group labeling) and adds i18n strings for the new conditions.
  • Implements a background synchronizer to keep stored tab-group tab ID snapshots current and translates group snapshots into DNR tabIds / excludedTabIds conditions.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
extension/wxt.config.ts Adds tabGroups as an optional permission.
extension/src/locales/en.json Adds English UI strings for tab group conditions and selector labels.
extension/src/locales/zh-CN.json Adds Chinese UI strings for tab group conditions and selector labels.
extension/src/lib/schema.ts Adds tab group binding schemas and extends filters with tabGroups / excludedTabGroups; omits them from export payloads.
extension/src/lib/permissions.ts Adds ensureTabGroupsPermission() helper for optional permission requests.
extension/src/lib/openState.ts Includes new filter groups in open-state tracking.
extension/src/lib/TESTS/tabGroupSync.test.ts Adds unit tests for tab-group snapshot synchronization logic.
extension/src/lib/TESTS/schema.test.ts Updates import/export tests to cover omission of tab group filters.
extension/src/lib/TESTS/schema.fixtures.ts Extends mock profile fixtures with tab group filters.
extension/src/lib/TESTS/profileRule.test.ts Adds coverage for tab-group-based “temporary tab binding” detection.
extension/src/lib/TESTS/buildCondition.test.ts Extends DNR condition-building tests to include tab group snapshots contributing tab IDs.
extension/src/entrypoints/popup/pages/profiles/components/SelectedProfile/index.vue Treats tab group filters as non-empty filters for UI state.
extension/src/entrypoints/popup/pages/profiles/components/SelectedProfile/conditions/TabGroups.vue Adds a new condition group UI for tab group filters.
extension/src/entrypoints/popup/pages/profiles/components/SelectedProfile/components/TabGroupSelect.vue Adds the multi-select widget to pick tab groups and persist group→tab snapshot bindings.
extension/src/entrypoints/popup/pages/profiles/components/SelectedProfile/components/FiltersFieldset.vue Wires the new tab group condition UI into the profile filters fieldset.
extension/src/entrypoints/popup/pages/profiles/components/Header/components/AddRuleOptionDialog/tabGroupConditions.ts Adds “add condition” actions for tab group conditions (gated by optional permission request).
extension/src/entrypoints/popup/pages/profiles/components/Header/components/AddRuleOptionDialog/conditions.ts Registers the new tab group condition options in the add-condition dialog.
extension/src/entrypoints/background/tabGroupSync.ts Adds background synchronization to keep tab-group tab ID snapshots current and purge stale bindings.
extension/src/entrypoints/background/profileRule.ts Extends temporary-binding detection to include tab group snapshots.
extension/src/entrypoints/background/index.ts Enables tab group sync on background startup.
extension/src/entrypoints/background/DNR/buildCondition.ts Maps tab group snapshots into DNR tabIds / excludedTabIds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +30 to +49
browser.tabs.onRemoved.addListener((tabId) => {
pendingRemovedTabIds.add(tabId);
scheduleSync();
});
browser.tabGroups.onRemoved.addListener((group) => {
pendingRemovedGroupIds.add(group.id);
scheduleSync();
});
browser.tabs.onUpdated.addListener((_tabId, changeInfo) => {
if (changeInfo.groupId !== undefined) {
shouldRefreshAllBindings = true;
scheduleSync();
}
});
browser.tabs.onCreated.addListener((tab) => {
if (tab.groupId !== undefined && tab.groupId !== browser.tabGroups.TAB_GROUP_ID_NONE) {
shouldRefreshAllBindings = true;
scheduleSync();
}
});
Comment on lines +123 to +129
const {
removedGroupIds = new Set(),
removedTabIds = new Set(),
refreshedTabIdsByGroupId,
} = changes;
const nextManager = structuredClone(manager);
let changed = false;
Comment on lines +88 to +106
async function refreshGroups() {
try {
const [queriedGroups, queriedTabs] = await Promise.all([
browser.tabGroups.query({}),
browser.tabs.query({}),
]);
tabs.value = queriedTabs;
groups.value = queriedGroups.sort((a, b) => {
const firstTabA = queriedTabs.find(tab => tab.groupId === a.id);
const firstTabB = queriedTabs.find(tab => tab.groupId === b.id);
return a.windowId - b.windowId || (firstTabA?.index ?? 0) - (firstTabB?.index ?? 0);
});
selectedGroupIds.value = selectedGroupIds.value.filter(groupId =>
queriedGroups.some(group => group.id === groupId),
);
} finally {
groupsLoaded.value = true;
}
}
@aiktb
aiktb force-pushed the agent/tab-group-conditions branch from adccf76 to fa8966e Compare July 29, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants