CLI/TUI scratchpad for daily notes, todos, and quick thoughts. Built with Go and the charm.sh toolchain.
- Daily scratchpad: a fresh page per day, kept on disk forever
- Three view modes that drill into one another:
- Year / month calendar with per-day previews and entry sparklines
- Notebook viewer with glamour-rendered markdown, scrubbable across pages
- External editor (
$EDITOR) for actually writing
- One semantic rule across the TUI: Enter goes deeper
- Inline editing: hand the editor to
tea.ExecProcess, save on exit, resume the same view with a refreshed render — no shell hop between edits - Light / dark palette swap driven by your terminal:
Ctrl+Ttoggles in-session,SIGUSR1re-detects, macOS appearance changes are picked up automatically via an fsnotify watcher on the global preferences plist - Glyph set choice: nerd-font icons or pure-ASCII fallback, set in
~/.sp/config.toml - Opt-in day templates: append one or more named Markdown sections from files or script output, with a built-in workday timeboxing helper
brew tap pders01/sp
brew install spRequires Go 1.24 or later.
git clone https://github.com/pders01/sp.git
cd sp
make install # → $GOPATH/bin/spOr manually:
go build -o sp ./cmd/spsp # open today's page in $EDITOR
sp -n # notebook viewer; Enter / e / i opens the editor
sp -c # calendar; Enter drills into the notebook on that day
sp --versionsp -c → calendar ──Enter──→ notebook ──Enter/e/i──→ editor
▲ ▲ │ │
│ └────── Esc ──────────┘ │
│ │
└─────── editor exits, view resumes ───────────────┘
(Calendar 'e' is a power-user shortcut: skip the notebook
and edit the picked day immediately.)
After the editor saves and exits, the active view repaints with the
updated content. Pop from notebook back to calendar with Esc/
Backspace; the calendar cursor follows wherever you were in the
notebook.
| Key | Action |
|---|---|
←/h →/l |
day (month view) / month (year view) |
↑/k ↓/j |
week (month view) / row (year view) |
H L |
jump month / year |
Enter |
drill into the notebook on that day |
e i |
edit the day immediately (month view) |
a |
choose template sections for the day |
m y |
switch to month / year view |
t |
reset cursor to today |
Ctrl+T |
cycle theme: auto → light → dark |
q Ctrl+C Esc |
quit |
| Key | Action |
|---|---|
←/h →/l |
previous / next page |
↑/k ↓/j |
scroll content |
Ctrl+u Ctrl+d |
half-page up / down |
b f pgup/pgdn |
full-page up / down |
g G |
jump to top / bottom |
Enter e i |
edit current page |
a |
choose template sections |
Esc Backspace |
pop back to calendar (when -c) |
Ctrl+T |
cycle theme |
q Ctrl+C |
quit |
Optional TOML at ~/.sp/config.toml. Missing fields fall back to the
defaults shown in config.example.toml:
[ui]
icons = "unicode" # or "nerd" if you have a Nerd Font installed
theme = "auto" # "auto" | "light" | "dark"
[templates]
allow_commands = false
[[templates.items]]
name = "Meeting notes"
file = "~/.sp/templates/meeting.md"
# Requires allow_commands = true above.
[[templates.items]]
name = "Issue tracker"
command = ["/path/to/issue-template", "--markdown"]auto resolves via GLAMOUR_STYLE → COLORFGBG → terminal
detection → macOS AppleInterfaceStyle. Send SIGUSR1 (pkill -USR1 sp) to re-detect after a manual switch.
Press a on a day to open the multi-select template chooser. Markdown files
and command stdout become separate ## sections; commands receive the selected
date in SP_DATE. Relative Markdown file paths resolve from the directory that
contains config.toml. Existing content is only appended to, never replaced.
Applied template IDs are stored in scratchpad JSON metadata to prevent duplicate
application without adding markers to the Markdown. Select an already-applied
template again to force a reapply—for example, after manually removing its
section. The built-in Workday timebox template remains opt-in like every
configured template.
Security: Command templates are disabled unless
templates.allow_commands = true. Enabling them runs explicitly configured programs with your user account's filesystem and network permissions. They receive a minimal environment and are limited to 10 seconds and 1 MiB of output, but they are not sandboxed and can still read or modify~/.sp. Only configure commands you fully trust.
sp resolves the editor via $EDITOR, then $VISUAL, then
platform-specific fallbacks (vim, nvim, nano, micro, emacs, code,
etc.).
export EDITOR=vim
export EDITOR="code --wait"Caveat: GUI editors that fork-and-detach (VS Code without
--wait, Sublime, Atom) will return immediately when invoked from
inside the TUI and look broken — tea.ExecProcess waits on the
launched process. For those, stick with bare sp (the synchronous
path keeps the original GUI polling loop).
Scratchpads live in ~/.sp/<YYYY-MM-DD>.json. Each file holds the
date, content (raw markdown), applied-template metadata, and creation / modified
timestamps.
sp/
├── cmd/sp/ main.go Cobra entry point
├── internal/
│ ├── config/ config.go TOML loader
│ ├── editor/ editor.go editor resolution + Prepare/Edit
│ ├── scratchpad/ scratchpad.go JSON store, ListDates, Save/Load
│ └── tui/
│ ├── app.go router model: calendar ↔ notebook ↔ editor
│ ├── calendar.go full-screen month / year grid
│ ├── notebook.go glamour viewer with inline edit
│ ├── branding.go Palette struct + light/dark variants
│ ├── icons.go IconSet (nerd / unicode)
│ ├── theme.go glamour style resolution
│ ├── theme_watcher.go shared SIGUSR1 + plist subscription
│ └── theme_watch_*.go per-OS plist watchers
├── Makefile build / test / lint / coverage / release
├── .golangci.yml mirrors fwrd's lint config
├── .github/workflows/ ci.yml + release.yml
├── config.example.toml
└── .goreleaser.yml
make build # ./sp
make test # all packages
make test-race # race detector
make coverage # writes coverage.html
make lint # golangci-lint (fall back to go vet)
make modernize # gopls source.fixAll
make ci # deps + lint + test + buildCI matrix runs on Linux, macOS, and Windows; lint pinned to
golangci-lint v1.64.8. See RELEASE.md for the GoReleaser flow.
MIT