diff --git a/CLAUDE.md b/CLAUDE.md index b65e8a1..b69c97f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,7 +108,7 @@ chrome-extension/ - Tabs are sorted alphabetically by title within each group - Only groups with 2+ tabs are created (singles remain ungrouped) - Chrome internal pages (`chrome://`, `chrome-extension://`, `about:`) are skipped -- Each group shows name and tab count: `github.com (25)` +- Each group shows name only: `github.com` - Colors rotate through: blue, red, yellow, green, pink, purple, cyan, orange - Bookmark save/restore automatically manages Tab Organizer bookmark folders with timestamps - Merge duplicate groups respects protected groups (protected groups absorb but never dissolve) diff --git a/chrome-extension/e2e/base-domain-and-edge-cases.test.js b/chrome-extension/e2e/base-domain-and-edge-cases.test.js index 99b049d..a067429 100644 --- a/chrome-extension/e2e/base-domain-and-edge-cases.test.js +++ b/chrome-extension/e2e/base-domain-and-edge-cases.test.js @@ -92,7 +92,7 @@ describe('Base Domain Grouping and Edge Cases', () => { // Should have one group for github.com (all subdomains grouped together) const githubGroup = groups.find(g => g.title.includes('github.com')); expect(githubGroup).toBeDefined(); - expect(githubGroup.title).toBe('github.com (3)'); + expect(githubGroup.title).toBe('github.com'); console.log('✅ Subdomains grouped by base domain.'); }, 90000); @@ -213,7 +213,7 @@ describe('Base Domain Grouping and Edge Cases', () => { const githubGroup = groups.find(g => g.title.includes('github.com')); expect(githubGroup).toBeDefined(); - expect(githubGroup.title).toBe('github.com (2)'); + expect(githubGroup.title).toBe('github.com'); console.log('✅ Chrome internal pages skipped.'); }, 90000); diff --git a/chrome-extension/e2e/collapsed-group-preservation.test.js b/chrome-extension/e2e/collapsed-group-preservation.test.js index 842da23..e5195f5 100644 --- a/chrome-extension/e2e/collapsed-group-preservation.test.js +++ b/chrome-extension/e2e/collapsed-group-preservation.test.js @@ -190,8 +190,8 @@ describe('Group State Preservation (Collapsed Groups)', () => { // Verify color is preserved expect(updatedGroup.color).toBe(initialColor); - // Verify tab count updated - expect(updatedGroup.title).toBe('github.com (5)'); // 3 initial + 2 additional + // Verify title is preserved + expect(updatedGroup.title).toBe('github.com'); console.log('✅ Collapsed state was preserved.'); }, 90000); diff --git a/chrome-extension/e2e/combine-groups.test.js b/chrome-extension/e2e/combine-groups.test.js index f5acc26..10b743d 100644 --- a/chrome-extension/e2e/combine-groups.test.js +++ b/chrome-extension/e2e/combine-groups.test.js @@ -214,8 +214,7 @@ describe('Combine Groups Feature', () => { console.log('Final groups:', finalGroups); expect(finalGroups.length).toBe(1); - expect(finalGroups[0].title).toContain('github.com'); - expect(finalGroups[0].title).toContain('5'); // 3 github + 2 google tabs + expect(finalGroups[0].title).toBe('github.com'); console.log('✅ Combine two groups test passed!'); @@ -354,8 +353,7 @@ describe('Combine Groups Feature', () => { console.log('Final groups:', finalGroups); expect(finalGroups.length).toBe(1); - expect(finalGroups[0].title).toContain('github.com'); - expect(finalGroups[0].title).toContain('7'); // 3 github + 2 google + 2 stackoverflow + expect(finalGroups[0].title).toBe('github.com'); console.log('✅ Combine multiple source groups test passed!'); diff --git a/chrome-extension/e2e/cross-window-organization.test.js b/chrome-extension/e2e/cross-window-organization.test.js index 76d2c0e..8bb43ec 100644 --- a/chrome-extension/e2e/cross-window-organization.test.js +++ b/chrome-extension/e2e/cross-window-organization.test.js @@ -102,7 +102,7 @@ describe('Cross-Window Tab Organization', () => { // Should have created github group with all tabs from both windows const githubGroup = groups.find(g => g.title.includes('github.com')); expect(githubGroup).toBeDefined(); - expect(githubGroup.title).toBe('github.com (3)'); + expect(githubGroup.title).toBe('github.com'); console.log('✅ Cross-window consolidation passed!'); }, 120000); @@ -164,7 +164,7 @@ describe('Cross-Window Tab Organization', () => { const githubGroup = groups.find(g => g.title.includes('github.com')); expect(githubGroup).toBeDefined(); - expect(githubGroup.title).toBe('github.com (2)'); // react + vue (angular duplicate removed) + expect(githubGroup.title).toBe('github.com'); // react + vue (angular duplicate removed) console.log('✅ Cross-window duplicate removal passed!'); }, 120000); @@ -216,7 +216,7 @@ describe('Cross-Window Tab Organization', () => { const developmentGroup = groups.find(g => g.title.includes('Development')); expect(developmentGroup).toBeDefined(); - expect(developmentGroup.title).toBe('Development (4)'); + expect(developmentGroup.title).toBe('Development'); console.log('✅ Cross-window category organization passed!'); }, 120000); @@ -282,7 +282,7 @@ describe('Cross-Window Tab Organization', () => { const githubGroup = groups.find(g => g.title.includes('github.com')); expect(githubGroup).toBeDefined(); - expect(githubGroup.title).toBe('github.com (3)'); // Only the 3 github tabs + expect(githubGroup.title).toBe('github.com'); console.log('✅ Chrome internal pages skipped correctly!'); }, 120000); diff --git a/chrome-extension/e2e/first-time-organization.test.js b/chrome-extension/e2e/first-time-organization.test.js index 829da4e..8563d17 100644 --- a/chrome-extension/e2e/first-time-organization.test.js +++ b/chrome-extension/e2e/first-time-organization.test.js @@ -143,11 +143,9 @@ describe('First-Time Organization (Create New Groups)', () => { // Verify we have 3 groups (github, google, example) expect(groups.length).toBe(3); - // Verify group titles match expected domains + // Verify group titles match expected domains (no longer include tab counts) const groupTitles = groups.map(g => g.title).sort(); - expect(groupTitles).toContain('example.com (3)'); - expect(groupTitles).toContain('github.com (5)'); - expect(groupTitles).toContain('google.com (4)'); + expect(groupTitles).toEqual(['example.com', 'github.com', 'google.com']); // Step 5: Verify tabs are sorted alphabetically within each group const tabs = await serviceWorker.evaluate(async () => { diff --git a/chrome-extension/e2e/tab-movement-and-ungrouping.test.js b/chrome-extension/e2e/tab-movement-and-ungrouping.test.js index 44600bf..8a522a9 100644 --- a/chrome-extension/e2e/tab-movement-and-ungrouping.test.js +++ b/chrome-extension/e2e/tab-movement-and-ungrouping.test.js @@ -117,8 +117,8 @@ describe('Tab Movement, Ungrouping, Mixed Operations', () => { const githubGroup = initialGroups.find(g => g.title.includes('github.com')); const googleGroup = initialGroups.find(g => g.title.includes('google.com')); - expect(githubGroup.title).toBe('github.com (3)'); - expect(googleGroup.title).toBe('google.com (2)'); + expect(githubGroup.title).toBe('github.com'); + expect(googleGroup.title).toBe('google.com'); // Navigate a GitHub tab to google.com console.log('Changing one GitHub tab to google.com...'); @@ -152,8 +152,8 @@ describe('Tab Movement, Ungrouping, Mixed Operations', () => { const updatedGoogle = updatedGroups.find(g => g.title.includes('google.com')); // GitHub should have 1 less, Google should have 1 more - expect(updatedGithub.title).toBe('github.com (2)'); - expect(updatedGoogle.title).toBe('google.com (3)'); + expect(updatedGithub.title).toBe('github.com'); + expect(updatedGoogle.title).toBe('google.com'); console.log('✅ Tab moved between groups correctly.'); }, 120000); @@ -183,7 +183,7 @@ describe('Tab Movement, Ungrouping, Mixed Operations', () => { console.log('Groups before closing tab:', groupsBefore); const exampleGroup = groupsBefore.find(g => g.title.includes('example.com')); expect(exampleGroup).toBeDefined(); - expect(exampleGroup.title).toBe('example.com (2)'); + expect(exampleGroup.title).toBe('example.com'); // Close one example.com tab console.log('Closing one example.com tab...'); @@ -283,11 +283,11 @@ describe('Tab Movement, Ungrouping, Mixed Operations', () => { // Should have created stackoverflow group const stackoverflowGroup = groupsAfter.find(g => g.title.includes('stackoverflow.com')); expect(stackoverflowGroup).toBeDefined(); - expect(stackoverflowGroup.title).toBe('stackoverflow.com (3)'); + expect(stackoverflowGroup.title).toBe('stackoverflow.com'); // Should have updated github group (2 initial + 2 new = 4) const githubGroup = groupsAfter.find(g => g.title.includes('github.com')); - expect(githubGroup.title).toBe('github.com (4)'); + expect(githubGroup.title).toBe('github.com'); console.log('✅ Mixed create and update worked correctly.'); }, 120000); diff --git a/chrome-extension/e2e/update-existing-groups.test.js b/chrome-extension/e2e/update-existing-groups.test.js index 3092772..2dba074 100644 --- a/chrome-extension/e2e/update-existing-groups.test.js +++ b/chrome-extension/e2e/update-existing-groups.test.js @@ -200,11 +200,9 @@ describe('Re-Organization (Update Existing Groups)', () => { const updatedGroupIds = updatedGroups.map(g => g.id).sort(); expect(updatedGroupIds).toEqual(initialGroupIds.sort()); - // Verify group titles reflect the new tab counts + // Verify group titles (no longer include tab counts) const groupTitles = updatedGroups.map(g => g.title).sort(); - expect(groupTitles).toContain('example.com (3)'); // was 2, now 3 - expect(groupTitles).toContain('github.com (5)'); // was 3, now 5 - expect(groupTitles).toContain('google.com (4)'); // was 2, now 4 + expect(groupTitles).toEqual(['example.com', 'github.com', 'google.com']); // Step 8: Verify tabs are sorted alphabetically within each group const allTabs = await serviceWorker.evaluate(async () => { diff --git a/chrome-extension/src/background/combineGroups.js b/chrome-extension/src/background/combineGroups.js index 802de92..c835fde 100644 --- a/chrome-extension/src/background/combineGroups.js +++ b/chrome-extension/src/background/combineGroups.js @@ -32,7 +32,7 @@ export async function combineGroups(sourceGroupIds, targetGroupId) { }; } - // Get current target tab count for final count calculation + // Get current target tab count for status message display const targetTabs = await chrome.tabs.query({ groupId: targetGroupId }); const tabsMoved = allSourceTabs.length; const newTargetTabCount = targetTabs.length + tabsMoved; @@ -41,9 +41,9 @@ export async function combineGroups(sourceGroupIds, targetGroupId) { const sourceTabIds = allSourceTabs.map(tab => tab.id); await chrome.tabs.group({ tabIds: sourceTabIds, groupId: targetGroupId }); - // Update target group title with new count + // Update target group title await chrome.tabGroups.update(targetGroupId, { - title: `${targetGroupName} (${newTargetTabCount})` + title: targetGroupName }); return { diff --git a/chrome-extension/src/background/combineGroups.test.js b/chrome-extension/src/background/combineGroups.test.js index 0292214..f175c36 100644 --- a/chrome-extension/src/background/combineGroups.test.js +++ b/chrome-extension/src/background/combineGroups.test.js @@ -59,7 +59,7 @@ describe('combineGroups', () => { groupId: 2, }); expect(chrome.tabGroups.update).toHaveBeenCalledWith(2, { - title: 'Development (7)', + title: 'Development', }); expect(result).toEqual({ sourceGroupNames: ['Documentation'], @@ -132,7 +132,7 @@ describe('combineGroups', () => { groupId: 2, }); expect(chrome.tabGroups.update).toHaveBeenCalledWith(2, { - title: 'Development (9)', + title: 'Development', }); expect(result).toEqual({ sourceGroupNames: ['Documentation', 'Research'], diff --git a/chrome-extension/src/background/mergeDuplicateGroups.js b/chrome-extension/src/background/mergeDuplicateGroups.js index 6ad2172..963a900 100644 --- a/chrome-extension/src/background/mergeDuplicateGroups.js +++ b/chrome-extension/src/background/mergeDuplicateGroups.js @@ -110,10 +110,9 @@ async function mergeDuplicateGroups() { try { await chrome.tabs.group({ tabIds: allSourceTabIds, groupId: targetGroupData.group.id }); - // Update target group title with new count - const newTabCount = targetGroupData.tabCount + allSourceTabIds.length; + // Update target group title await chrome.tabGroups.update(targetGroupData.group.id, { - title: `${baseName} (${newTabCount})` + title: baseName }); mergedGroups += sourceGroupsData.length; diff --git a/chrome-extension/src/background/mergeDuplicateGroups.test.js b/chrome-extension/src/background/mergeDuplicateGroups.test.js index 78a996c..61a0da7 100644 --- a/chrome-extension/src/background/mergeDuplicateGroups.test.js +++ b/chrome-extension/src/background/mergeDuplicateGroups.test.js @@ -55,7 +55,7 @@ describe('mergeDuplicateGroups', () => { groupId: 1, }); expect(chrome.tabGroups.update).toHaveBeenCalledWith(1, { - title: 'github.com (15)', + title: 'github.com', }); expect(result.mergedGroups).toBe(1); expect(result.tabsMoved).toBe(5); @@ -134,7 +134,7 @@ describe('mergeDuplicateGroups', () => { groupId: 2, }); expect(chrome.tabGroups.update).toHaveBeenCalledWith(2, { - title: 'github.com (15)', + title: 'github.com', }); }); diff --git a/chrome-extension/src/background/organizeTabs.js b/chrome-extension/src/background/organizeTabs.js index 5e5804f..a192a33 100644 --- a/chrome-extension/src/background/organizeTabs.js +++ b/chrome-extension/src/background/organizeTabs.js @@ -210,9 +210,9 @@ async function organizeTabs(mode = 'domain', allWindows = false) { await chrome.tabs.ungroup(toRemove); } - // Update group title with new count (preserve color and collapsed state) + // Update group title (preserve color and collapsed state) await chrome.tabGroups.update(existingGroup.id, { - title: `${groupName} (${newTabIds.length})` + title: groupName }); groupsUpdated++; @@ -221,7 +221,7 @@ async function organizeTabs(mode = 'domain', allWindows = false) { const groupId = await chrome.tabs.group({ tabIds: newTabIds }); await chrome.tabGroups.update(groupId, { - title: `${groupName} (${newTabIds.length})`, + title: groupName, color: getNextColor(), collapsed: false }); diff --git a/chrome-extension/src/background/organizeTabs.test.js b/chrome-extension/src/background/organizeTabs.test.js index 75a3016..3128dcf 100644 --- a/chrome-extension/src/background/organizeTabs.test.js +++ b/chrome-extension/src/background/organizeTabs.test.js @@ -189,9 +189,9 @@ describe('organizeTabs', () => { expect(chrome.tabs.ungroup).not.toHaveBeenCalledWith(1); expect(chrome.tabs.ungroup).not.toHaveBeenCalledWith(2); - // Should update the existing group title (even though count didn't change) + // Should update the existing group title expect(chrome.tabGroups.update).toHaveBeenCalledWith(5, { - title: 'github.com (2)' + title: 'github.com' }); // Should NOT create a new group @@ -210,7 +210,7 @@ describe('organizeTabs', () => { await organizeTabs('domain'); expect(chrome.tabGroups.update).toHaveBeenCalledWith(1, { - title: 'github.com (2)', + title: 'github.com', color: 'blue', collapsed: false }); @@ -231,12 +231,12 @@ describe('organizeTabs', () => { expect(chrome.tabs.group).toHaveBeenCalledTimes(2); expect(chrome.tabGroups.update).toHaveBeenCalledWith(1, { - title: 'localhost (2)', + title: 'localhost', color: 'blue', collapsed: false }); expect(chrome.tabGroups.update).toHaveBeenCalledWith(2, { - title: 'local-network (2)', + title: 'local-network', color: 'red', collapsed: false }); @@ -268,9 +268,9 @@ describe('organizeTabs', () => { groupId: 5 }); - // Should update group title with new count + // Should update group title expect(chrome.tabGroups.update).toHaveBeenCalledWith(5, { - title: 'github.com (4)' + title: 'github.com' }); // Should NOT ungroup any tabs @@ -298,9 +298,9 @@ describe('organizeTabs', () => { // Note: chrome.tabs.ungroup is called with array from within group diff logic expect(chrome.tabs.ungroup).toHaveBeenCalledWith([3]); - // Should update group title with new count + // Should update group title expect(chrome.tabGroups.update).toHaveBeenCalledWith(5, { - title: 'github.com (2)' + title: 'github.com' }); }); @@ -328,7 +328,7 @@ describe('organizeTabs', () => { // Should update existing github group (no changes needed) expect(chrome.tabGroups.update).toHaveBeenCalledWith(5, { - title: 'github.com (2)' + title: 'github.com' }); // Should create new stackoverflow group @@ -336,7 +336,7 @@ describe('organizeTabs', () => { tabIds: [3, 4] }); expect(chrome.tabGroups.update).toHaveBeenCalledWith(7, { - title: 'stackoverflow.com (2)', + title: 'stackoverflow.com', color: 'blue', collapsed: false }); @@ -398,8 +398,8 @@ describe('organizeTabs', () => { // Check that both groups were created with correct titles const updateCalls = chrome.tabGroups.update.mock.calls; const titles = updateCalls.map(call => call[1].title); - expect(titles).toContain('Development (3)'); - expect(titles).toContain('Documentation (2)'); + expect(titles).toContain('Development'); + expect(titles).toContain('Documentation'); }); test('should categorize social media sites correctly', async () => { @@ -420,7 +420,7 @@ describe('organizeTabs', () => { // Check the title of the created group const updateCalls = chrome.tabGroups.update.mock.calls; const lastCall = updateCalls[updateCalls.length - 1]; - expect(lastCall[1].title).toBe('Social Media (3)'); + expect(lastCall[1].title).toBe('Social Media'); }); test('should categorize shopping sites correctly', async () => { @@ -440,7 +440,7 @@ describe('organizeTabs', () => { // Check the title of the created group const updateCalls = chrome.tabGroups.update.mock.calls; const lastCall = updateCalls[updateCalls.length - 1]; - expect(lastCall[1].title).toBe('Shopping (2)'); + expect(lastCall[1].title).toBe('Shopping'); }); test('should categorize cloud services correctly', async () => { @@ -461,7 +461,7 @@ describe('organizeTabs', () => { // Check the title of the created group const updateCalls = chrome.tabGroups.update.mock.calls; const lastCall = updateCalls[updateCalls.length - 1]; - expect(lastCall[1].title).toBe('Cloud Services (2)'); + expect(lastCall[1].title).toBe('Cloud Services'); }); test('should group uncategorized tabs as "Other"', async () => { @@ -504,9 +504,9 @@ describe('organizeTabs', () => { groupId: 5 }); - // Should update group title with new count + // Should update group title expect(chrome.tabGroups.update).toHaveBeenCalledWith(5, { - title: 'Development (3)' + title: 'Development' }); }); }); @@ -680,7 +680,7 @@ describe('organizeTabs', () => { expect(updateCalls[9][1].color).toBe('red'); }); - test('should include tab count in group title', async () => { + test('should use domain name only as group title', async () => { const mockTabs = [ { id: 1, url: 'https://github.com/r1', title: 'R1', groupId: -1 }, { id: 2, url: 'https://github.com/r2', title: 'R2', groupId: -1 }, @@ -693,7 +693,7 @@ describe('organizeTabs', () => { await organizeTabs('domain'); expect(chrome.tabGroups.update).toHaveBeenCalledWith(1, { - title: 'github.com (3)', + title: 'github.com', color: 'blue', collapsed: false }); diff --git a/chrome-extension/src/utils/extractGroupBaseName.js b/chrome-extension/src/utils/extractGroupBaseName.js index 7390fd4..1e8ffd8 100644 --- a/chrome-extension/src/utils/extractGroupBaseName.js +++ b/chrome-extension/src/utils/extractGroupBaseName.js @@ -3,14 +3,16 @@ /** * Extracts the base name from a group title by removing the tab count suffix. - * This allows matching existing groups with new grouping operations. + * Primarily used for backward compatibility with older group formats that + * included tab counts in the title. Also enables matching existing groups + * with new grouping operations regardless of their title format. * * Examples: - * - "github.com (15)" → "github.com" - * - "acme.com (5)" → "acme.com" + * - "github.com (15)" → "github.com" // Legacy format with count + * - "github.com" → "github.com" // Current format (unchanged) * - "Development (10)" → "Development" * - * @param {string} groupTitle - The full group title with count + * @param {string} groupTitle - The group title (with or without count suffix) * @returns {string} The base name without the count suffix */ function extractGroupBaseName(groupTitle) {