From f079f0b2d87e6fc6b87ab6bf5db0eca5c5b984da Mon Sep 17 00:00:00 2001 From: WebDevNerdStuff Date: Fri, 13 Mar 2026 19:27:13 -0400 Subject: [PATCH 1/2] add min tabs to group option and functionality --- src/Options.vue | 3 +- src/Popup.vue | 2 +- src/background.ts | 46 +++++++++++++------------ src/components/Dialog/EditDialog.vue | 13 +++++++ src/components/Popup/GroupSelection.vue | 2 +- src/util/schemas.ts | 1 + 6 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/Options.vue b/src/Options.vue index b6d4672..3c3a142 100644 --- a/src/Options.vue +++ b/src/Options.vue @@ -101,7 +101,7 @@ function deleteGroup(group: GroupConfiguration) { function addGroup( title: string, color: `${chrome.tabGroups.Color}`, - { strict, merge }: SaveOptions, + { strict, merge, minTabsToGroup }: SaveOptions, ) { const id = crypto.randomUUID() @@ -113,6 +113,7 @@ function addGroup( options: { strict, merge, + minTabsToGroup, }, }) diff --git a/src/Popup.vue b/src/Popup.vue index e2d7da0..370df50 100644 --- a/src/Popup.vue +++ b/src/Popup.vue @@ -130,7 +130,7 @@ async function createFromCurrentGroup( title, color, matchers: [], - options: { strict: false, merge: false }, + options: { strict: false, merge: false, minTabsToGroup: 2 }, }, ]) diff --git a/src/background.ts b/src/background.ts index 6bd1068..17e63e1 100644 --- a/src/background.ts +++ b/src/background.ts @@ -38,25 +38,6 @@ const augmentedGroupConfigurations = computed(() => })), ) -const chromeTabsByGroupConfiguration = computed(() => { - const tabsByGroups = new Map() - const tabs = chromeState.tabs.items.value - - for (const tab of tabs) { - const group = getGroupConfigurationForTab(tab) - if (!group) continue - - if (tabsByGroups.has(group)) { - tabsByGroups.get(group)!.push(tab) - } else { - const groupTabs = [tab] - tabsByGroups.set(group, groupTabs) - } - } - - return tabsByGroups -}) - /** * { [string: windowId]: Map } */ @@ -116,6 +97,13 @@ function getGroupConfigurationForTab(tab: chrome.tabs.Tab) { const groupCreationTracker = new GroupCreationTracker() +function hasEnoughTabsToGroup( + group: GroupConfiguration, + tabs: chrome.tabs.Tab[], +) { + return tabs.length >= group.options.minTabsToGroup +} + async function assignTabsToGroup( tabs: chrome.tabs.Tab[], group: GroupConfiguration, @@ -126,9 +114,10 @@ async function assignTabsToGroup( tab.splitViewId === undefined || tab.splitViewId === chrome.tabs.SPLIT_VIEW_ID_NONE, ) - if (tabs.length === 0) return + if (!hasEnoughTabsToGroup(group, tabs)) return + const windowId = tabs[0].windowId const tabGroupPredicate = createGroupConfigurationMatcher(group) @@ -141,6 +130,7 @@ async function assignTabsToGroup( // Before attempting a merge: Check whether the source and target windows are compatible to move tabs between them let shouldMerge = false + if (group.options.merge) { const sourceWindow = chromeState.windows.items.value.find( window => window.id === windowId, @@ -322,6 +312,8 @@ async function groupAllAppropriateTabs() { tabsByGroups, } of chromeTabsByWindowIdAndGroupConfiguration.value) { for (const [group, tabs] of tabsByGroups) { + if (!hasEnoughTabsToGroup(group, tabs)) continue + for (const tab of tabs) { assignedTabIds.add(tab.id!) } @@ -617,6 +609,8 @@ when(groupConfigurations.loaded) console.debug('Waiting for extension state to initialize...') await when(chromeState.tabs.loaded) + console.debug('groupConfigurations', groupConfigurations) + console.debug( 'Extension state initialized, grouping all appropriate tabs now...', ) @@ -679,12 +673,20 @@ when(groupConfigurations.loaded) // by moving a tab. const updatedTab = await chrome.tabs.get(update.tab.id!) + const matchingGroupsInWindow = + chromeTabsByWindowIdAndGroupConfiguration.value.find( + ({ windowId }) => windowId === updatedTab.windowId, + )?.tabsByGroups + let assignedAny = false - for (const [group, tabs] of chromeTabsByGroupConfiguration.value) { + for (const [group, tabs] of matchingGroupsInWindow ?? []) { if (!tabs.some(tab => tab.id === updatedTab.id)) continue + if (!hasEnoughTabsToGroup(group, tabs)) break + assignedAny = true - await assignTabsToGroup([updatedTab], group) + await assignTabsToGroup(tabs, group) + break } // Check if tab has been reassigned, diff --git a/src/components/Dialog/EditDialog.vue b/src/components/Dialog/EditDialog.vue index 19ff35a..d7bdefa 100644 --- a/src/components/Dialog/EditDialog.vue +++ b/src/components/Dialog/EditDialog.vue @@ -38,6 +38,7 @@ const props = withDefaults( options: () => ({ strict: false, merge: false, + minTabsToGroup: 2, }), }, ) @@ -64,6 +65,7 @@ const editTitleLazy = ref(editTitle.value) const editColor = ref(props.color) const editStrict = ref(props.options.strict) const editMerge = ref(props.options.merge) +const editMinTabsToGroup = ref(props.options.minTabsToGroup); const editFieldBlurred = ref(false) const colorMenu = ref() @@ -88,6 +90,7 @@ watch( editColor.value = props.color editStrict.value = props.options.strict editMerge.value = props.options.merge + editMinTabsToGroup.value = props.options.minTabsToGroup; editFieldBlurred.value = false await nextTick() @@ -139,6 +142,7 @@ function save(event: KeyboardEvent) { emit('save', editTitle.value, editColor.value, { strict: editStrict.value, merge: editMerge.value, + minTabsToGroup: editMinTabsToGroup.value, }) show.value = false } @@ -203,6 +207,15 @@ function remove() { + + + + diff --git a/src/components/Popup/GroupSelection.vue b/src/components/Popup/GroupSelection.vue index 15f268c..1db4c50 100644 --- a/src/components/Popup/GroupSelection.vue +++ b/src/components/Popup/GroupSelection.vue @@ -89,7 +89,7 @@ async function addFromCurrent() { title: tabGroup.value!.title ?? '', color: tabGroup.value!.color, matchers: [], - options: { strict: false, merge: false }, + options: { strict: false, merge: false, minTabsToGroup: 2 }, }, ]) await groupsChanged diff --git a/src/util/schemas.ts b/src/util/schemas.ts index 09450df..5f334cf 100644 --- a/src/util/schemas.ts +++ b/src/util/schemas.ts @@ -5,6 +5,7 @@ import { colors } from './resources' export const SaveOptionsSchema = z.object({ strict: z.boolean().default(false), merge: z.boolean().default(false), + minTabsToGroup: z.number().int().positive().default(2), }) const matcherPatternRegex = new RegExp(matcherPattern) From 27b16cb9cb3d028004577c90a1056cb806f2043f Mon Sep 17 00:00:00 2001 From: WebDevNerdStuff Date: Fri, 13 Mar 2026 19:32:34 -0400 Subject: [PATCH 2/2] update tests with new option --- src/test/browser/options/create.test.ts | 2 +- src/test/browser/options/edit.test.ts | 6 +++--- src/test/browser/options/sort.test.ts | 6 +++--- src/test/browser/options/url-patterns.test.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/browser/options/create.test.ts b/src/test/browser/options/create.test.ts index 2b4f826..ba72c9b 100644 --- a/src/test/browser/options/create.test.ts +++ b/src/test/browser/options/create.test.ts @@ -81,7 +81,7 @@ test('Test group creation flow', async ({ page }) => { title: 'Test Group', color: 'blue', matchers: [], - options: { strict: true, merge: true }, + options: { strict: true, merge: true, minTabsToGroup: 2 }, }, ]) }) diff --git a/src/test/browser/options/edit.test.ts b/src/test/browser/options/edit.test.ts index 6b629e9..915e104 100644 --- a/src/test/browser/options/edit.test.ts +++ b/src/test/browser/options/edit.test.ts @@ -25,7 +25,7 @@ test.beforeEach(async ({ page }) => { title: 'Test Group', color: 'blue', matchers: [], - options: { strict: true, merge: true }, + options: { strict: true, merge: true, minTabsToGroup: 2 }, } as GroupConfigurationWithoutId, ]) @@ -105,7 +105,7 @@ test('Edit and save groups', async ({ page }) => { title: 'Edited Group', color: 'red', matchers: [], - options: { strict: false, merge: false }, + options: { strict: false, merge: false, minTabsToGroup: 2 }, }, ]) }) @@ -136,7 +136,7 @@ test('Delete Groups and undo deletion', async ({ page }) => { title: 'Test Group', color: 'blue', matchers: [], - options: { strict: true, merge: true }, + options: { strict: true, merge: true, minTabsToGroup: 2 }, }, ]) }) diff --git a/src/test/browser/options/sort.test.ts b/src/test/browser/options/sort.test.ts index 205f9b2..d0fd68c 100644 --- a/src/test/browser/options/sort.test.ts +++ b/src/test/browser/options/sort.test.ts @@ -21,19 +21,19 @@ test('Drag and drop groups', async ({ page }) => { title: 'Test Group 1', color: 'blue', matchers: [], - options: { strict: true, merge: false }, + options: { strict: true, merge: false, minTabsToGroup: 2 }, } const group2 = { title: 'Test Group 2', color: 'red', matchers: ['example.com', 'example.org'], - options: { strict: true, merge: false }, + options: { strict: true, merge: false, minTabsToGroup: 2 }, } const group3 = { title: 'Test Group 3', color: 'green', matchers: ['github.com'], - options: { strict: true, merge: false }, + options: { strict: true, merge: false, minTabsToGroup: 2 }, } // Define groups programmatically diff --git a/src/test/browser/options/url-patterns.test.ts b/src/test/browser/options/url-patterns.test.ts index 459942c..d5cd090 100644 --- a/src/test/browser/options/url-patterns.test.ts +++ b/src/test/browser/options/url-patterns.test.ts @@ -25,7 +25,7 @@ test.beforeEach(async ({ page }) => { title: 'Test Group', color: 'blue', matchers: [], - options: { strict: true, merge: false }, + options: { strict: true, merge: false, minTabsToGroup: 2 }, } as GroupConfigurationWithoutId, ])