da87bff9c3e21e25421caba7f05896a812e2bc02
A grocery trip is twenty lines, and opening the full dialog twenty times is the thing this exists to avoid. PurchaseDialog gains a One / Many toggle, shown only when adding — editing an existing purchase edits exactly one, so the toggle is hidden there. In Many mode the date, post and currency are set once at the top and each line is just a description and an amount; Enter on an amount starts the next line, so a receipt can be typed without leaving the keyboard. Blank trailing rows are ignored rather than flagged. The whole batch commits in a single mutate: one write, one queue entry, and no way to half-commit a shopping trip. Lines are deliberately simple. A line needing a split or a payment plan is saved plain and then edited from the month view — building per-line split and plan editors would have been most of the work for a case the grocery run never hits, and would have slowed the common path. Line logic lives in a pure module (src/ui/bulkEntry.ts) following the purchaseForm.ts pattern, so it is testable with no DOM: 20 tests covering blank-row handling, per-row validation messages, the running total, and that N lines become N purchases landing wholly in the shared post. Each line is built through the same toPurchase the single form uses, so there is one definition of what a purchase is. Validation mirrors validatePurchase in what it deliberately does NOT reject: a negative amount is a refund, not an error. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Budget 2.0
A personal budgeting PWA. Envelope-style monthly budgets with rollover, split purchases, finance plans, multi-currency, and JSON/ODS export. All data lives in your browser — there is no server and no account.
Development
bun install
bun --hot src/index.ts # dev server with hot reload
bun test # run the test suite
bun run build # production build into dist/
Self-hosting with Docker
docker build -t budget2 .
docker run --rm -p 3000:3000 budget2
To serve from a subpath, build and run with BUN_PUBLIC_BASE_PATH:
docker build -t budget2 --build-arg BUN_PUBLIC_BASE_PATH=/budget/ .
docker run --rm -p 3000:3000 -e BUN_PUBLIC_BASE_PATH=/budget/ budget2
GitHub Pages
Pushing to main builds and deploys to Pages automatically, using
/<repository-name>/ as the base path. Enable Pages with "GitHub Actions" as
the source in the repository settings.
Your data
Everything is stored in this browser's IndexedDB. Export regularly from Settings → Your data: the JSON export is both your backup and how you move your budget to another device. Import replaces everything and downloads a backup of your current data first.
Known limitations
- Data is per-browser. There is no account and no sync. If you clear this browser's site data, or move to a different browser or device, your budget is gone unless you exported it first. The JSON export is not just a nice-to-have backup — it is the only way to move a budget between devices.
- The install prompt is incomplete.
manifest.webmanifestcurrently only ships an SVG icon. Chrome requires PNG icons at 192×192 and 512×512 to offer the "Install app" prompt; until those are added, the app works fine as a regular tab but won't be installable on Chrome/Chromium.
Documentation
- Design:
docs/superpowers/specs/2026-09-01-budget-app-design.md - Implementation plan:
docs/superpowers/plans/2026-09-01-budget-app.md
Languages
TypeScript
98.3%
CSS
1.4%
HTML
0.2%