Scratchpad is a macOS SwiftUI drawing app designed for fast whiteboarding with a trackpad surface and a document library.
Scratchpad is not a template app anymore. It currently includes:
- Multi-window document workflow (
Homelibrary + one editor window per.scratchpadfile) - Drawing tools: pen, highlighter, eraser
- Editing tools: select (rectangle/lasso), text, and shapes
- Floating toolbar with title editing, paper/canvas style, color palette, width controls, export, and clear/new actions
- Trackpad integration via
OpenMultitouchSupport - Autosave and file persistence in JSON-backed
.scratchpaddocuments - Export options: PNG, PDF, and raw
.scratchpad
- Swift 5 + SwiftUI
- AppKit interop where needed (window/event handling, save/export panels)
- Swift Package dependency:
OpenMultitouchSupport(1.0.12)Sparkle(2.9.x) for signed in-app updates with a custom SwiftUI toolbar chip
Scratchpad/ScratchpadApp.swift- App entrypoint and scene setup (
homewindow + documentWindowGroup)
- App entrypoint and scene setup (
Scratchpad/ContentView.swift- Main editor composition, event monitors, autosave loop, export hooks
Scratchpad/Home/- Library view for listing/opening/deleting scratchpad documents
Scratchpad/Toolbar/- Floating toolbar and tool controls
Scratchpad/Interaction/- Mouse-based interaction layer for selecting, moving, resizing, shape placement, etc.
Scratchpad/Canvas/- Render layers (grid, strokes, items, selection overlays)
Scratchpad/Trackpad/- Trackpad touch ingestion and surface UI
Scratchpad/Models/- Document state, persistence, stroke/item models, naming
- Scratchpad files are JSON documents with
.scratchpadextension. - Autosaved docs are stored under:
~/Documents/Scratchpad
- The home/library window lists files from that directory.
- macOS
- Xcode 17+
- Open
/Users/krishshah/Code/Scratchpad/Scratchpad.xcodeproj. - Select the
Scratchpadscheme. - Build and run with
Cmd+R.
cd /Users/krishshah/Code/Scratchpad
xcodebuild -project Scratchpad.xcodeproj -scheme Scratchpad -configuration Debug buildOpenMultitouchSupportis resolved through Swift Package Manager.MLXandMLXNNare resolved through Swift Package Manager fromml-explore/mlx-swift.Sparkleis resolved through Swift Package Manager fromsparkle-project/Sparkle.- If package resolution is stale/broken, in Xcode run:
File -> Packages -> Reset Package CachesFile -> Packages -> Resolve Package Versions
- The app ships Sparkle, but does not use Sparkle's stock windows. The only surfaced updater UI is the toolbar chip on the right side of the editor toolbar.
- On launch, the updater starts and performs a background check immediately. After that, Sparkle continues checking on the configured interval (
SUScheduledCheckInterval = 3600seconds). - The website download CTA should keep pointing at
/download. That route already redirects to the latest GitHub release DMG, and/appcast.xmlproxies the latest release'sappcast.xml. .github/workflows/release.ymlpublishes a new GitHub release on every push tomain, andscripts/ci/build_release.shhandles version bumping, archive/export, notarization, DMG creation, and Sparkle appcast generation.
BUILD_CERTIFICATE_BASE64: base64-encoded Developer ID Application.p12P12_PASSWORD: password for that.p12APPLE_ID: Apple ID used for notarizationAPPLE_APP_SPECIFIC_PASSWORD: app-specific password fornotarytoolAPPLE_TEAM_ID: Apple Developer team IDSPARKLE_PRIVATE_ED_KEY: private Ed25519 key used bygenerate_appcast --ed-key-fileSPARKLE_PUBLIC_ED_KEY: public Ed25519 key embedded into the app at build time
- The Sparkle public key is not sensitive and can be committed once finalized.
- The Sparkle private key must stay out of the repo. Keep it only in GitHub Actions secrets and any trusted local release machines.
The LaTeX conversion action expects a converted MLX model directory on disk. Runtime inference happens inside the app in Swift using MLX. The Python script is only for one-time weight conversion.
The checked-in weights live in Scratchpad/TexoMLXModel.bundle/ and are bundled into the app (the .bundle suffix keeps the directory intact when Xcode copies resources). weights.safetensors (~77 MB) is stored via Git LFS, so:
# one-time, per machine
brew install git-lfs
git lfs install
# when cloning for the first time (clone will already pull LFS files)
git clone https://github.com/KrishKrosh/Scratchpad.git
# in an existing checkout that predates LFS, fetch the real file
git lfs pullTODO: move to first-run download + cache so the app binary stays lean. Tracked in
TexoModelLocator.loadBundle().
When you run from Xcode, the app checks these locations automatically (first hit wins):
SCRATCHPAD_TEXO_MLX_MODEL— explicit override.local/TexoMLXModelinside this worktree — dev override (can be a symlink)TexoMLXModelin the built app bundle resources — default, picks up the bundled copy~/Library/Application Support/Scratchpad/Models/TexoMLX— future downloaded cache
For local development you can avoid the 77 MB bundle-copy cost on every debug build by symlinking .local/TexoMLXModel at the converted model directory:
- Prepare a Texo checkpoint and tokenizer locally.
- Convert the checkpoint:
python Tools/convert_texo_to_mlx.py \
--checkpoint /path/to/texo_checkpoint.pt \
--tokenizer /path/to/Texo/data/tokenizer/tokenizer.json \
--config /path/to/texo_model_config.json \
--output ~/Library/Application\\ Support/Scratchpad/Models/TexoMLX- Link the converted model into the repo for Xcode:
mkdir -p .local
ln -sfn ~/Library/Application\\ Support/Scratchpad/Models/TexoMLX .local/TexoMLXModelExpected files in that directory:
config.jsontokenizer.jsonweights.safetensors
Cmd+Dtoggles drawing mode (see trackpad surface hint in-app).Escexits drawing mode.- Selection supports rectangle/lasso and resize handles.
- Undo/redo flows through
NSUndoManagerinDocumentModel.