Macro to quickly close all safari tabs except pinned and current one

I am wondering whether such a thing be possible to do quickly. With just a press of a hotkey, all the current tabs that are open except the one activated now and the pinned tabs would close. It would save me quite some time.

Right now the solution I can see is getting current url of my current tab, close the safari window, open it again and paste it in but that is really undesirable as first it takes time to close and then open the safari window and secondly I just feel there would be a more elegant solution than this.

Essentially I want to go from this :

to this as quick as possible :

I would appreciate any help on this. Thank you.

You mean, like this context menu you get by right-clicking on one of the tabs?

:slight_smile:

1 Like

Yep, now I just need to do it from a hotkey if I can :slight_smile:

There doesn’t seem to be a native hotkey for it but maybe I haven’t looked good enough.

If not could I replicate this behaviour with some magic KM macro?

Here’s a start. It should close all tabs except the current one (but see notes below):

activate application "Safari"
tell application "Safari"
	set currentWindow to window 0
	set tabCount to count of tabs of currentWindow
	repeat with i from tabCount - 1 to 0 by -1
		if index of tab i of currentWindow is not equal to index of current tab of currentWindow then
			close tab i of currentWindow
		end if
	end repeat
end tell
  • If you have more than one window open, you might need to change the “set currentWindow” - probably not but I didn’t test it.
  • Not sure about pinned tabs.
2 Likes

If you need to know how to use this in KM, just ask. I’m researching pinned tabs…

Just ran your script and it really is a great start, however it does close all my pinned tabs too. I hope there is a way to keep them open. You guys are really amazing with how fast you can think of solutions like this.

OK, I think this works, but see notes below:

Safari - Close all but current and pinned tabs.kmmacros (2.8 KB)

In order to determine if a tab is pinned or not, I had to resort to checking to see if the "Unpin Tab" menu item exists for that tab. So that means I had to switch to each tab, and wait until the tab is actually loaded before checking the menu item. Which slows down the macro drastically.

So if you look in the script, you'll see "delay 0.5". It means "delay a half second". You may need to adjust this higher ("delay 0.7", "delay 1.0", etc) to get it to work consistently. So if you notice pinned tabs being closed, adjust this value.

There may be other ways of doing this. Maybe someone else will jump in with a better way that doesn't require delays.

1 Like

Thank you a lot Dan, this is indeed a solution however it does take unbearably long time especially given that my use case with this macro will be when I have 10+ tabs open. I will be trying to use this as an alternative for now so thank you a lot for taking the time and writing it out. I really appreciate it.

The first solution is so fast and smooth that it gives me hope there is a chance that something like this can be done with the pinned tabs staying intact.

Sure hope you're on Yosemite or better better yet, El Capitan. Assuming you are, try this:

Safari - Close all but current and pinned tabs - JXA version.kmmacros (3.0 KB)

I think this will work, although it's possible I have a bug or two in it, time will tell.

I made a lot of assumptions, not all of them thrill me, but it's a start. Assumptions:

  1. Either you only have one Safari window open, or the API method "windows[0]" always returns the front window.
  2. You don't have more than one tab open with the same name. If this is not true, it will either keep both of them open, or close both of them, depending on the situation.
  3. A bunch of other assumptions that are probably correct, but you know what they say about assumptions...

Let me know. If it works, it's plenty fast, as in almost instantaneous.

2 Likes

Oh wow,

This works just as I wanted it too. All of your assumptions are correct and this works exactly like the native 'close other tabs' does and just as fast :slight_smile:

The only thing is that I get this message whenever there are two tabs of same name open :

Instead of it closing both of them or leaving them open. Thank you so much Dan.

Also remember this post that you helped me with? I want make it so that this modified clipboard from my 2Do app will get piped to Bitbar or something alike so that I can then display the task in my menu bar. I was just wondering, would such a thing be possible to do? I remember seeing Bitbar menu bar icon from one of your screenshots so I presume you are familiar with it.

This would indeed be really nice to make as I will be able to see a current task I want to work on in my menu bar and keep focused on the work. Essentially I just want to display text that will get piped into it from the content of the clipboard. What do you think of it? Would it be hard to do you think?

No problem. The knowledge I learn from things like this is worth the price of my time, believe me.

Here’s some code to try. It’s not the most elegant solution, but it might work. Paste this into the JavaScript action in the macro:

(function() {
    'use strict';

    function getPinnedTabs() {
        var _safariProc = Application("System Events").processes["Safari"];
        var _window = _safariProc.windows[0];
        var _tabGroup = _window.tabGroups[0];

        var _result = [];
        try {
            for (var i = 0; i < _tabGroup.radioButtons.length; i++) {
                var _radioButton = _tabGroup.radioButtons[i];
                if (_radioButton.roleDescription() === "pinned tab") {
                    _result.push(_radioButton.description());
                }
            };
        } catch (e) {}

        return _result;
    }

    function processTabs(pinnedTabs) {
        var _safari = Application("Safari");
        var _window = _safari.windows[0];
        var _currentTabName = _window.currentTab().name();
        for (var i = _window.tabs.length - 1; i >= 0; i--) {
            var _tab = _window.tabs[i];
            var _tabName = _tab.name();
            if (_tabName !== _currentTabName &&
                _pinnedTabs.findIndex(function(item) {
                    return item == _tabName;
                }) < 0) {
                _tab.close();
            };
        }
    }

    Application("Safari").activate();

    var _pinnedTabs = getPinnedTabs();
    processTabs(_pinnedTabs);
})()

There’s no need for a KM macro. Option+Cmd+W should already be wired to Safari’s Close other Tabs function.

3 Likes

Well, that was news that would have been useful hours ago! :stuck_out_tongue:

1 Like

The basics of the normal AppleScript method for dealing with this (Dan did it in JXA).

I’m not bothering to retain the current tab, since there’s a keyboard shortcut for that.

-Chris

--------------------------------------------------------------------------------
tell application "System Events"
   tell application process "Safari"
      tell front window
         tell tab group 1
            set pinnedTabList to name of radio buttons whose role description is "pinned tab"
         end tell
      end tell
   end tell
end tell

tell application "Safari"
   tell front window
      (name of tabs)
      repeat with theTab in (get name of tabs)
         if theTab is not in pinnedTabList then
            close tab theTab
         end if
      end repeat
   end tell
end tell
--------------------------------------------------------------------------------

Just came across this thread. I’m not sure if this is a new hotkey but if you press OPTION-COMMAND-W it closes all tabs but the current. Easy to script in KM.

1 Like

While that may work in Safari, in Chrome it closes ALL tabs.