Commit graph

37 commits

Author SHA1 Message Date
cfe0212bbc BRouter UX: gap-fill preview+reversible save; per-segment route planner
Gap-fill (track detail): GPS.fillGaps now returns {points,gaps} with per-gap
status; the 🧩 control previews ONLY the filled dropouts (cyan casing; unfilled
= red dashed) with a Save/Cancel panel. Save is a reversible in-place patch
(estimated points added, original untouched; DB.saveGapFill/revertGapFill +
estimated/gap_filled columns), then recalculateSession. Revert removes them.

Route planner: renamed menu 'Нарисовать маршрут' → 'Нарисовать тренировку'
(it makes a workout session, not a route); routes empty-state now points at the
on-phone planner. showRouteEditor rewritten per-leg: each leg road (BRouter) or
straight independently, road by default, tap a leg to toggle it, long-press to
insert, silent straight fallback where no road; the global button sets all legs.
2026-09-10 15:26:05 +03:00
8e720ecd17 BRouter: offline route planning by waypoints + segment download UI
Stage 3 — planning: showRouteEditor gets a "🚲 По дорогам" toggle. Waypoints
stay editable markers; with it on, the line between them (and the saved
geometry) follows roads via BRouter (brouterMode/routeGeom, 450ms debounce on
edits). BRouter.route (router.js) now: Android -> embedded engine, otherwise
(desktop / at home) -> the PC RouteServer over HTTP (http_request bypasses the
WebView CSP; LAN 192.168.1.18:17777 / WAN mirror.invalid:17777). Lets planning be
debugged on the desktop.

Stage 2 — segment UI: a "Сегменты" button on the roads screen opens
showBrouterSegments — region tiles (E35_N45/E40_N45), "есть" status, a
Download/Update button with percent progress via BRouter.downloadTileAndWait.

Verified: RouteServer returns 83 trkpt for a 3-waypoint trekking route (3594 m),
parseRouteGPX reads trkpt; both files compile into the apk. End-to-end UI (tap
waypoints -> routed line; in-app segment download) pending desktop/phone run.
2026-09-09 17:33:11 +03:00
d078db7288 POI overlay on maps: circular contrasting icons + category filter (offline)
Points of interest (cafe, shops, water, toilets, bank/ATM, bus, shelter,
viewpoint, ...) as circular contrasting badges (emoji in a white/dark circle,
readable on light and dark tiles) — not pins. A 📍 control toggles the layer
and opens a per-category filter (checkboxes, remembered). Markers are culled to
the visible bounds and shown from zoom 13 (cap 500/view), on the recording,
track-detail and roads maps.

Data is bundled and fully offline: frontend/data/pois.json ([{la,lo,c,n}], 358KB,
5662 POIs) fetched once from Overpass for ~100km around region (the whole
bbox returned in one query; adaptive subdivision was ready if it had truncated).

Verified functionally on device: 124 badges rendered in view at z16 over
region with correct bounds/category culling; the live visual could not be
screenshotted (the WebView surface is black under CDP). frontend/data + poi.js.
2026-09-09 16:51:58 +03:00
375fb0a7ab Map: dark backdrop + reliable tile caching
Two fixes to the dark map + offline tiles:
- Dark backdrop: body.dark-map also darkens .leaflet-container (was #ddd), so
  the light Leaflet background no longer flashes bright between/behind tiles
  while zooming or where a tile is missing.
- Tile caching: createTile fetched the display <img> from the URL but cached
  via a SEPARATE fetch — on a flaky mobile link that second request often
  failed, so tiles showed but were never cached and re-zooming re-fetched them.
  Now one fetch caches and displays from the same blob, so every shown tile is
  cached. CSP already allows connect-src to the OSM tile host.

Verified on device: container bg rgb(27,31,36) under dark map; IndexedDB tile
cache populated.
2026-09-09 13:46:22 +03:00
5c23c4dda4 Map: dark-map toggle (invert tiles only), no repacking
A moon button on the recording, track-detail and roads maps inverts ONLY the
tile layer: body.dark-map applies `invert(1) hue-rotate(180deg) brightness(1.5)`
to .leaflet-tile-pane, so the track/route/roads/puck overlays keep their colours.
Works the same on offline (tiles_*.db) and online tiles — no keys, quotas or
tile repacking. The choice persists in localStorage (baldav_dark_map).

Chosen after ruling out dark tile providers (CARTO/Thunderforest/2GIS/Yandex —
keys/quotas/ToS/projection/RF-reachability) and the transport style (inverts
badly in dense city); plain OSM inverted looks good city and countryside.

Verified on device: filter on the tile pane only, overlay pane filter none,
button active and remembered.
2026-09-09 13:33:05 +03:00
34927e3f52 rec screen: status-bar backdrop so the system bar stays visible over the map
The recording map is edge-to-edge and drew behind the system status bar,
leaving its icons unreadable over the tiles (the map controls were already
offset by --safe-area-top, so the design intended a visible bar). Paint a
solid strip the height of the top inset behind the bar — like the header on
every other screen. Disabled in the rotated landscape modes.
2026-09-07 17:15:19 +03:00
1b10aa67de Wind: "headwind in the face" label button on the recording screen
Variant 5 from the findings doc, as GROUND TRUTH for validating the
from-track wind estimate — it does NOT feed kcal/power. Chosen scope
(per user): a toggle button now, binary "in the face" only, voice later.

- rec screen button "Ветер в лицо": tap opens a headwind interval, tap
  closes it; the open state is highlighted and survives a screen re-render
- GPS.toggleWindMark()/_closeOpenWindMark(); recording.windMarks =
  [{startTs,endTs}] ms, persisted to localStorage, written to the DB on
  finish and restored on crash recovery (a dangling interval auto-closes)
- gps_sessions.wind_marks (JSON) + DB.setSessionWindMarks (separate from
  finishGpsSession so recompute paths preserve marks); parsed in getGpsSession
- rec-bottom wraps now so the third button never overflows a narrow screen

Analysis (marks vs estimate) stays in tools/track once enough km accumulate.
2026-09-07 17:10:16 +03:00
3ca2bc00c0 Chestny Znak scanner: bundle offline ZXing decoder, ЧЗ badge, session auto-push
- Native BarcodeDetector needs Google Play Services (fails "Barcode detection
  service unavailable" on de-Googled ROMs). Replace it with a bundled, fully
  offline ZXing decoder (frontend/lib/zxing.js, @zxing/library UMD): scanner uses
  BrowserDatamatrixCodeReader (TRY_HARDER) for live camera and decodeFromImageUrl
  for the photo fallback. No Google, codes never leave the device.
- Source badge "ЧЗ" (src-chzn) for products added via the scanner.
- tools/chzn_export.sh: adb push the exported session to the device Downloads so
  BalDav's file picker can import it directly.
2026-08-27 04:31:58 +03:00
493c9b92df Recording map: navigation puck (position+direction), z-order panes, compass-only mini-compass
- One direction indicator instead of two geo-arrows: an SVG divIcon L.marker
  (rotateWithView) — a track-colored circle (20% darker) with white + 1px black
  rings, plus a triangle pointing the ACTIVE source heading (compass in compass
  mode, GPS course in GPS mode). Rotation = geo heading; rotateWithView keeps it
  correct as the map turns. Replaces posMarker + compass/course arrows.
- Z-order via panes: roads -> roadsPane(240), route routePane(250), track
  overlay(400), puck markerPane — tiles<roads<route<track<puck (track no longer
  hidden under roads).
- Mini-compass shown only in compass mode; tap-to-calibrate kept.
- darkenHex helper; .rec-puck css (no default div-icon box).
2026-08-24 23:06:26 +03:00
bf3c0e2534 Track detail de-jitter: fix overlay stacking, native dialogs, slow save
Fixes to the smoothing/routes UI reported on device:
- Blank detail after save/revert: showSessionDetail was called from inside the
  open detail modal, stacking a second overlay; querySelector("#sd-map") then
  hit the stale, already-initialized Leaflet map and threw, aborting the rest
  of the render (map + speed chart). New reopenDetail() closes the current
  detail (and its fullscreen-map child) and reopens fresh, like recalc/refine.
- Native confirm()/prompt() replaced with in-app appConfirm/appPrompt dialogs
  (winStack + history integrated, hardware back cancels). Used for smoothed
  save, route delete, route rename.
- Slow smoothed save: one UPDATE per point (4000+ IPC round-trips) replaced
  with a chunked CASE update — 18 statements for 4366 points, params kept
  under SQLite's 999 limit. Verified value-for-value on a copy DB.
- Closing the fullscreen map now also closes the smoothing panel (it is a
  fixed element that otherwise lingered over the collapsed map).
- "≈" button active state now reflects panel-open only, not session.smoothed
  (it looked permanently pressed after a save).

Docs: README + PROJECT_MAP updated for saved routes, de-jitter, accuracy_m,
routes table and new gps_points/gps_sessions columns. Version 0.1.393.
2026-07-31 23:51:20 +03:00
9d09b4c4a4 Saved routes overlay + per-activity road colors + reversible track de-jitter
Routes:
- routes table + CRUD helpers (db.js), GPX rtept/trkpt/wpt parser (gps.js)
- "Saved routes" screen (import/rename/recolor/delete, one active at a time)
- Route overlay on both the recording map and the past-track detail map,
  toggled by an "M" button; default ON at Start when a route is selected
- Draw after real map-load via async whenMapReady(), no setTimeout

Road colors:
- per-activity configurable colors (walk/bike/run); fix walk roads (atype "w")
- get_road_points maps run -> "r" (lib.rs)

De-jitter (Savitzky-Golay):
- GPS.smoothTrackSG (quadratic LS per window, iterated, endpoints anchored)
- reversible storage: lat_smoothed/lon_smoothed + session.smoothed flag
- detail-screen tuning panel (window/iters sliders, live preview,
  save smoothed / revert original, kcal recomputed on new points)
- accuracy_m column per point for later accuracy-gated recompute

Active map-button state restyled to inverted (dark glyph on translucent
white) for OLED; detail map safe-area handled in fullscreen.
2026-07-31 22:11:55 +03:00
2051a604e1 GPS accuracy-gated points + bearing; fix startup UI freeze
- Native: hard-reject fixes coarser than the Min-accuracy setting (drops
  coarse cell ~800m that stuck the track); remove low-accuracy and adaptive
  relaxation branches so only accurate fixes are recorded, honest gaps otherwise
- Store bearing_deg per point (gps_points migration + addGpsPoint)
- Default Min-accuracy 20 -> 50m (one-time migration, respects user override)
- Fix: http_request was synchronous with no timeout; an unreachable host
  (LAN sync on startup) blocked the whole UI ~30s. Now async + spawn_blocking
  + connect(4s)/global(12s) timeouts, fails in background without freezing
- Bundled prior work: course-up map orientation, compass calibration,
  online elevation refine (open-elevation), distance gap-gate fix
2026-07-07 00:28:27 +03:00
50ec5c21f8 Health-diet.ru products (24K), GPS session safety, build.sh upload fix
Products:
- health-diet.ru scraper (Tampermonkey) + import script
- 11469 products with 37 nutrients, Dodo 258, McDonalds 619, KFC 361, Ashan 2000
- Rust import_seed_products: dynamic column list, auto-create missing columns
- Seed version bumped to 10

GPS session safety:
- Separate gps_points_{ts}.jsonl per session, deleted only after DB save
- Toast on DB errors, NaN guard in finishGpsSession

Other:
- build.sh: router/VPS upload only with -u flag
- Orientation buttons bottom-right, weather via Yandex Pogoda
2026-06-28 23:17:51 +03:00
4ed6e6f039 Activity types (squats/jumprope/stretch/swim), road map, tile import, fixes
Activity types:
- New types: squats, jump rope, stretching, swimming pool
- MET-based kcal auto-calculation (6.0/12.3/2.3/8.0)
- Dynamic modal fields per type (sets/reps, distance, minutes)
- Fix: state.latestWeight is object {weight_kg}, not number

Road map:
- road_points table with 2m dedup, activity type (bike/walk)
- Rust rebuild_roads + get_road_points (bypass SQL plugin 28K limit)
- Segment detection by 100m gap, bike=blue walk=brown

Tile system:
- CSP fix: a.tile.openstreetmap.org matches *.tile pattern
- pack_tiles.js tool, import from file/router, ureq body limit bypass
- Tile errors: done(null,tile) prevents crash cascade

Other fixes:
- Manual routes: skip gap segmentation (synthetic timestamps)
- Draw route: own CSS class, not affected by landscape rotation
- Orientation lock button (A/L), hysteresis 65/35 degrees
- GPS placeholder only when state=waiting_gps
- Food amount inputmode=text for formula support (4*30)
- showModal onMount callback
2026-06-26 21:04:21 +03:00
184b86b18c Tile import, road map, orientation lock, CSP fix, tile error handling
- Tile import: pack_tiles.js (PNG→SQLite), Rust import_tiles + download_file
  commands, JS importFromSqlite, UI in settings (file + router download)
- Road map: road_points table with 2m dedup, rebuild_roads + get_road_points
  Rust commands (bypass Tauri SQL plugin 28K row limit), bike=blue walk=brown
- Orientation lock button (A/L) on recording map, hysteresis 65/35 degrees
- CSP fix: tile URL a.tile.openstreetmap.org (matches *.tile CSP pattern)
- Tile errors: done(null, tile) instead of Error to prevent crash cascade
- GPS placeholder: only show when state=waiting_gps (not on screen re-enter)
- ureq body size: use into_reader() to bypass 10MB limit for tile downloads
2026-06-26 16:41:14 +03:00
704c0892ec Landscape panel on the right side, nav hidden, GPS placeholder fix
- flex-direction:row in landscape — panel 200px on the right, stats vertical
- CSS rotation compensates phone orientation, user sees element coords directly
- Bottom nav hidden in landscape, restored on exit
- GPS placeholder overlay: separate div from map container to avoid Leaflet conflict
- display:contents removed, clean column layout for stats in landscape
2026-06-25 18:23:58 +03:00
fda820568e Compass map rotation, landscape layout, GPS placeholder
- leaflet-rotate plugin: map rotates by compass heading via deviceorientation
- Landscape detection: CSS viewport rotation when phone held sideways (gamma > 50)
- Bottom nav hidden in landscape mode
- GPS placeholder: pixel art overlay shown until GPS signal arrives
- invalidateSize() after map init to prevent black screen
2026-06-25 16:54:45 +03:00
7c93bb75c3 OTA updates, BLE HR, water tracking, GPS altitude filter, BMR fix, nutrient units
OTA: VPS nginx endpoint, build.sh -u upload flag, Kotlin check + browser download
BLE HR: FitPro protocol (PAIR/CCCD/HR_SWITCH/REALTIME/MEASURE), GATT queue,
  ACK responses, auto-detect device type, auto-start with GPS, re-trigger 25s
  Kotlin BleHrService, btleplug for desktop, file IPC for Android
Water: products.water_pct column, getDailyWaterMl(), progress bar on today screen
GPS altitude: outlier spike filter (>4m), stuck run interpolation (5+ identical),
  configurable smoothing (altSmoothHalf setting), filtered raw for kcal calc
BMR: removed broken estimateRealBmr, added activity level dropdown (1.2-1.725),
  fixed 7-day trend using dailyBase instead of raw bmr
Nutrients: ginseng/resveratrol/coq10 columns, fixed folate/selenium units (removed
  mult:1000), iodine/chromium input in mcg, addSupplement now inserts all 41 columns
UI: parseAmount() for 4*30 expressions, recipe portion amount field, nutrient
  reserves strip, GPS sessions in today view, pan on charts, showToast(),
  HR norms in settings, HR on recording screen, HR chart in track details
Scrapers: 5ka Load JSON button, magnit_scraper.user.js (NUXT_DATA parsing)
2026-06-07 01:36:38 +03:00
45d3736a99 Chart zoom 2x faster, chart padding, clearer food cost format
Charts:
- Zoom speed: 0.02 -> 0.04 (all 5 chart instances)
- stats-chart-block: added padding 0 14px to match content margins

Food cost of workout:
- Was: "1.8 x Хлеб (102г, 273 кк)" — ambiguous "1.8 x"
- Now: "1.8 порц. Хлеб (по 102г, 273 кк/порц.)" — clear units
- Header: "485 ккал — это как:" instead of "485 ккал ="
- Math: 485/273 = 1.78 portions of 102g each

Duplicates: seed DB has none (verified). Device dupes from pre-dedup
imports — JS migration handles them on next launch.
2026-06-01 13:51:47 +03:00
8d9b5aaf7e Source badges on products: Магнит (red), 5ка (green), KDV (orange), ИОН (gray), Свой (yellow)
- sourceBadge() helper renders colored badge by source field
- searchProducts SELECT now includes source column
- getFoodForDate SELECT includes p.source as product_source
- Badge shown on product cards in search and food log entries
- CSS: .src-badge with per-source colors (pastel, dark-theme friendly)
2026-05-30 15:59:50 +03:00
7b41382ea4 Add-btn border: gray (--border) instead of primary 2026-05-29 20:30:36 +03:00
f871e5bb28 Add custom product with full nutrients, bigger + buttons with border
Custom product:
- "+" button next to search on Products tab
- showAddProductModal: name, KBJU, + all MICRO_FIELDS (34 nutrient fields)
- All optional except name
- Creates product with source="manual"
- Refreshes search after save

Button style:
- .add-btn: 0.5px border with primary color, rounded corners (6px)
- Min 40x36px for finger-friendly tapping on retina
- Flexbox centered content
2026-05-29 18:21:49 +03:00
818276d60c Merge two nutrient strips into one: scroll collapsed, wrap expanded
Was: upper micro-strip (supplements only, scroll) + lower nutrient-strip
(food+supps, expand). Now: ONE strip at top that does both.

Collapsed: horizontal scroll, single row, no wrap.
Tap: expands to full wrap view showing all nutrients.
Tap again: collapses back to scroll row.

Data source: food (product micronutrients on-the-fly) + supplements.
Sorted by % RDI ascending (most deficient first).

Removed: buildMicroStrip() call, .micro-strip element, old scroll handler.
2026-05-29 14:55:59 +03:00
de5cfd5c21 Manual KBJU input, omega-3/6/9, product micronutrients, nutrient strip expand
1. Manual food entry: added B/Zh/U fields. Creates custom product in DB
   with user-provided KBJU, then logs it. Fixed addProduct lastInsertId.

2. Omega 3-6-9: columns in supplements, in supplement totals query,
   in MICRO_FIELDS form (34 fields total now).

3. Product card micronutrients: searchProducts now fetches all micro
   columns. renderProdDetails shows nutrient chips (sorted by deficiency)
   when product is expanded.

4. Supplement form: added B5, B7, chromium, manganese, molybdenum,
   iodine, phosphorus, omega-3/6/9, silicon, vanadium, lutein, lycopene.

5. Nutrient strip: tap to expand/collapse. Collapsed = 1 row (36px).
   Tap = toggle full view showing all nutrients.

6. Seed DB v6: added "Тесто для чебуреков 500г" (261 kcal, 7.1/3.8/48.2).
2026-05-29 14:47:09 +03:00
885ef27f36 RDI table, nutrient % in food/recipes, daily deficiency strip
RDI (bmr.js):
- 27 nutrients per TR TS 022/2011 Russian daily intake standards
- Exposed as window.BMR.RDI

Supplement columns (db.js):
- 12 new columns: B5, B7(biotin), D, K, Cr, Mn, Mo, I, P, Si, V, lutein, lycopene, carotene
- getSupplementTotalsForDate expanded to sum all new columns

Food nutrient display (main.js):
- Removed asterisk (*) from daily kcal (guessed tint stays)
- getFoodForDate now JOINs all micronutrient columns from products
- foodNutrientLine() shows "C 12%, Ca 5%, Fe 8%..." under each food item
- calculateRecipeNutrition expanded to 19 micronutrients with cooking loss

Daily nutrients section (main.js):
- "Нутриенты" section on Today screen between Добавки and Сон
- calcDailyNutrients() sums food (on-the-fly from products) + supplements
- Sorted by % RDI ascending — most deficient first
- Color coded: red < 30%, orange 30-80%, green >= 80%

Recipe detail:
- "Микронутриенты (на порцию, % РСП)" chips section
2026-05-29 13:38:14 +03:00
9384bb8d2e Goal markers, 7-day kcal balance, weight-calorie correlation, food cost
Idea 2 — Hill progress markers:
- goal_markers table, CRUD, map-based placement
- Session detail shows closest approach to each marker
- Progress comparison with previous sessions

Idea 6 — 7-day kcal balance widget:
- Rolling average below summary cards on Today screen
- Green = deficit, red = surplus

Idea 7 — Weight vs calorie correlation chart:
- Dual Y-axis: weight (left) + 7-day rolling balance (right)
- Added to stats section in renderStatsCharts

Idea 8 — Food cost of workout:
- Shows session kcal as portions of frequently eaten foods
- "460 kcal = 2.3 x гречка или 1.4 x творог"
2026-05-27 17:45:50 +03:00
1d6efcd851 Tire pressure Crr, lap comparison, auto climb segments, bike maintenance
Idea 1 — Tire pressure:
- tire_pressure_bar field in bikes table and bike modal
- Crr corrected by sqrt(3.0 / pressure), reference 3.0 bar
- Passed through all opts chains (recording, recovery, recalculate)

Idea 3 — Lap comparison:
- "Сравнить с..." button in session detail
- Shows sessions with same type and similar distance (+-30%)
- Overlay: current track green/red (faster/slower), comparison cyan dashed
- Nearest-neighbor speed matching between tracks
- Summary: time and speed difference

Idea 4 — Auto climb segments:
- computeStats returns segments[] with type (climb/flat/descent)
- Gradient > 2% = climb, < -2% = descent, else flat
- Min 50m per segment, adjacent same-type merged
- Stats per segment: distance, elevation, gradient, duration, speed
- UI section in session detail with colored icons

Idea 5 — Bike maintenance log:
- bike_maintenance table (date, description, mileage, next_service_km)
- Total mileage from GPS sessions per bike
- Overdue indicator when current mileage >= next_service_km
- Add/delete entries in bike modal
- CRUD functions on window.DB
2026-05-27 16:45:33 +03:00
e4f3d3e3ca Alternative units, analytics charts, elevation smoothing, batch GPS update
Alternative food units (Phase 3a):
- serving_units table: шт, ст.л., ч.л., стакан, мл
- product_units junction: eggs linked to шт (60g)
- Unit selector chips in food modal after product selection
- Display "2 шт" instead of "120г" in food log when unit is set
- amount_g stays canonical, unit info is display metadata

Analytics (Phase 4):
- Stats section on Today screen with period selector (30d/90d)
- Weight trend chart (line, purple)
- Macros donut (protein/fat/carbs with kcal center text)
- Kcal balance bar chart (intake green vs expenditure red, includes BMR)
- Chart fullscreen + zoom support (reuses GPS chart patterns)

GPS improvements:
- Elevation smoothing: 5-point moving average + 1m dead zone
- Fixes inflated elevation gain (440m → ~70m for a real 70m route)
- Batch polyline update: poll loop suppresses per-point addLatLng,
  calls onBatchUpdate to rebuild polyline from sorted points at once
- Eliminates visual jumps when app returns from background

Boot screen: removed verbose migration/GPS status labels,
shows "Загрузка..." only, updates text only during actual seed import
2026-05-23 17:12:19 +03:00
049f4d6546 Unified product search dropdown, UI fixes
- Reusable createProductSearch() component used in food and ingredient modals
- Search dropdown: position:absolute, z-index:100, shadow, scrollable
- Products tab: list scrolls independently (max-height: calc(100vh - 209px - safe areas))
- prod-card flex-shrink:0 prevents collapse in scroll container
- Products tab reverted to normal list (not dropdown) with expanded cards
- Search limit raised to 200, sorted by starts-with-query first
- Ingredient modal: uses createProductSearch instead of manual wiring
- Food modal: uses createProductSearch instead of manual wiring
2026-05-21 00:48:27 +03:00
3e5333cec6 Recipes, nutrients_guessed, seed import fixes, search improvements
Recipes:
- Cooking methods table + 6 presets (boiling, frying, steaming etc.)
- Recipe editor with live KBJU calculation including cooking loss
- Recipe detail view, log-to-diary, ingredient search
- Split food log into "Products" and "Dishes" sections
- Products screen sub-tabs: Продукты / Рецепты

Nutrient enrichment:
- 1620 store products enriched with ION micronutrients (guessed flag)
- Purple tint on guessed food entries, * on daily kcal total
- Seed DB v4 with nutrients_guessed column

Seed import fixes:
- Rust: always refresh seed DB from bundled assets on launch
- Rust: check app_config_dir for main DB (plugin-sql stores there on desktop)
- Rust: ATTACH-based UPDATE to sync nutrients_guessed for existing products
- Rust: read all 27 columns including micronutrients
- Rust: emit seed_progress events + indeterminate progress bar animation
- JS: boot screen with status labels during migrations/import
- JS: nutrients_guessed column created in JS migration as safety net

Search:
- Results sorted by starts-with-query first, then by name length
- Default limit raised to 200
- Modal search results scroll without max-height cap

Bug fixes:
- product_id=0 for custom recipe ingredients (NOT NULL constraint)
- getRecipe JOIN for cooking_method_name
- calcTotals applies cooking loss from selected method
- Ingredient modal targets last overlay (not parent)
- select elements dark theme (color-scheme: dark)
- renderToday wrapped in try/catch
2026-05-21 00:06:50 +03:00
0032c309b7 Seed DB: +5ka (3103) +ION (1078) with micronutrients, GPS fixes, UI improvements
Products:
- 5ka scraper: recursive category BFS, 3103 products imported
- ION scraper (web.ion.ru): fetch-based, 1111 products with full vitamins/minerals
- Seed DB now 13480 products (magnit 8627, 5ka 3103, ion 1078, kdv 672)
- 13 micronutrient columns added: Na, K, Ca, Mg, P, Fe, vit A/B1/B2/B3/C/E, carotene

GPS fixes:
- computeStats sorts points by seq/ts before calculating (fixes wrong elevation/kcal)
- Trail restore on recording screen sorts points
- Old tracks with NULL seq repaired via migration (db.js)
- Activity type saved to localStorage, restored on GPS screen

UI:
- Track points invisible but tappable (popup with time/alt/speed)
- Charts: pointer-events:none on canvas when minimized (page scrolls)
- Navigation history stack (20 entries), back gesture doesn't close app
- Session list sort: added id DESC tiebreaker
2026-05-19 23:53:49 +03:00
b26264538b GPS point ordering + GPX export + chart zoom fixes
GPS background recording:
- Batched writes (10s flush) in native service
- Polling lock prevents parallel processing
- Track by timestamp instead of line count
- Recovery idempotent via max DB timestamp
- Seq numbers on live recorded points

GPX export:
- Button in session detail
- SAF file picker on Android (ACTION_CREATE_DOCUMENT)
- GPS config in metadata (accuracy, minSpeed, minDist)

Charts:
- Pinch zoom enabled at creation (Hammer.js registers handlers)
- touch-action: pan-y blocks pinch in normal view (page scrolls)
- Fullscreen: CSS-only expand (no canvas manipulation)
- Downsampled to 500 points for smooth rendering
- Back gesture collapses fullscreen before closing modal

Build: --real flag finds first non-emulator device
2026-05-16 21:21:13 +03:00
daf71333ff GPX export + GPS background recording fixes
GPX export: button in session detail, standard GPX 1.1 format.

Background GPS fixes:
- Native service: batched writes (10s flush) instead of per-point
  appendText — eliminates race condition with reader
- JS reader: skip corrupt lines, sort by timestamp, track by ts
  instead of line count — handles partial writes and reordering
- Recovery: idempotent import using max DB timestamp
- Live recording: seq numbers on every point
- FileObserver watches correct path (app root, not files/dev.baldav.app)
- Removed ACCESS_BACKGROUND_LOCATION (causes throttling)

Nav: "Трекинг" button on GPS screen when recording active.
2026-05-16 10:49:55 +03:00
f717204ac8 Seed products DB + Settings screen + build fixes
GPS recording overhaul: inline screen (not modal), floating bar
on other screens, manual pause/resume, crash recovery via
localStorage + JSONL file, _processIncomingPoint extracted.

Settings: profile as button, GPS config, permissions, app info.
Products seed DB: 9299 products bundled, imported on first launch.
Build: version/hash in build.rs, signing, install command.
2026-05-15 14:36:22 +03:00
d238af55da Android support + Settings screen + Mobile UX fixes
Settings: profile button, offline maps toggle, GPS permission toggle.
Import: unified GPX/FTB, detect by extension.
Mobile UX: bigger nav, no native alerts, safe area insets.
Track: seq for GPX, stored session stats in detail view.
2026-05-14 14:17:57 +03:00
ff12646844 GPS track rendering + offline maps + UI improvements
Track rendering: seq-based ordering matching FitoTrack v15.6
(insertion order, not relativeTime sort which scatters AGPS points).
Colored time-gradient points with dark contour on track map.

Offline maps: tile cache with online/offline toggle in settings,
download script (tools/download_tiles.js) with parallel curl.

UI: removed useless map from GPS screen, click-to-zoom on detail map,
locate-me button, zoom level display, desktop layout improvements.
2026-05-13 17:53:19 +03:00
1affadb05d Initial commit: BalDav health tracker
Tauri 2 + pure JS + SQLite personal health tracking app.

Nutrition module:
- 9299 products in DB (Magnit SSR scraping + KDV)
- Product search with case-insensitive Cyrillic (name_lower column)
- Food logging from product DB or manual entry
- Supplement tracking with per-pill micronutrients
- Micronutrient % strip on Today screen

Fitness/GPS module:
- FitoTrack .ftb and GPX import with seq-based ordering
- Session detail view: map, speed/elevation charts (Chart.js)
- Offline tile caching (IndexedDB) with region download
- Track rendering with colored time-gradient points

UI:
- Fullscreen modals with back-button (history API)
- Dark theme, desktop-responsive layout
- 5 tabs: Today, Products, Track, Supplements, Profile

Tools:
- Magnit/KDV HTML parsers
- Dedup import with discrepancy detection
- FitoTrack backup analysis
2026-05-13 10:21:24 +03:00