Repository navigation
Phone layout, Apple Health from iPhone (status, steps), and a data/ folder - #21
Merged
Merged
Conversation
Add viewport meta so phones get a real layout instead of a scaled-down desktop page. Under 480px: full-height hero, bottom-docked presets and Pause/End, 44px+ targets, header controls behind a menu button, history as cards. Add a web app manifest for full-screen home-screen launch, pressed states for buttons (Bootstrap's default went transparent), and a native screen wake lock on the active screen. Co-Authored-By: Claude <[email protected]>
A phone can't scan its own screen, so on iPhone/iPad the per-session and setup QR codes become buttons. The log link is now an x-callback-url whose x-success opens /health_logged/<id>, marking the session logged and clearing the prompt on every open page via SSE. Dismiss is stored separately (health_logged: 0 pending, 1 logged, 2 dismissed). Loopback hosts are swapped for the LAN IP so the phone can reach the callback. Co-Authored-By: Claude <[email protected]>
Recent Sessions shows a filled/outline heart per session when Apple
Health export is on. An Edit toggle reveals per-row Log (QR on desktop,
direct Shortcut link on iOS) and Delete (POST /delete_session/<id>,
completed sessions only). SSE health_status_changed now carries
{id, status} so other pages update the row, banner, and open QR modal.
Co-Authored-By: Claude <[email protected]>
Settings, the session database, crash-recovery state, and JSON-migration backups now live in data/ (backups in data/backups/) instead of next to the code. Files left in the app folder by older versions are moved on first start, never overwriting an existing file. config.py reads the legacy root config.json until it moves, and migrate_json() takes a backup_dir. pytest, ruff, and coverage settings merge into pyproject.toml, replacing pytest.ini, ruff.toml, and .coveragerc; requirements-dev.txt adds coverage[toml] so coverage reads it on Python 3.10. Co-Authored-By: Claude <[email protected]>
- Close the Log QR modal only on a "logged" status change; a lingering "dismissed" change closed a later Log for that same session. - Dismiss sends the banner's session id instead of marking whichever session is newest at click time. - /health_logged and dismiss only act on completed sessions; unknown, in-progress, or failed writes return 404 without broadcasting. - Move the database with its -wal/-shm as one group so a stale file in data/ can't separate them. Co-Authored-By: Claude <[email protected]>
Bootstrap tooltips on the Recent Sessions heart icons, shown on hover, tap, and keyboard focus, and updated live when a session is logged elsewhere. Duration through Avg Speed are now right-aligned like Avg Speed already was, so spare width spreads evenly instead of collecting before the last column. Co-Authored-By: Claude <[email protected]>
The logged heart uses the theme's text color instead of Apple Health pink, which clashed with themed palettes, fell just under 3:1 contrast on some light rows, and read as the app's danger red. Theme accents were too faint on most dark-mode rows. Fill vs outline carries the state. Co-Authored-By: Claude <[email protected]>
The Shortcut logs it as a Steps sample alongside the workout. Points the install link and the exported backup at the updated Shortcut. Co-Authored-By: Claude <[email protected]>
A page that hasn't seen the log yet (or the Settings off/on path) could downgrade a logged session to dismissed, bringing back its Log button and inviting a duplicate Health workout. Co-Authored-By: Claude <[email protected]>
data/ folder
Merged
SeanathanVT
added a commit
that referenced
this pull request
Oct 8, 2026
* Add unreleased section to CHANGELOG. * Add full unit test suite, CI for GitHub Actions and GitLab, and two crash fixes (2.5, 2.7) (#19) * feat(tests): add full unit test suite and CI (ROADMAP 2.5, 2.7) Cover routes, BLE sequences (fake controller), status-packet math, persistence, and run.py; 300 tests, ~98% line coverage, green on Python 3.10/3.12/3.13. CI runs ruff and pytest with coverage on GitHub Actions and GitLab CI. fix: /pause and speed routes no longer return 500 when the BLE loop is gone; a non-UTF-8 session_state.json no longer crashes startup. Install signal and atexit handlers only when startup is enabled, move run.py's launcher into main() for testability, and pin ruff with its lint fixes applied. Co-Authored-By: Claude <[email protected]> * docs(roadmap): add 3.18 transition hint flashing on every button press Co-Authored-By: Claude <[email protected]> * test: cover migration edge cases, shutdown paths, and SSE failures Add tests for non-list and bad-end_time legacy history, a failed .migrated rename, graceful shutdown with no session/controller/client, End while paused, connect with no BLE client, discard with nothing pending, /shutdown loop-stop edge cases, SSE double-remove on close, and a failed broadcaster tick. Each test fails if its target branch is broken. storage.py reaches 100%; line+branch coverage 97.3% -> 98.4%. Update ROADMAP 2.5's "Not covered" list to match the measured gaps. Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]> * fix(security): block cross-site state changes (ROADMAP 2.2) (#20) * fix(security): block cross-site POST requests (ROADMAP 2.2) Reject any non-GET request whose Origin isn't the app's own origin, or whose Sec-Fetch-Site is cross-site or same-site, so a website open in a browser on the network can't start the belt, change speed, clear history, or shut the app down. No token or sign-in: the app is LAN-only and LAN devices are trusted by design. Same-origin forms and fetches, requests without either header (curl, run.py's /shutdown), and all GETs are unaffected. Co-Authored-By: Claude <[email protected]> * fix(security): make /reconnect POST-only and harmonize docs (ROADMAP 2.2) /reconnect changed state on GET, so the cross-site guard skipped it and any web page could trigger a Bluetooth reconnect. Make it POST-only and turn the connecting screen's Connect / Try Again links into form buttons, so every state-changing route goes through the guard. Also harmonize docs and comments with the code: - shutdown comments: /stats_stream frames instead of /stats polling - remove "previously / before this change" narration from comments - README: startup connects in up to 3 attempts, database_path isn't on the Settings page, add apple_health_export_enabled row, note the LAN trust model on the host row - ROADMAP 2.2: files and known limits; 3.4: point at _build_stats_payload() - CHANGELOG: /reconnect POST-only, reconnect in the blocked actions Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]> * docs(roadmap): add 3.19 phone layout Co-Authored-By: Claude <[email protected]> * Phone layout, Apple Health from iPhone (status, steps), and a data/ folder (#21) * feat(ui): phone layout and screen wake lock (ROADMAP 3.19, 3.12) Add viewport meta so phones get a real layout instead of a scaled-down desktop page. Under 480px: full-height hero, bottom-docked presets and Pause/End, 44px+ targets, header controls behind a menu button, history as cards. Add a web app manifest for full-screen home-screen launch, pressed states for buttons (Bootstrap's default went transparent), and a native screen wake lock on the active screen. Co-Authored-By: Claude <[email protected]> * feat(health): on-device Log/Install buttons and auto-confirm on iOS A phone can't scan its own screen, so on iPhone/iPad the per-session and setup QR codes become buttons. The log link is now an x-callback-url whose x-success opens /health_logged/<id>, marking the session logged and clearing the prompt on every open page via SSE. Dismiss is stored separately (health_logged: 0 pending, 1 logged, 2 dismissed). Loopback hosts are swapped for the LAN IP so the phone can reach the callback. Co-Authored-By: Claude <[email protected]> * feat(health): per-session Apple Health status, retroactive log, delete Recent Sessions shows a filled/outline heart per session when Apple Health export is on. An Edit toggle reveals per-row Log (QR on desktop, direct Shortcut link on iOS) and Delete (POST /delete_session/<id>, completed sessions only). SSE health_status_changed now carries {id, status} so other pages update the row, banner, and open QR modal. Co-Authored-By: Claude <[email protected]> * chore: move runtime files into data/, tool configs into pyproject.toml Settings, the session database, crash-recovery state, and JSON-migration backups now live in data/ (backups in data/backups/) instead of next to the code. Files left in the app folder by older versions are moved on first start, never overwriting an existing file. config.py reads the legacy root config.json until it moves, and migrate_json() takes a backup_dir. pytest, ruff, and coverage settings merge into pyproject.toml, replacing pytest.ini, ruff.toml, and .coveragerc; requirements-dev.txt adds coverage[toml] so coverage reads it on Python 3.10. Co-Authored-By: Claude <[email protected]> * fix: review follow-ups for Apple Health status and data/ relocation - Close the Log QR modal only on a "logged" status change; a lingering "dismissed" change closed a later Log for that same session. - Dismiss sends the banner's session id instead of marking whichever session is newest at click time. - /health_logged and dismiss only act on completed sessions; unknown, in-progress, or failed writes return 404 without broadcasting. - Move the database with its -wal/-shm as one group so a stale file in data/ can't separate them. Co-Authored-By: Claude <[email protected]> * feat(ui): heart icon tooltips, right-aligned history numbers Bootstrap tooltips on the Recent Sessions heart icons, shown on hover, tap, and keyboard focus, and updated live when a session is logged elsewhere. Duration through Avg Speed are now right-aligned like Avg Speed already was, so spare width spreads evenly instead of collecting before the last column. Co-Authored-By: Claude <[email protected]> * style(ui): monochrome heart for logged sessions The logged heart uses the theme's text color instead of Apple Health pink, which clashed with themed palettes, fell just under 3:1 contrast on some light rows, and read as the app's danger red. Theme accents were too faint on most dark-mode rows. Fill vs outline carries the state. Co-Authored-By: Claude <[email protected]> * feat(health): send step count to the Apple Health Shortcut The Shortcut logs it as a Steps sample alongside the workout. Points the install link and the exported backup at the updated Shortcut. Co-Authored-By: Claude <[email protected]> * fix(health): don't let Dismiss undo a logged session A page that hasn't seen the log yet (or the Settings off/on path) could downgrade a logged session to dismissed, bringing back its Log button and inviting a duplicate Health workout. Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]> * docs(roadmap): add 5.4 native HealthKit companion app Records the Shortcuts limits found while adding steps (no indoor flag, no End Date, no attaching samples) and that HealthKit works on a free Apple ID with 7-day reinstalls. Co-Authored-By: Claude <[email protected]> * Fix transition hint flashing on every button press (ROADMAP 3.18); add AGENTS.md (#22) * fix(ui): show transition hint only during belt sequences (ROADMAP 3.18) Submitting any form called setTransitioning() with the page's hint id, so presets and steppers flashed "Getting the belt moving…" and shifted the layout, and Start/Pause/Resume/End flashed the wrong text. disableButtonsOnSubmit() now only disables and dims the buttons, and the hint follows the SSE belt_transitioning flag alone. Also stop SSE ticks from re-enabling the buttons while a submit is in flight: a page-level submitting flag keeps them disabled until the page navigates away, and is cleared on a back/forward-cache restore. Co-Authored-By: Claude <[email protected]> * docs: mark ROADMAP 3.18 complete and add CHANGELOG entries Co-Authored-By: Claude <[email protected]> * docs: add AGENTS.md (CLAUDE.md symlink) and Shortcut rebuild note AGENTS.md holds repo rules for coding agents: keep docs in sync with code, git workflow (maintainer commits, PRs target development), desktop-first design target, and supported platforms. CLAUDE.md links to it so Claude Code reads the same file. ROADMAP: note that Log Health Sample's Steps Value row only appears after granting Shortcuts write access to Steps. Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]> * docs(changelog): release 1.9.0 Map every completed ROADMAP item to its CHANGELOG version. Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
base.htmlhad no viewport meta, so phones rendered a shrunken desktop page. Under 480px: the speed reading fills the screen, presets and Pause/End are full-width rows docked at the bottom, every tap target is at least 44px, the header controls fold into a ⋮ menu, and Recent Sessions shows as cards. Desktop layout is pixel-identical to before at 1280px.navigator.wakeLockon the Active screen. Browsers only allow it onlocalhost/HTTPS, so it keeps the computer's screen on but not a phone on LAN HTTP.static/manifest.json(display: standalone), so Add to Home Screen opens full-screen.shortcuts://x-callback-url/run-shortcutURL whosex-successopens/health_logged/<id>. That marks the session logged, and every open page updates within about a second over SSE, including the desktop that showed the QR. This works with the already-installed Shortcut.apple_health/log-walkingdad-workout.shortcutpoint to the new version.data/folder. Settings, the database, crash-recovery state and the JSON-migration backups move out of the code folder intodata/(backups indata/backups/).pytest.ini,ruff.tomland.coveragercare merged intopyproject.toml.Upgrade notes
config.json, the database (with its-wal/-shm, as one group),session_state.jsonand anysession_history.json.*backups intodata/. Nothing is overwritten; a blocked move is skipped and logged. Until then,config.pystill reads a rootconfig.json.data/config.jsonfrom now on.config.json.examplestays in the root.stepsfield. The first run asks for permission to write Steps to Health.sessions.health_loggednow holds three values:0pending,1logged,2dismissed. Before this, Dismiss also wrote1, so existing1rows show as logged even if some were only dismissed. No schema change or migration.hostmust be0.0.0.0(the default), and when the desktop useslocalhostthe callback URL uses its LAN IP instead. If the phone can't reach it, the workout is still logged and only the prompt stays.Notable implementation details
/health_logged/<id>is a GET, becausex-successopens as a Safari navigation. It only acts on completed sessions and returns 404 otherwise. The CSRF guard skips GETs, so the most another website could do is mark one session logged.is-iosclass; iPadOS is detected as a touch-capable "Macintosh"), and.ios-only/.not-iosonly ever hide elements.--bs-btn-active-bgwas never set. They now show a darker pressed color and shrink slightly, on desktop too.--text-colorrather than the theme accent or Apple pink. Accents fall under 3:1 contrast on 9 of the 15 dark themes, and the pink read as the app's danger red.Testing
pytestpasses (379) andruff check .is clean. New tests cover:stepsin the data sent to the Shortcut) and the LAN-IP swap/health_loggedvalidation/delete_session(with the samples cascade and the in-progress refusal)data/relocation (all-or-nothing database group, never overwriting)data/backups/config.json,walkingdad.dband migration backups. All 32 sessions were preserved, the settings save went todata/config.json, and the root was left clean.stepsfield.Not in this PR
🤖 Generated with Claude Code