tabs.ts
1 /** 2 * Browser tab management helpers: extract, diff, and cleanup tab state. 3 */ 4 5 export function extractTabEntries(raw: unknown): Array<{ index: number; identity: string }> { 6 if (Array.isArray(raw)) { 7 return raw.map((tab: Record<string, unknown>, index: number) => ({ 8 index, 9 identity: [ 10 tab?.id ?? '', 11 tab?.url ?? '', 12 tab?.title ?? '', 13 tab?.name ?? '', 14 ].join('|'), 15 })); 16 } 17 18 if (typeof raw === 'string') { 19 return raw 20 .split('\n') 21 .map(line => line.trim()) 22 .filter(Boolean) 23 .map(line => { 24 // Match tab list format: "- 0: (current) [title](url)" or "- 1: [title](url)" 25 const tabMatch = line.match(/^-\s+(\d+):\s*(.*)$/); 26 if (tabMatch) { 27 return { 28 index: parseInt(tabMatch[1], 10), 29 identity: tabMatch[2].trim() || `tab-${tabMatch[1]}`, 30 }; 31 } 32 // Legacy format: "Tab 0 ..." 33 const legacyMatch = line.match(/Tab\s+(\d+)\s*(.*)$/); 34 if (legacyMatch) { 35 return { 36 index: parseInt(legacyMatch[1], 10), 37 identity: legacyMatch[2].trim() || `tab-${legacyMatch[1]}`, 38 }; 39 } 40 return null; 41 }) 42 .filter((entry): entry is { index: number; identity: string } => entry !== null); 43 } 44 45 return []; 46 } 47 48 export function extractTabIdentities(raw: unknown): string[] { 49 return extractTabEntries(raw).map(tab => tab.identity); 50 } 51 52 export function diffTabIndexes(initialIdentities: string[], currentTabs: Array<{ index: number; identity: string }>): number[] { 53 if (initialIdentities.length === 0 || currentTabs.length === 0) return []; 54 const remaining = new Map<string, number>(); 55 for (const identity of initialIdentities) { 56 remaining.set(identity, (remaining.get(identity) ?? 0) + 1); 57 } 58 59 const tabsToClose: number[] = []; 60 for (const tab of currentTabs) { 61 const count = remaining.get(tab.identity) ?? 0; 62 if (count > 0) { 63 remaining.set(tab.identity, count - 1); 64 continue; 65 } 66 tabsToClose.push(tab.index); 67 } 68 69 return tabsToClose.sort((a, b) => b - a); 70 } 71 72 export function appendLimited(current: string, chunk: string, limit: number): string { 73 const next = current + chunk; 74 if (next.length <= limit) return next; 75 return next.slice(-limit); 76 }