A tabbed terminal scratchpad. Each tab is a quick note; each workspace is a named set of tabs. Built with Go + Bubble Tea.
Notes are plain Markdown files on disk, so they stay greppable and editable by hand. By default they live under your XDG data dir β but the storage location and other behavior are fully configurable (see Configuration).
Features: tabbed notes Β· named workspaces Β· fuzzy find across tabs (^f) Β· live Markdown preview (^o) Β· reorder tabs Β· atomic saves Β· plain-Markdown storage Β· --print for scripting Β· config via flags, env, or file.
A workspace is just a named drawer of tabs. Think of it as one project's scratch area.
- Open
padwith no argument β thedefaultworkspace. - Open
pad ideasβ a workspace calledideas(created on first use). - Each workspace has its own independent set of tabs. Switching workspaces swaps the whole tab bar.
Use them however you like: one per project, one per context (work, personal, meeting), or just live in default forever. You don't have to think about workspaces at all if you don't want to β the default one is always there.
<data-dir>/
βββ default/
β βββ 1-untitled.md
β βββ 2-todo.md
βββ ideas/
βββ 1-feature-brainstorm.md
| Platform | Recommended |
|---|---|
| Linux | install script or prebuilt binary |
| macOS | Homebrew or install script |
| Any (with Go) | from source |
Downloads the right binary for your OS/arch, verifies its checksum, and installs it:
curl -fsSL https://raw.githubusercontent.com/ianaya89/scratchpad/main/install.sh | shInstalls to ~/.local/bin by default. Override with env vars:
PAD_INSTALL_DIR=/usr/local/bin PAD_VERSION=v0.1.0 \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ianaya89/scratchpad/main/install.sh)"brew install ianaya89/tap/padThe Homebrew cask is macOS-only. On Linux, use the install script or a prebuilt binary.
Grab a tarball for your OS/arch from the releases page, then:
tar -xzf pad_*_linux_amd64.tar.gz
install -m 0755 pad ~/.local/bin/padgit clone https://github.com/ianaya89/scratchpad ~/pad
cd ~/pad
make install # builds with version info into ~/.local/bin/pad
# or: go build -o ~/.local/bin/pad .Ensure ~/.local/bin is on your PATH (or set PREFIX=/usr/local make install).
If your Go toolchain can't reach the checksum database (sandboxed/offline), prefix builds with
GOSUMDB=off.
pad # open the "default" workspace
pad ideas # open/create the "ideas" workspace
pad --dir ~/notes # store notes under ~/notes instead of the default
pad --print # dump the workspace's notes to stdout (no TUI)
pad --print --tab todo
pad --new "meeting" --content "kickoff notes" # create a note, no TUI
echo "remember this" | pad --append todo # append to a note from stdin
pad --help # full flag reference# create a note (content from --content or stdin)
pad --new "Standup" --content "- shipped X"
git log --oneline -5 | pad --new "recent commits"
# append to a note (matched by title; created if absent)
echo "buy milk" | pad --append shopping
# read notes back out
pad --print --tab standup
# list workspaces, or tabs of a workspace
pad --list
pad --list work
padreopens each workspace on the tab you last used. The footer shows a live word/char count while editing.
| Key | Action |
|---|---|
^t |
New tab |
^d (or ^w) |
Delete current note (asks to confirm) |
^x |
Toggle a task checkbox (- [ ] β - [x]) on the current line |
^z / ^y |
Undo / redo (per tab) |
^n |
Next tab |
^p |
Previous tab |
alt+1β¦alt+9 |
Jump straight to tab N |
^β / ^β |
Move current tab right / left |
^f |
Find across tabs (title + body) in the workspace |
^o |
Full-screen Markdown preview of the current note |
^b |
Toggle live split preview (source left, rendered right) |
^r |
Rename current tab |
^e |
Workspace picker (switch, or n to create new) |
^/ (or F1) |
Help overlay |
^s |
Save now |
^q (or ^c) |
Save everything and quit |
When tabs exceed the terminal width, the bar scrolls to keep the active tab
visible and shows βΉN / NβΊ counters for hidden tabs on each side.
Live split preview (^b) splits the screen β you keep editing the raw
Markdown on the left while a Glamour-rendered
view updates on the right as you type (the editor stays visible; it isn't hidden).
^o is the full-screen rendered view. Rendering is debounced (~120 ms after you
stop typing) and the renderer is cached, so typing stays responsive.
If you run pad inside tmux,
^bis tmux's prefix key β press it twice, or remap tmux's prefix, to reach the split toggle.
Fallback keys: switch with alt+h/alt+l or shift+β/shift+β; move with alt+H/alt+L.
Terminal note:
^p/^n(plainctrl+letter) are delivered by essentially every terminal. They replacectrl+n/ctrl+pline movement inside a note β use the arrow keys for that.ctrl-arrows are also bound but some terminals grab them for pane navigation.
Everything resolves with the precedence flag β environment variable β config file β default.
| Flag | Env var | Default | Description |
|---|---|---|---|
--dir PATH |
PAD_DIR |
$XDG_DATA_HOME/pad, else ~/.local/share/pad |
Where notes are stored. ~ is expanded. |
--workspace NAME (or positional arg) |
PAD_WORKSPACE |
default |
Workspace to open. |
--autosave N |
PAD_AUTOSAVE |
3 |
Autosave interval in seconds (accepts 5 or 5s). 0 disables periodic autosave. |
--config PATH |
PAD_CONFIG |
$XDG_CONFIG_HOME/pad/config.toml |
Config file location. |
| β | PAD_THEME |
dark |
Markdown preview theme (dark, light, dracula, tokyo-night, pink, nottyβ¦). |
--print / --tab TITLE |
β | β | Dump notes to stdout and exit (no TUI). |
--new TITLE / --append TITLE / --content TEXT |
β | β | Create or append to a note from CLI/stdin and exit (no TUI). |
--version |
β | β | Print version and exit. |
A simple key = value file (TOML-ish). Default location ~/.config/pad/config.toml:
dir = "~/notes"
workspace = "work"
autosave = 5Set defaults once in your shell profile, e.g.:
# put notes wherever you want
export PAD_DIR="$HOME/Documents/scratch"
export PAD_WORKSPACE="work"
export PAD_AUTOSAVE="5"For example, to keep notes inside an existing nb setup:
export PAD_DIR="$HOME/.nb/scratchpad"- Storage β each tab is a file
<data-dir>/<workspace>/NN-slug.md. The numeric prefixNNfixes tab order; the slug is derived from the tab title. - Saving β autosaves on the configured interval, and also on tab switch, rename, workspace switch, and quit. A
β’next to a tab title means it has unsaved changes in the buffer. - Renaming β renames the underlying file (keeps its numeric prefix, so order is preserved).
- Deleting β removes the file from disk after a
y/nconfirm. If you delete the last tab, a fresh emptyuntitledtab is created so there's always somewhere to type.
pad/
βββ model.go # model, tab/workspace ops, save/undo/redo, checkpoints
βββ update.go # Init/Update loop and all key handlers
βββ markdown.go # Glamour preview + split rendering, layout sizing
βββ view.go # rendering (tab bar, footer, overlays)
βββ store.go # file storage: workspaces, notes, slugs, paths
βββ config.go # flag/env/file resolution
βββ cli.go # main(); non-TUI runners (--print/--list/--new/--append)
βββ *_test.go # unit tests
βββ go.mod
make build # build ./pad with version stamped from git
make install # build into $PREFIX/bin (default ~/.local)
make test # go test ./...
make check # fmt + vet + test
make demo # re-render demo.gif (needs vhs)CI (build, vet, gofmt, race tests) runs on every push and PR.
Tag and push; the release workflow runs GoReleaser to
build cross-platform binaries, publish a GitHub release, and update the Homebrew
cask in ianaya89/homebrew-tap:
git tag v0.1.0
git push origin v0.1.0The binary's --version is stamped from the tag. Update CHANGELOG.md before tagging a release.
One-time setup for the Homebrew step β the default GITHUB_TOKEN can't write
to the tap repo, so add a Personal Access Token with write access to it:
# create a fine-grained PAT with "Contents: read & write" on ianaya89/homebrew-tap,
# then store it as a secret on this repo:
gh secret set HOMEBREW_TAP_TOKEN --repo ianaya89/scratchpadWithout that secret the release still succeeds, but the cask update step fails.
MIT
