Set Up a New Mac (May 2025 Edition)

Previously: July 2019, July 2022, August 2023

  • Upgrade to latest MacOS
  • Restore from Blackblaze backup: u directory, dotfiles, .config dir, .cursor dir, .ssh dir, .vim dir, any other dot dirs that look useful, ~/Library/Application Support/Alfred
  • Set perms on .ssh
    $ cd ~ && chmod 600 ~/.ssh/* && chmod 700 ~/.ssh && chmod 644 ~/.ssh/*.pub
    
  • Hide Desktop icons
    $ defaults write com.apple.finder CreateDesktop false
    $ killall Finder
    
  • Show hidden files in Finder with Cmd-Shift-.
  • 1Password
  • Dock: remove everything, hiding on, magnification on
  • Alfred (and give it Accessibility access, add license key)
  • System Settings, Mouse, set Trackpad speed to max and enable right-click
  • Sublime Text 4 (add license key, set "trim_trailing_white_space_on_save": "all" in sublime prefs, install Package Control, install Nord Theme via Package Control)
  • subl commandline sublime.
    $ ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" ~/u/bin/subl
  • sublime git-ignorer. (1) add Package Control repo https://github.com/apc999/sublime-text-gitignore (2)
    install package sublime-text-gitignore (3) use menu item : File->Exclude Git-ignored (4) Give this a keyboard shortcut in Sublime, Preferences, Key Bindings with { "keys": ["super+shift+i"], "command": "exclude_ignored" }
  • Keyboard shortcut to toggle word wrap in Sublime Text. Preferences, Key Bindings { "keys": ["super+shift+w"], "command": "toggle_setting", "args": { "setting": "word_wrap" } }
  • Install FiraCode and add it to Sublime Text prefs with "font_face": "Fira Code".
  • Nord theme for Terminal and make default in preferences.
  • Polar theme for terminal when ssh’ing to remotes
  • For both Nord and Polar themes: “Use Option as Meta Key” in Terminal to make left Alt work as Esc. And Terminal, Settings, Advanced, un-check “Audible bell” and “Only when sound is muted”
  • System Preferences, Sound, turn “Alert volume” all the way down
  • Use Ctrl-f7 to enable keyboard navigation of dialogs
  • Homebrew. (Installs OSX commandline tools)
  • Discord
  • Change all .txt to open with Sublime Text
  • WhatsApp
  • Chrome. Sign in to Chrome to get extensions, then tell it you really do want uBlock Origin via “Keep for Now” under Extensions in Settings. Uninstall Chrome if they’ve finally locked it down entirely
  • Firefox. Sign in to sync extensions. Set as default browser
  • Skitch
  • Backblaze
  • Parallels Desktop + Windows + MTGO
  • Disable Ask Siri in System Preferences, Siri
  • Turn on “tap to click” in System Preferences, Trackpad
  • Disable long-press (it’s annoying when selecting text in Chrome): defaults write -g ApplePressAndHoldEnabled -bool false
  • Spotify
  • Slack
  • Rectangle Pro
  • DaisyDisk
  • ChatGPT
  • Claude
  • Linear
  • Zed
  • brew install nvim
  • brew install mariadb
  • brew install npm
  • brew install pnpm@9
  • brew install apache2.
  • brew install php. Enable in Apache with LoadModule, FilesMatch and DirectoryIndex directives as per homebrew output and brew services restart apache2.
  • brew install gh
  • brew install composer
  • brew install bat
  • brew install atuin
  • brew install delta
  • npm install -g rtm-cli
  • git clone important repos (ff, pd, server)
  • Restore any local databases for dev (ff, decksite, logsite)
  • ln -s ~/ff/src/www /opt/homebrew/var/www/ff
  • GRANT ALL ON ff.* TO ff@localhost IDENTIFIED BY *REDACTED*
  • pd setup
  • Check open but unsaved files in Sublime Text in case there’s anything you need to preserve
  • git status in any recent repos and check for uncommitted work.

zsh “shortcuts”

$ cat ~/.config/zsh/nav.zsh 
───────┬─────────────────────────────────────────────────────────────────────
       │ File: /Users/bakert/.config/zsh/nav.zsh
───────┼─────────────────────────────────────────────────────────────────────
   1   │ export MONOREPO_ROOT="/Users/bakert/cardeio-monorepo-v2"
   2   │ export ORGANIZED_PLAY="$MONOREPO_ROOT/apps/organized-play"
   3   │ export PLAY_NEXT="$MONOREPO_ROOT/apps/play-next"
   4   │ export LUDWIG="$MONOREPO_ROOT/apps/ludwig"
   5   │ export CARD_SEARCH_DEMO="$MONOREPO_ROOT/apps/card-search-demo"
   6   │ export DECK_BUILDER="$MONOREPO_ROOT/packages/deck-builder"
   7   │ 
   8   │ hash -d mr="$MONOREPO_ROOT"
   9   │ hash -d op="$ORGANIZED_PLAY"
  10   │ hash -d pn="$PLAY_NEXT"
  11   │ hash -d lw="$LUDWIG"
  12   │ hash -d cs="$CARD_SEARCH_DEMO"
  13   │ hash -d db="$DECK_BUILDER"
───────┴──────────────────────────────────────────────────────────────────────

Use prefixed with ~ anywhere you need a path:

$ cd ~mr
$ vi ~lw/src/index.ts

How to make Firefox auto-suggest “shortcut URLs”

I use both gmail.com (redirects to mail.google.com) and rmilk.com (redirects to rememberthemilk.com) all the time as shortcuts. Chrome “remembers” this and as soon as I type “gm” or “rm” it is auto-suggesting them and I just hit enter to get where I want to be.

I just switched back to Firefox (ublock origin forever!) and it massively down-rates URLs that redirect in its “what to auto-suggest” algorithm. You can tweak this, though, by putting `about: config` into the URL bar and setting `places.frecency.permRedirectVisitBonus` and `places.frecency.redirectSourceVisitBonus` to 2000 over their initial low values.

firefox-source-docs.mozilla.org/browser/urlbar/ranking.html has all the gory details and more.

Nord Light Theme

I like my terminal to change theme when I ssh into another machine. I have been using Solarized Light and Solarized Dark to facilitate this for some time. But now I have gotten hooked on Nord which has no light version. So I made one, just for Terminal for now at least.

Screenshot of Nord Light theme with colored ls output

There are notes about a “bright ambiance” version of the theme in the Nord docs but no actual themes available. The official Terminal.app theme actually deviates from the core 16-color palette but I didn’t do that here. I just chose the most appropriate color from the 16 color palette for each option in a Terminal theme.

Avoid trailing comma when emitting lists in Mustache templates

In pure Mustache there’s no (simple) way to avoid a trailing comma on a list:

{{#items}}{{.}}, {{/items}}

Which produces something like: “First, Second, Third, “

There are various ways around this. If you’re working in HTML I think the nicest is to model your list as an actual HTML list and use CSS:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Lists</title>
    <style>
      .list {           
        margin: 0;      
        padding: 0;
      }
      .list li {           
        display: inline;             
      }
      .list li:not(:last-child):after {
        content: ", ";               
      }
    </style>
  </head>
  <body>
    <ol class="list">
      <li>First</li>
      <li>Second</li>
      <li>Third</li>
    </ol>
  </body>
</html>

Which produces: “First, Second, Third”.

Python types for a decorator that is agnostic about the wrapped func’s return type


import random
from typing import Callable, ParamSpec, TypeVar

T = TypeVar("T")
P = ParamSpec("P")

def some_global_condition() -> bool:
return random.choice([True, False])

def my_return_type_preserving_decorator(val: T) -> Callable[[Callable[P, T]], Callable[P, T]]:
def decorator(func: Callable[P, T]) -> Callable[P, T]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
if some_global_condition():
return val
return func(*args, **kwargs)
return wrapper
return decorator

@my_return_type_preserving_decorator((True, True, True))
def my_func(n: int) -> tuple[bool, bool, bool]:
return n > 10, n > 100, n > 1000

x = my_func(5)
for r in x:
print("yes" if r else "no")

Format Percentage for Display in JavaScript

Input Output
0.12 12%
0.0012345 0.12%
0.0 0

2 significant digits but locale-sensitive, strip meaningless trailing zeroes and decimal dividers.

const formatPercentage = function (value) {
    return new Intl.NumberFormat(
        undefined, // use browser locale
        // Choose the options that get us closest to our desired style – a percentage that tells you enough to be useful but no unnecessary decimal places or trailing zeroes.
        {
            style: "percent", // Treat decimals as percentages, multiplying by 100 and adding a % sign.
            minimumSignificantDigits: 2,
            maximumSignificantDigits: 2
        }
    )
        .format(value)
        // \D here is the locale-agnostic decimals separator.
        .replace(/(\D[0-9]*?)0+%$/, "$1%") // Get rid of trailing zeroes after the decimal separator.
        .replace(/\D%/, "%") // Clean up the scenario where we got rid of everything after the decimal separator and now have something like "4.%.
        .replace(/^0%$/, "0"); // Replace literal "0%" with "0" as zero is unitless.
};