Lucas WintherandClaude Opus 5 71e8fc948b perf(export): fold once per month when building the ODS
monthView folds from the start of the dataset, so it costs O(months) per
call. The summary sheet and every per-post sheet each called it once per
(post, month), making a full export O(posts x months^2). buildWorkbook now
takes one pass, indexes the figures by post, and hands that to the sheets.

TODO called this "fine at personal-budget scale". Measured against the old
code it is not, once history accumulates: 30 posts over 10 years went from
7983 ms to 135 ms (59x), and that work is synchronous on the main thread, so
it was a visible freeze on clicking Export. 10 posts x 2 years: 44 ms -> 2.5 ms.

content.xml is byte-for-byte identical on a fixture exercising every sheet
(versioned rules, an override, an archived post, a percent split, a fixed
split and a finance plan).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 14:27:23 +02:00

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.webmanifest currently 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
S
Description
No description provided
Readme
1.7 MiB
Languages
TypeScript 98.3%
CSS 1.4%
HTML 0.2%