Skip to content
wildeyes edited this page Apr 11, 2022 · 42 revisions

Table of Contents

General options

module.exports = {
  defaultBrowser: "Google Chrome",
  options: {
    // Hide the finicky icon from the top bar. Default: false
    hideIcon: false, 
    // Check for update on startup. Default: true
    checkForUpdate: true,
    // Change the internal list of url shortener services. Default: undefined
    urlShorteners: (list) => [...list, "custom.urlshortener.com"],
    // Log every request with basic information to console. Default: false
    logRequests: false
  },
};

The default list of short url providers can be found here. Finicky uses this list to resolve urls to the intended destination before running them through the rule system.

Matching urls

module.exports = {
  defaultBrowser: "Google Chrome",
  rewrite: [
    {
      match: "example.org/*",
      url: "http://example.com",
    },
  ],
  handlers: [
    {
      match: "apple.com/*",
      browser: "Safari",
    },
  ],
};

Examples

A handler or url rewrite always needs a matcher to match the incoming url. There are a couple of different ways to match a url:

  // Only matches that exact url
  match: "http://example.com/path?query" 
  // Matches anything the Regular Expression matches
  match: /^https?:\/\/example\.com/.*$/ 
  // A function that accepts some arguments. 
  match: ({ url }) => url.host === "example.com"
  // Or, an array with any combination of the above
  match: ["https://example.com", /^http:\/\/example.(org|com)\/.*/]

The full parameter description for functions is available here

Selecting a browser

module.exports = {
  defaultBrowser: "Safari",
  handlers: [
    {
      match: ({ url }) => url.host.endsWith("example.org"),
      browser: "Firefox",
    },
  ],
};

1. Browser (or app) as a string:

  // Name
  browser: "Google Chrome" 
  // Bundle identifier (useful when the app name isn't unique)
  browser: "com.google.Chrome" 
  // Path to application. Useful when both name and bundle id aren't unique
  browser: "/Applications/Firefox.app" 

Finicky will try detect automatically if what was entered was a name, a bundle id or a path name. If you don't know what bundle id to use, see this wiki entry

2. You can define more options with a browser object.

If you want to force the string type you can supply it in a browser object:

  browser: {
    name: "Google Chrome",
    appType: "appName" // Force name type. "appName", "bundleId" or "appPath",
    openInBackground: true // or false, force opening the browser in the background
  }

From Finicky 3.2, you can specify a profile to activate when using Chrome, Brave, Edge or Vivaldi as your browser.

{
  match: "example.com/*",
  browser: { 
    name: "Google Chrome",
    profile: "Default", 
  }
},
{      
  match: "example.org/*",
  browser: { 
    name: "Google Chrome",
    profile: "Profile 1", 
  }
}

The profile name you supply to Finicky must match the folder name of the profile. For Chrome, the folders are located at ~/Library/Application Support/Google/Chrome. Folder names are called "Profile 1", "Profile 2", etc.

⚠️ Please note ⚠️ If you supply a folder name of a profile that does not exist, e.g. "Avocado", Chrome and Brave will create a new profile using that folder name. This appears to work pretty well, but I would advise against this since there is a risk corrupt your Chrome configuration. In the future, I want to add some verification to avoid this possibility, so it might stop working.

3. A browser function that returns either a browser string or object

  browser: ({ url }) => url.host === "example.com" ? "Google Chrome" : "Firefox"
  browser: ({ urlString }) => {
    name: "Google Chrome",
    openInBackground: urlString.includes("facebook")
  }

Rewriting urls

If you want to change the incoming url in any way, you can do that in a couple of different ways.

module.exports = {
  defaultBrowser: "Safari",
  rewrite: [
    {
      match: ({ url }) => url.host.endsWith("example.org"),
      url: "https://gitlab.com",
    },
  ],
};

The url property can either be:

  // A string for the a completely new url
  url: "http://example.com?something=else"
  // A URL Object
  url: {
    "host": "example.com",
    "protocol": "https"
  }
  // A function that returns either a url string or a url object
  url: ({ url }) => {
    return {
      ...url,
      protocol: "https"
    }
  }

Parameters

Matcher, browser and url function callback all accept the same options object as the only argument. Below is the full object including a description of the parts.

// Options object:
// Available as the first parameter when using match, browser or url functions.

{
  "urlString": "http://username:password@example.com:3000/pathname/?search=test#hash", // The full URL string
  "url": { // The URL parsed into parts
    "username": "username",
    "host": "example.com",
    "protocol": "http",
    "pathname": "/pathname/",
    "search": "search=test",
    "password": "password",
    "port": 3000,
    "hash": "hash"
  },
  "opener": {
    "pid": 1337,
    "path": "/Users/user/Applications/Example.app",
    "bundleId": "com.example.app",
    "name": "Example app"
  },
  "keys": { // DEPRECATED, replaced by finicky.getKeys – Status of modifier keys on keyboard. 
    "control": false,
    "function": false,
    "shift": false,
    "option": false,
    "command": false,
    "capsLock": false
  },
  "sourceBundleIdentifier": "net.kassett.finicky", // DEPRECATED, use opener.bundleId instead
  "sourceProcessPath": "/Applications/Finicky.app" // DEPRECATED, use opener.path instead
}

Example configuration

module.exports = {
  defaultBrowser: "Google Chrome",
  options: {
    // Hide the finicky icon from the top bar
    hideIcon: true
  },
  handlers: [
    {
      // Open any link clicked in Slack in Safari
      match: ({ opener }) =>
        opener.bundleId === "com.tinyspeck.slackmacgap",
      browser: "Safari"
    },
    {
      // You can get the path of the process that triggered Finicky (EXPERIMENTAL)
      match: ({ opener }) =>
        opener.path && opener.path.startsWith("/Applications/Slack.app"),
      browser: "Firefox"
    },
    {
      match: ["http://zombo.com"],
      browser: {
        name: "Google Chrome Canary",
        // Force opening the link in the background
        openInBackground: true
      }
    },
    {
      match: finicky.matchHostnames(["example.com"]),
      // Opens the first running browsers in the list. If none are running, the first one will be started.
      browser: ["Google Chrome", "Safari", "Firefox"]
    },
    {
      match: ["http://example.com"],
      // Don't open any browser for this url, effectively blocking it
      browser: null
    },
    {
      // Open links in Safari when the option key is pressed
      // Valid keys are: shift, option, command, control, capsLock, and function.
      // Please note that control usually opens a tooltip menu instead of visiting a link
      match: () => finicky.getKeys().option,
      browser: "Safari"
    }
  ]
};

API specification

finicky.log(message: string)

Logs a string to the finicky console

module.exports = {
  defaultBrowser: "Google Chrome",
  handlers: [{
    match: ({ url, urlString }) => {
      finicky.log("Received URL " + urlString);
      return url.host === "apple.com";
    }
    browser: "Safari"
  }]
}

finicky.notify(title: string, subtitle: string)

Display a notification

module.exports = {
  defaultBrowser: "Google Chrome",
  handlers: [{
    match: ({ url, urlString }) => {
      finicky.notify("Received URL", urlString);
      return url.host === "apple.com";
    }
    browser: "Safari"
  }]
}

finicky.getBattery() => { isCharging: bool, isPluggedIn: bool, chargePercentage: number }

Get the battery status.

module.exports = {
  // Use Safari as default when battery is low
  defaultBrowser: () => finicky.getBattery().chargePercentage < 50 ? "Safari" : "Google Chrome",
}

finicky.getKeys() => { key: bool }

Get the pressed status of keyboard modifier keys. Available keys are "control", "function", "shift", "option", "command", "capsLock"

if (finicky.getKeys().command) { 
 finicky.log("Command is pressed")
}

finicky.getSystemInfo() => { localizedName: string, name: string }

Get system information

finicky.getSystemInfo() // returns {"localizedName":"John’s Mac mini","name":"Johns-Mac-mini.local"}

finicky.matchHostnames(matchers: string | RegExp | (string | RegExp)[]) => bool

Utility function to make it easier to match on the domain/hostnames part

module.exports = {
  defaultBrowser: "Google Chrome",
  handlers: [{
    match: finicky.matchHostnames(["apple.com", /example\.(com|org|net)/]),
    browser: "Safari"
  }]
}