diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 77f1376..b4b09b2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -50,3 +50,18 @@ jobs:
exit 1
fi
echo "No conflict markers."
+
+ sync-server:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+
+ - name: Install dependencies
+ run: pip install flask flask-cors
+
+ - name: Sync server tests
+ run: python -m unittest sync-server/test_server.py -v
diff --git a/.gitignore b/.gitignore
index 5eec986..9fcee08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
.claude
+__pycache__/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1d3e603..45d7254 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -15,13 +15,15 @@ making changes, edit `index.html` directly.
## Running the checks
-There is no test suite to run locally. CI validates:
+There is no test suite yet. To try a change, serve the repo root with any static
+server (for example `python3 -m http.server`) and open it in a browser. CI checks:
-- `index.html` and `docs/index.html` are valid HTML (via `htmlhint`)
-- `manifest.json` is valid JSON
- the version string in `index.html` matches the cache name in `sw.js`
+- `.nojekyll` is present, so GitHub Pages serves the site as is
+- there are no broken symlinks
+- there are no leftover merge conflict markers
-Before submitting a PR, verify those things pass by checking the CI output.
+Before submitting a PR, check that CI passes.
## What to contribute
@@ -33,9 +35,11 @@ Before submitting a PR, verify those things pass by checking the CI output.
## What is out of scope
-- Server-side features. Everything runs in the browser. There is no server, no account
- and no analytics — that is a deliberate constraint.
-- Third-party API integrations that require keys or accounts.
+- Server-side features the app depends on. Everything runs in the browser, with no
+ account and no analytics, and that is a deliberate constraint. The self-hosted sync
+ server and the bring-your-own-key AI features are opt-in extras behind Developer Mode,
+ and the app must keep working fully without them.
+- Integrations that need a key or account for core features.
- Framework migrations. The single-file architecture is intentional.
## Style
@@ -44,4 +48,5 @@ Match the surrounding code. Comments explain *why*, particularly where something
non-obvious has been done for a specific reason (PWA quirks, iOS Safari workarounds,
etc.).
-Keep the file self-contained. Do not add external script or style dependencies.
+Keep the file self-contained. The only external scripts are ZXing (barcodes) and
+Tesseract.js (label OCR); please don't add more.
diff --git a/README.md b/README.md
index ffa1bd1..61f073d 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,9 @@
-# Macro Tracker
+# Macrolog
-A mobile-first progressive web app (PWA) for tracking daily calories and macronutrients. No account, no backend, no app store — install it directly from the browser.
+A free, mobile-first progressive web app (PWA) for tracking daily calories and macronutrients. No account, no ads, no app store: install it straight from the browser.
**Live app:** https://macroslog.co.uk/
+**Beta channel:** https://beta.macroslog.co.uk/ (new features land here first)
**Docs:** https://macroslog.co.uk/docs/
---
@@ -11,14 +12,26 @@ A mobile-first progressive web app (PWA) for tracking daily calories and macronu
| Feature | Detail |
|---|---|
-| Barcode scanning | Camera scan → Open Food Facts lookup → log with weight |
-| Food search | Text search across millions of Open Food Facts products |
-| Manual entry | Add custom foods to a personal local database |
-| Recipe builder | Compose recipes from ingredients, log by weight or portion |
-| Daily dashboard | Progress rings for kcal, protein, carbs, fat, and fibre vs targets |
-| History | Browse any past day's food log |
-| Offline support | Service worker caches the app and barcode lookups for offline use |
-| Data export | Full JSON export of all logged data |
+| Barcode scanning | Camera scan, Open Food Facts lookup, log by weight |
+| Food search | Open Food Facts and USDA FoodData Central |
+| Nutrition label scanning | Photograph a label and the values are read with on-device OCR |
+| Custom foods and quick add | Save your own foods, or log raw macros |
+| Recipes | Build from ingredients, log by weight or portion |
+| Today dashboard | Calories ring, protein, carbs, fat and fibre against your targets, plus a sugar limit |
+| Water, exercise, weight and notes | Daily cards on the Today page |
+| History and trends | Calendar of past days, trend charts and a macro history view |
+| Fasting | Intermittent fasting timer with presets |
+| Setup wizard | Suggests targets from your stats, activity, goal and diet type |
+| Appearance | Dark, light or auto, plus which tabs and Today sections to show |
+| Offline | The service worker caches the app and food lookups |
+| Your data | JSON export and import, CSV exports of food and weight logs |
+
+### Developer features (opt-in)
+
+Turn on **Developer Mode** at the bottom of **Settings** to show:
+
+- **Sync server:** a small self-hosted Flask server (`sync-server/`) that keeps your log in sync between your phone, tablet and computer. See [the developer docs](https://macroslog.co.uk/docs/developer.html).
+- **AI features:** describe a meal in words to log it, plus daily and weekly summaries, using your own Claude, Gemini or OpenAI key. The key stays on your device and requests go straight to the provider.
---
@@ -27,40 +40,36 @@ A mobile-first progressive web app (PWA) for tracking daily calories and macronu
On **iOS (Safari):** Share → Add to Home Screen.
On **Android (Chrome):** browser menu → Install app.
-The app launches in full-screen standalone mode with no browser chrome.
+The app launches full screen with no browser chrome.
---
-## Default targets
+## Targets
-| Macro | Default |
-|---|---|
-| Calories | 2750 kcal |
-| Protein | 134 g |
-| Carbs | 430 g |
-| Fat | 55 g |
-
-Adjust in the Settings tab. Changes are stored locally.
+On first launch the setup wizard suggests targets. Until then the defaults are 2500 kcal, 60 g protein, 310 g carbs, 85 g fat, 30 g fibre and a 60 g sugar limit. Change them any time from the Today page or Settings.
---
## Storage
-All data lives in `localStorage` — no server, no account. Use **Settings → Export JSON** to back up or migrate your data.
+All data lives in the browser's `localStorage`. There is no server copy unless you run your own sync server, so use **Settings → Data → Export JSON** to back up or move your data.
---
## Tech stack
-- Pure HTML + CSS + Vanilla JS — zero npm dependencies, no build step
-- Service Worker for offline caching and PWA installation
-- Web App Manifest for installability
-- [Open Food Facts API](https://world.openfoodfacts.org/) — free, no API key required
-- [ZXing JS](https://github.com/zxing-js/library) via unpkg for barcode decoding
+- Plain HTML, CSS and JavaScript in a single `index.html`: no npm dependencies, no build step
+- Service worker (`sw.js`) for offline use and installation
+- [Open Food Facts](https://world.openfoodfacts.org/) and [USDA FoodData Central](https://fdc.nal.usda.gov/) for food data, no API key needed
+- [ZXing](https://github.com/zxing-js/library) for barcode decoding and [Tesseract.js](https://github.com/naptha/tesseract.js) for label OCR, both from CDNs
- Hosted on GitHub Pages
+See [CONTRIBUTING.md](CONTRIBUTING.md) to get involved.
+
---
## License
-Data from Open Food Facts is available under [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/).
+Macrolog is free software under the [GNU General Public License v3.0 or later](LICENSE).
+
+Food data from Open Food Facts is available under the [Open Database License](https://opendatacommons.org/licenses/odbl/1-0/). USDA FoodData Central data is in the public domain.
diff --git a/docs/changelog.html b/docs/changelog.html
index ba4fa96..75168cb 100644
--- a/docs/changelog.html
+++ b/docs/changelog.html
@@ -224,14 +224,73 @@
Macrolog
Changelog
A history of every update to Macrolog.
-
+
+
+
+
+ Beta
+ 6 October 2026
+
+
+
+
New
+
+
Two-way sync — every device pointed at your sync server shares one log. Changes merge entry by entry, sync a few seconds after each change, and a sync that would delete a large part of your log asks first
+
Sync server — new /sync/state endpoint with revision checks, atomic writes and tests
+
+
+
+
+
+
- v2026-09-02.2
- 2 September 2026
+ v2026-10-06.2
+ 6 October 2026Latest
+
+
+
Fixed
+
+
Chart dates — the dates under the Trends and Macro History charts were cut off and now show in full
+
Trends axis labels — the kg and kcal labels no longer overlap the top axis values, and the first and last bars no longer cover the axis numbers
+
First-run tour — steps whose target isn't on screen now show the tip in the middle instead of pointing at the top corner
+
Ko-fi card — on tablets and larger screens it sits above the bottom navigation instead of behind it
+
History calendar — the "Jump to Today" button was hidden behind the bottom navigation, so tapping it opened Settings instead
+
Additional Nutrients — the section in Manual and Quick Add now collapses when you tap its heading; it stays open by default
+
Food names — names in the macro breakdown popup are now shown as plain text, so a product name containing HTML can't change the page
+
Docs What's New — the update cards now appear in Chrome; they stayed hidden because the reveal never triggered
+
+
+
+
+
+
+
+
+
+ v2026-10-06.1
+ 6 October 2026
+
+
+
+
Fixed
+
+
Custom foods — new installs no longer start with a set of pre-filled custom foods, and foods you delete from your list no longer reappear the next time the app opens
+
+
+
+
+
+
+
+
+
+ v2026-09-02.2
+ 7 September 2026
+
Fixed
@@ -242,6 +301,50 @@
Changelog
+
+
+
+
+ Docs
+ 7 September 2026
+
+
+
+
Docs
+
+
Feature guides — new pages for recipes, installing, appearance, trends, the barcode scanner, nutrition label scanning and custom foods
+
Developer & AI guide — sync server setup, remote access options and AI features in one place
+
+
+
+
+
+
+
+
+
+ v2026-09-03.8 (beta)
+ 3 September 2026
+
+
+
+
New
+
+
Recipe drafts — save a recipe as a draft before it's finished, and delete drafts you no longer need
+
+
+
+
Fixed
+
+
Status bar colour — follows the selected style and is never purple
+
Bold nav — the floating nav hides behind open popups, the setup wizard and the on-screen keyboard
+
Gradient style — glass styling now covers every page, with a stronger gradient in light mode
+
Water buttons — use the selected style's colours
+
+
+
+
+
@@ -270,7 +373,7 @@
Changelog
New
-
/beta/ build — separate beta PWA at /beta/ with all 7 UI themes
- The sync server lets you access your macro data from any device on your network, or anywhere via a Cloudflare tunnel. It's a small Flask app you run yourself — your data never touches anyone else's infrastructure.
+ The sync server keeps a copy of your food log on a computer you control, reachable on your network or anywhere via a Cloudflare tunnel. It's a small Flask app you run yourself, so your data never touches anyone else's infrastructure. Every phone, tablet and computer that points at the same server shares one log: add a meal on your phone and it appears on your desktop, and the other way round.
Getting started
@@ -497,21 +497,33 @@
Getting started
Requires Python 3 and pip.
Clone the repo, install the dependencies, and start the server.
The server listens on port 5001 by default.
+
Set SYNC_API_KEY to require an API key. Without it, anyone who can reach the server can read your log.
+
Data is saved as JSON files in ~/.claude/garmin-tools/ on the server.
Enter your server's local IP, e.g. http://192.168.1.x:5001.
-
Tap Connect — your food log will sync automatically.
+
Turn on Developer Mode at the bottom of Settings. This shows the Sync Server card.
+
In Settings → Sync Server, enter your server's HTTPS address (see Remote Access below), e.g. https://your-tunnel.trycloudflare.com, and the API key, then tap Save.
+
Saving syncs straight away. After that Macrolog syncs a few seconds after each change, when you open it, and every couple of minutes while it's open. ⇅ Sync on the Today page syncs now.
+
Repeat on each device, using the same address and key.
+
+
+
How sync works
+
+
Food entries, notes, weight, water, exercise, fasts, recipes, custom foods, targets and settings all sync. Theme, display choices, AI settings and the sync settings stay on each device.
+
Changes merge item by item, so edits on two devices don't overwrite each other. If both devices changed the same entry, the last device to sync keeps its version, and an edit beats a delete. Water added on two devices on the same day is added together.
+
A sync that would delete a large part of your log, for example after a browser cleared its storage, pauses instead. Tapping Sync now explains what it would delete and asks first.
+
Clear All Data on a synced device only clears that device; the next sync downloads your log again.
+
Upgrading from the old one-way sync? Update the server first, then the app. The first sync merges the server's copy with your device, so an entry you deleted since your last upload may come back once.
@@ -530,12 +542,12 @@
Three ways to reach it.
From simplest to most permanent — pick whichever matches how you want to use the server.
-
1 — LAN only (simplest)
+
1 — LAN only
- Connect while on the same Wi-Fi network. No setup needed — just the IP address. Great for a home setup where your phone and server share a network.
+ Connect while on the same Wi-Fi network using the server's local IP address. Macrolog is served over HTTPS, and browsers block it from talking to a plain http:// address, so this only works if you put the server behind HTTPS yourself (for example with a reverse proxy and a certificate). For most setups, a tunnel below is easier.
-
2 — Cloudflare Quick Tunnel
+
2 — Cloudflare Quick Tunnel (simplest)
A temporary HTTPS URL, no account required. The URL changes each session, so it's ideal for occasional remote access.
- Every release is driven by real use. This one is a small but welcome fix for barcode scanning.
+ Every release is driven by real use. This one keeps your custom foods list to foods you added, and makes the chart dates readable again.
- After a barcode scan, the food result popup now clears the bottom navigation bar, so the Log button is always fully visible and tappable.
+ New installs no longer start with a set of pre-filled custom foods, and any food you delete from your list now stays deleted.
- Added extra bottom padding to the result sheet so its action button no longer sits behind the nav bar.
+ If your list already has these foods, they stay where they are. Delete any you don't use and they won't come back.
+
+
+
+
+
+
+
+
+
+
Readable chart dates
+
+
+ The dates along the bottom of the Trends and Macro History charts were cut off. They now show in full, and the kg and kcal labels no longer overlap the axis numbers.
+
+
+
+
+ Also: the History calendar's "Jump to Today" button is no longer hidden behind the bottom navigation, the Additional Nutrients section now collapses when tapped, the first-run tour no longer points at hidden parts of the screen, and on tablets the Ko-fi card no longer sits behind the navigation.
@@ -1621,7 +1642,7 @@
Run your own lightweight Flask sync server to keep every entry on infrastructure you control, and plug in your own AI provider (Claude, Gemini, or OpenAI) for meal descriptions and summaries — your API key stays on-device. Both are optional and off by default.