Autocomplete now draws from every user's logged exercises, not
just the requesting user's history. New users get suggestions from
day one.
- db.get_all_exercise_names(): case-insensitive grouping, ordered
by usage count desc, alphabetical tiebreak, excludes names that
only appear in soft-deleted workouts.
- server.api_get_exercise_names simplified to a one-liner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Display workouts as "#N" based on each user's own ordered list of
non-deleted workouts (rank by timestamp ascending). Global auto-
increment id stays the primary key, used only internally and in
exports. User-visible surfaces now all use the per-user number:
- /history listing
- /delete now accepts the per-user number
- Save confirmations (bot text and Mini App toast)
Deleting a workout renumbers the later ones downward, as expected
for a pure display transform.
New db helpers: get_user_workout_number, resolve_user_number, and
get_workouts now includes user_number per row via SQLite window
function.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Format: 'YYYY-MM-DD <short-sha>'. Preferred path uses
`git log -1 --format='%cs %h'`.
Fallback path (no git on PATH) resolves HEAD (loose or packed refs)
and parses the loose commit object via zlib to extract committer
date, converted to UTC date. Degrades gracefully to SHA-only if
the commit object is packed or unreadable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two concerns sharing one gray block felt crowded. Giving the
next-exercise input its own card creates clear visual separation
and a natural "what comes next" feel.
Drop the now-redundant top-border/padding on .sets-section since
it's its own card now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The empty "Exercise name" field above the current exercise's sets
felt counter-intuitive. Putting it below the sets reads naturally:
current exercise on top, prompt to start the next one at the bottom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The systemd service environment on the NixOS deploy host has a
minimal PATH without git, so subprocess.run(['git', ...]) fails
with FileNotFoundError and /api/version returned 'unknown'.
Fall back to reading .git/HEAD directly (resolving the ref) and
returning the short SHA. Loses tag/dirty detection, but unblocks
the version badge in deployed environments.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Compute `git describe --tags --always --dirty` at server startup
and expose via unauthenticated /api/version. Render as small muted
text at the bottom of the Mini App so the running version can be
confirmed at a glance.
Once tags exist, the badge will show e.g. v0.1.0 or v0.1.0-3-gSHA.
Until then it shows the short SHA.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Typing in the exercise-name input while an exercise was in progress
but had no sets would silently replace it. Hide the name row in that
state so the user commits to adding a set (or explicitly removes the
exercise) before starting another.
Because the name row was the only way to abandon a 0-set exercise,
always show the "Remove exercise" escape hatch whenever a current
exercise exists.
Consolidate the save/name-row/delete visibility logic in syncEditorUI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Save button was gated on workout.length, which stays 0 until a
second exercise is started (the transition that flushes the current
exercise into workout[]). Single-exercise workouts had no reachable
save path.
Gate on workout.length OR currentExercise sets, and recompute
visibility when sets are added/removed and on draft restore.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The telegram-fitness-bot/ subdirectory was the initial scaffold,
superseded by the top-level implementation and unused since.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users can send /feedback <text> to record feedback. Stored in a new
feedback table with user_id, text, created_at. Updated /start help text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebuilt Log view: exercise name input with autocomplete, per-set
entry (reps x weight), exercise cards with edit/remove. Comma and
dot both work as decimal separators. Notes field. Collapsible raw
text input as fallback.
Edit saved workouts from History (pencil icon). Loads exercises
into editor, Save becomes Update, Cancel returns to History.
localStorage draft persistence: auto-saves on every state change,
restores on reopen (24h expiry), clears on save.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deleted workouts are marked with deleted_at instead of being removed.
All queries filter on deleted_at IS NULL. New update_workout() does
soft-delete + recreate preserving the original timestamp. PUT endpoint
at /api/workouts/{id}. POST /api/workouts now accepts a note field.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parser now supports per-set notation (8x25, 5x35, 6x40),
bodyweight exercises (3x10), and asterisk separators.
Failed parse lines get user-facing error feedback instead of
being silently ignored.
Added /delete <id> and /export commands. Stats computed in SQL
instead of loading all workouts into memory. API gains DELETE,
CSV and JSON export endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Telegram workout tracker bot with Mini App web UI, SQLite database,
API server, and cloudflared tunnel support.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
QUIC tunnels consistently returned 1033 errors. Forcing http2 fixes it.
Added a daemon thread to drain cloudflared's stdout pipe to prevent
buffer-full blocking that would kill the tunnel.
- Replace localtunnel with cloudflared (no interstitial password page)
- Wait for "Registered tunnel connection" before starting bot
- Serve index.html at / instead of directory listing
- Remove localtunnel npm package build from flake.nix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- server.py: aiohttp API serving webapp/ and REST endpoints using existing db.py
- start.py: orchestrator that loads token, starts server + localtunnel + bot
- webapp/: Mini App frontend (Log, History, Stats) with Telegram-native theming
- bot.py: added Mini App menu button and inline button on /start
- flake.nix: added aiohttp + localtunnel, nix run now uses start.py
Python bot that parses workout messages (Exercise: SetsxRepsxWeight),
detects supersets from consecutive lines, extracts machine IDs, stores
both raw message text and parsed data in SQLite, and reads original
timestamps from forwarded Saved Messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>