Help: Close other windows except the active one

Heya!

Framework: I use safari a lot with multiple windows but the ram slows down. So, how to close all the "background windows" except the current active one?

Ex here: current one is the KM forum, I would want to close the other ones with a shortcut.

(Tiny tip: If you got multiple tabs within a safari window and would want to close the other tabs except your current one, the default shortcut from Safari is option + command + w).

Also, is there a way to do this with other applications (ex: multiple pdfs)? Saw some applescript but for pdfs - skim (?). I mainly use Preview for pdfs

I believe for any AppleScriptable app, you can just use a variation on Chris' AppleScript:

tell application "Safari"
	set docList to documents
	if length of docList > 1 then
		repeat with i from 1 to length of docList
			if i > 1 then
				close (item i of docList)
			end if
		end repeat
	end if
end tell

That works here in testing. However, you can also do this with native Keyboard Maestro actions if you prefer; here's an example for Safari:

Download Macro(s): Close non-frontmost Safari windows.kmmacros (6.7 KB)

Macro screenshot

Macro notes
  • Macros are always disabled when imported into the Keyboard Maestro Editor.
    • The user must ensure the macro is enabled.
    • The user must also ensure the macro's parent macro-group is enabled.
System information
  • macOS 26.6.1
  • Keyboard Maestro v11.1.1

Note that this was just the first method that came to mind—there are likely simpler, faster, and/or better ways to do this.

-rob.

tysm Rob! Yay :head_shaking_vertically:

My head went straight to:

That's the whole macro. No check needed -- if WINDOWCOUNT() is 1 or 0 the "For Each" will silently fail because a valid range can't be created.

Presumably also something like:

tell application "Safari" to close (windows where its index > 1)

See, I knew there were shorter and better options! :slight_smile:

-rob.

The KM action approach shown by @Nige_S has two advantages:

  1. We don't need to specify the name of the application, and
  2. it covers the case of Electron apps like Visual Studio Code, which lack an osascript interface.

( Application names can be generalised in JXA – in contrast with AppleScript, JS application names are late bound, i.e. they don't have to be fixed at compile time, and the lack of an osascript library could be covered by falling back to System Events, but by then the JS is getting verbose, and the KM actions are much the better choice)

FWIW the JS might have the rough shape of something like:

Expand disclosure triangle to view JS source
(() => {
    ObjC.import('AppKit');

    const
        frontAppName = ObjC.unwrap(
            $.NSWorkspace.sharedWorkspace.frontmostApplication.localizedName
        ),
        app = Application(frontAppName);

    try {
        const
            wins = app.windows,
            otherWindows = wins.where({ index: { '>': 1 } }),
            n = otherWindows.length;

        return (
            app.close(otherWindows),
            `${n} other windows of ${frontAppName} closed.`
        );

    } catch (e) {
        const
            frontProc = Application("System Events").processes
                .whose({ frontmost: true }).at(0),
            [nClosed, nOther] = Array.from(frontProc.windows().slice(1))
                .reduce(
                    ([yes, no], win) => {
                        const
                            closeButton = win.buttons
                                .whose({ subrole: 'AXCloseButton' }).at(0);

                        return closeButton.exists()
                            ? (
                                closeButton.click(),
                                [1 + yes, no]
                            )
                            : [yes, 1 + no]
                    },
                    [0, 0]
                );

        return `${nClosed} window(s) of ${frontAppName} closed.` + (
            0 < nOther
                ? ` ${nOther} windows unclosable.`
                : ""
        );
    };
})();

It should also be slightly quicker, at least until you get into the 20-30 windows range (2ms per KM loop, 40-60ms to instantiate the AS depending on CPU speed).

The AS approach will be more robust in cases where windows get re-ordered during execution, since it closes by window reference rather than index number.

You could probably create a generalised AS script using System Events to get the frontmost app then targeting it directly, and if that throws an error (no AS interface) falling back to System Events to do the window closing as you've described above.

Cannot reproduce this macro,

WINDOWCOUNT () ?

/okn

Just type WINDOWCOUNT() into that box where the '1' appears.

-rob.