How I Navigate Hundreds of Tabs on Chrome with JXA and Alfred

Using fuzzy search via fzf in iTerm2
Insane permissions needed by Chrome extensions these days

Open source doesn’t mean safe when you’re too lazy to inspect the code.

Choosing the Right Tool for the Job

  • Writing a Chrome Extension: I didn’t want to write a Chrome extension because extensions are only useful when the browser is focused. This is bad because when I’m focused on another window, such as my terminal, and I want to jump to a specific tab, I’d have to open Chrome then start using my extension. Too much work.
  • Using AppleScript: AppleScript is a language used for controlling applications with Apple Events. It looked promising at first until I played with some examples. Using languages like C, C++, Python, Java, Go, and JavaScript my entire career, I instantly felt out of my comfort zone and decided to get back to this if I couldn’t find a better alternative.
  • Using Python: I was pretty excited about learning that there are some libraries that you can use to talk to MacOS. They ended up being super outdated with no proper documentation. So unfortunately this was a no-go situation for me, since I wanted to move fast.

What is JXA?

I still don’t know what the X stands for. Perhaps MacOS X? ¯\_(ツ)_/¯

Getting Started With JXA

  • Create your file with a regular js extension. (e.g. script.js)
  • Paste this line as the first line in your file:
    #!/usr/bin/env osascript -l JavaScript
  • Make your script executable:
    chmod +x ./script.js
  • And simply run it with:
console.log("Hello multiverse 👽")

Connecting to Chrome

Opening a dictionary in Script Editor
Opening Google Chrome dictionary in Script Editor
Google Chrome Dictionary in Script Editor
const chrome = Application('Google Chrome')
chrome.includeStandardAdditions = true, winIdx) => {
window.tabs().forEach((tab, tabIdx) => {
console.log(tab.title(), tab.url())
})[winIdx].tabs[tabIdx].close()[winIdx].visible = true[winIdx].activeTabIndex = tabIdx[winIdx].index = 1
const print = function (msg) {
const input = function (msg) {
return $.NSString.alloc.initWithDataEncoding(

A New Project is Born: Chrome Control

Chrome Control in iTerm2

Using Chrome Control with Alfred

Creating a new workflow
"items": [
"title": "Inbox (1) - <hidden> - Gmail",
"url": "",
"winIdx": 0,
"tabIdx": 0,
"arg": "0,0",
"subtitle": ""
"title": "iPhone - Apple",
"url": "",
"winIdx": 0,
"tabIdx": 1,
"arg": "0,1",
"subtitle": ""
Adding a new Script Filter to the workflow
Setting up the Script Filter
Chrome Control on Alfred
Creating a Hotkey Trigger
Setting up the Hotkey Trigger
Connecting the Hotkey Trigger to the Script Filter

Focusing on a Selected Tab

Connecting the Script Filter output to a Run Script action
Configuring the Script Filter to run the `focus` command when an item is selected
Connecting the tabs command to the Chrome Control close command
Opening the connection configuration
Setting up the alt key behavior
Holding `alt` and hitting enter will close this tab

Deduping Open Tabs

Deduping tabs with Chrome Control
Connecting the dedup command to Chrome Control
Chrome asking me if I’m sure about closing the duplicate tabs
chrome.includeStandardAdditions = true

Closing Tabs by Keywords

Closing tabs by title
Closing tabs by URL
Connecting the `Close URL` command with Alfred
Setting up the `close url` command on Alfred
Connecting the `close url` keyword to Chrome Control
Closing tabs with URLs that contain either `apple` or `doc`
  • alt + t List all tabs
  • alt + d Dedup tabs
  • alt + c Close tabs by URL
  • alt + shift + c Close tabs by title
Chrome Control workflow on Alfred

Source Code and Chrome Control Alfred Workflow




Get the Medium app