diet-guard/app/README.md
Krzysztof kuhy Rudnicki 50f311b6b8
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Gate the companion app in the push hook, and document it properly
`ci_mirror.sh` -- the pre-push gate -- ran only the Python side: a clean-venv
pytest and `pre-commit --all-files`. Nothing ran the app's suite, and
`.pre-commit-config.yaml` has no Dart hooks at all, so a red `app/` could be
pushed with a fully green gate. It now runs `flutter analyze` and
`flutter test` too, and *fails* rather than skips when `app/` exists but
flutter is missing: a gate that quietly passes without its toolchain is the
same as no gate. Verified by breaking a Dart file on purpose and watching the
push gate refuse it.

app/README.md was still the stock "A new Flutter project" template. It now
says what the two targets are, why there is no `linux/`, what the wrapper
owns, why the port and Chrome profile are fixed, and which behaviours the
desktop genuinely gives up.

Drops MIGRATION_PROMPT.md, whose content now lives in CLAUDE.md and the
migration commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:01:40 +02:00

76 lines
3.2 KiB
Markdown

# diet_guard_app
Companion app for `diet_guard`: log meals on the phone, and on the Linux
desktop, sharing one food log through GitHub-backed sync.
## Two targets, and only two
| Target | What it is |
|---|---|
| Android | a normal Flutter app (`flutter build apk`) |
| Linux desktop | the Flutter **web** build, served by a local wrapper and shown in a Chrome `--app` window |
There is no `linux/` directory. Flutter's GTK embedder manages ~20fps at
3840x2160 -- measured on a stock `flutter create` counter app, so it is the
toolkit rather than this code -- where the same Dart rendered by Chrome
sustains ~144fps (`~/todo/docs/desktop-performance-findings.md`). Running
`flutter create --platforms linux` here would silently restore the slow path.
## Commands
```bash
flutter test # the app's suite
flutter analyze
bash run.sh # build the web bundle and launch the desktop app
bash install_arch.sh # build + install the pacman package and .desktop entry
flutter build apk --release # the phone build
adb install -r build/app/outputs/flutter-apk/app-release.apk
```
Never `adb uninstall` or `pm clear` this package: both wipe the on-device log.
## The desktop wrapper
`bin/diet_guard_desktop.dart` is the other half of the desktop app, because a
browser cannot touch the filesystem. It:
- serves the built web assets from `build/web` (or `/opt/diet-guard-app/web`
once packaged);
- keeps an on-disk copy of every document and photo under
`~/.local/share/diet-guard-desktop/`, which is what makes a wiped Chrome
profile recoverable -- for photos it is the *only* copy, since they never
sync;
- holds the GitHub token (`~/.config/diet_guard_app/sync_token`, falling back
to the PC gate's `~/.config/diet_guard/sync_token`) and proxies
`api.github.com` plus the device flow, whose endpoints send no CORS headers
and so cannot be called from a page at all.
**The port (8732) and the Chrome `--user-data-dir` must stay fixed.**
IndexedDB is keyed by origin and lives inside that profile, so changing either
looks to the app like a device with no history.
## Platform seams
`dart:io` does not fail a web *compile*; it becomes a stub that throws at
runtime, so the symptom of a missed seam is a blank white window rather than a
build error. Every platform edge is a conditional export -- `document_store*`,
`blob_store*`, `background_tasks*`, `notification_backend*`, `token_vault*`,
`github_client_factory*`, `attached_image*`. Branch on `kIsWeb` *before* any
`Platform.is…`, which itself throws on web.
What the desktop genuinely loses, by design rather than omission:
- **Background reminders.** A browser runs nothing once its window is closed,
so the due-slot check runs in-page every 5 minutes while the window is open.
On the PC the real backstop is `diet-guard-gate.timer`, which locks the
screen.
- **Camera capture.** The browser picker is a file input.
- **Photo sharing between devices** -- unchanged: `imagePath` was always
stripped before push.
## Sync identity
Three devices push under `diet-guard-sync/devices/`: `pc` (the Python gate),
`phone`, and `desktop`. Each must keep its own id -- two devices sharing one
would overwrite each other's pushed log on every tick.