Taming 1500 Safari Tabs

Published on September 10, 2024
Taming 1500 Safari Tabs

When Safari is open, I find that I'm terribly frustrated by the blink-long gap between button-press and glyph-appear. I reckon that's because, with three Apple devices sharing tabs, and an ill-disciplined approach to looking stuff up, I have countless tabs open.

Not countless. Of course they can be counted. After a couple of queries to ask how to manage my tabs and to see if it knew a tool I didn't (reader, it did), I asked Claude to write me an script to pick them out of Safari, show me them as text with a bunch of details, and count them....

I'd like to create a custom automator script to extract details of my tabs from safari. I guess that the details I want are the title, URL, parent window details and any date information. Can you help with that?

...and it did. There were fucking thousands.

Claude didn't simply give me code. It gave me alternatives and instructions. I followed its instructions, ran the supplied AppleScript in Automator and it produced enough for me to see that the numbers were credible, and the details were dumb.

Seeking to polish out the dumb, I asked Claude to sort the set by url and count the top 10 most-common hostnames. It wrote a bubble sort (because training data, I guess), so I gave that a jiggle towards a native sort with another prompt. Then asked it to switch the AppleScript into JavaScript, because frankly writing AppleScript is like playing an 80s 'natural language' text adventure game. I loved those things, but there's better stuff to do with one's attention now.

The suggested script used URL, and JavaScript for Automation baulked / borked at that. Once I'd asked for a regex to hoik out the hostname instead, all was broadly OK, and the script was producing similar output, this time via clearer code. I broke out a few functions and parameterised a magic number, to give me some sense of agency and to make the thing clearer still if I came back to it.

Then, a touch of polishing and integration. I switched from working on the code in Automator, which is a pain, to working on the code in Script Editor, and running the executable in Shortcuts. With the benefit of hindsight, I'm actually not sure precisely why. But the decision cost me.

Could I get Script Editor to ouptut a \n newline? I could not. Did the script give me half an hour's debugging arseache with a rogue empty-but-not-really tab? It did. Did I resort to idiot debugging and tiny iterations? Yes: I blame Script Editor, and I draw your attention, fellow debug-bruised Script Wranglers (and me when forgetful), to the Log History in Script Editor's Windows menu. Did Shortcuts try to ask permission to open every single URL when I put the output into an notification – and am I therefore happy with the ugly compromise of a poorly formatted dialog? Why, yes, and yes again.

The Shortcut-wrapped script produced something like this...

Taming 1500 Safari Tabs

...which probably tempts you to make t00 many assumptions about my politics:

So that set me rolling, with a tool that took an hour to get from an idea to good-enough, closing and bookmarking and switching and sorting tabs, and from time to time hitting that Shortcut and seeing... the number of open tabs going down. Down! I could see the numbers of tabs for my top sites of awfulness roll themselves up like a woodlouse and vanish – pop – like a dull grey bubble. The numbers went down by tens, then hundreds. How satisfying.

Regular reports kept me going. Having the numbers meant I could come back after a break and still feel progress. I could open a few more tabs (because crap habits don't die easy), and not feel so hopeless. The situation that slowed down my (32GB M2Max) beast of a machine for six months turned from a problem into a story and a bit of code to share.

The important part of this tool was how it changed my output, not how it made the report. I needed the tool to motivate me to do the work, not to do all the work itself. The work would not have been done, by now, without it.

If Claude hadn't compressed half of humanity's code, I'd have either spent hours on the tool (unlikely) or spent yet more months glaring at the beachball. Here's the trivial script. It is not Claude's and it's not mine, but it stands on mighty shoulders.

function getTopHostnames(tabs, limit = 5) {
	function getHostame(urlString) {
		// need to use regex as JS for auto doesn't appear to have a URL object - could use const parsedUrl = new URL(url); const hostname = parsedUrl.hostname;
		const regex = /^https?:\/\/([^/?#]+)(?:[/?#]|$)/i;
		const matches = urlString.match(regex);
		const hostname = matches && matches[1];
		return hostname
	}
	
	const hostnameCount = tabs.reduce((acc, tab) => {
	const hostname = getHostame(tab.url);
		acc[hostname] = (acc[hostname] || 0) + 1;
		return acc;
	}, {});

	return Object.entries(hostnameCount)
	.sort((a, b) => b[1] - a[1])
	.slice(0, limit)
	.map(([hostname, count]) => ` ${count} of ${hostname}`);
}

function getListOfTabsSortedByURL(input, parameters) {
    var Safari = Application('Safari');
    var sys = Application('System Events');
    
    var tabInfo = [];
    var windowCount = 0;
    
    Safari.windows().forEach(function(window) {
        windowCount++;
        var tabCount = 0;
		//if (window.tabs() == null) {
		//	return("windows.tabs() == null")
		//}
        window.tabs().forEach(function(tab) {
            tabCount++;
            tabInfo.push({
                title: tab.name(),
                url: tab.url(),
                window: windowCount,
                tab: tabCount
            });
        });
    });
    
    // Sort tabInfo by URL
    tabInfo.sort(function(a, b) {
        return a.url.localeCompare(b.url);
    });
    
	return tabInfo;
	
}


function run() {
	const sortedTabs = getListOfTabsSortedByURL()
	const numTabs  = sortedTabs.length;
	const topSites = getTopHostnames(sortedTabs, 10 );
	return "You have "+numTabs+" tabs open. Common Sites: "+topSites;
}

run()

I have a Shortcut that picks up this script, uses the Run Shell Script action to call osascript to run it, and the Show action to pop it up where i can see it.

Taming 1500 Safari Tabs

So that's my tiny handy tool. I used it intensely for a weekend, and I'll probably forget about it in a week.

Have it if you want it. Use it but don't trust it. It's ephemeral, it's got no tests, it's a means to an end, it's yours. Better yet, make the tool to support the task that's bugging you, and tell the rest of us.