Commit graph

48 commits

Author SHA1 Message Date
e6674e2014 Fix B9 and Se showing 0%: unit mismatch mg vs mcg
B9 (folate) label said "мкг" but packaging shows "мг" — user entered
0.4 mg, DB stored 0.4 mcg, 0.4/200 = 0%. Fixed: label says "мг",
mult:1000 converts to mcg on save, back to mg on display.
Same for selenium: packaging uses mg, DB uses mcg.

Beta-carotene showing 0% is correct — Daily Men supplement contains
retinol acetate (vit A), not beta-carotene. They are different forms.
2026-05-29 17:53:57 +03:00
22a2f6296d Update PROJECT_MAP, README, session logs with current stats 2026-05-29 17:47:58 +03:00
4d97ba607b Always copy seed DB from assets, VACUUM seed to reclaim space
Size comparison was unreliable — SQLite reuses free pages internally,
so adding products didn't change file size. New seed never copied.
Now: always overwrite on every launch (~8MB, <1 sec).
VACUUM reduced seed from 8.5MB to 8.2MB.
2026-05-29 15:38:10 +03:00
636de9bc25 Add EPA and DHA fields to supplements (omega-3 breakdown) 2026-05-29 15:21:59 +03:00
c986b6af9a Russian names for minerals in supplement form: Кальций, Магний, Железо etc. 2026-05-29 14:59:15 +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
03358964e2 Fix all querySelector('.modal-overlay') to grab last (topmost) overlay
When a sub-modal opens over a parent modal, querySelector returns the
FIRST (parent) overlay, not the new one. Compare items, recipe buttons,
goal markers — none of them got onclick bindings because they searched
in the wrong overlay.

All 11 occurrences replaced with [...querySelectorAll(...)].pop() to
always get the topmost (most recently added) modal overlay.
2026-05-29 10:41:10 +03:00
d5fa37a9fa Fix recipe save: lastInsertId was undefined, ingredients got null recipe_id
tauri-plugin-sql execute() returns { lastInsertId } but it's unreliable
(returns 0 or undefined for INSERT). New recipe got rid=undefined,
then addRecipeIngredient failed with NOT NULL constraint on recipe_id.

Fix: query actual id after insert with SELECT id ORDER BY id DESC LIMIT 1.
Edit worked because rid came from the existing recipeId parameter.
2026-05-29 09:25:30 +03:00
1f53b8fcc2 Add try/catch + alert on modal save button to surface hidden errors 2026-05-28 23:56:37 +03:00
45341ddba4 Real BMR from weight change history, not just formula
Added BMR.estimateRealBmr(weights, foodTotals):
- Uses actual weight delta + total food intake over 30 days
- Formula: realExpenditure = (totalIntake + dWeight_kg * 7700) / days
- 7700 kcal per kg of fat, negative dWeight = weight loss
- Requires 3+ days of data and 2+ weight entries
- Sanity check: rejects < 500 kcal/day as bad data
- Overrides formula BMR when enough data available

renderToday now queries recent weights + food and uses real BMR
if calculated, falling back to Mifflin-St Jeor formula.
2026-05-28 23:17:45 +03:00
2b147ef1dc Fix expenditure showing -- when BMR or profile incomplete
Two issues:
1. expend was null when BMR=null, even if GPS sessions had kcal.
   Now shows activity kcal alone if BMR unavailable.
2. calcBmr returned null if height/birth_year missing, but user
   had weight in profile. Now falls back: weight_log -> latestWeight
   -> profile.weight_kg. Also returns bmr_formula if height missing
   instead of null.

BMR calculation chain:
- bmr_estimated (manual override) takes priority
- Then formula: needs height + birth_year + weight (from any source)
- Then bmr_formula (pre-computed value in profile)
- Last resort: null → expenditure shows activity kcal only
2026-05-28 23:15:35 +03:00
5f0c21741c Fix remaining overlay.remove bypassing winStack
Found 5 places that did overlay.remove() + history.back() directly
instead of going through winStack:
- Session detail recalculate/refresh
- Delete supplement
- Delete bike
- Delete goal marker
- Goal marker add button
- Compare session selection

All converted to: winStack.pop().close() + _skipPops + history.back()
No more direct overlay.remove() outside of winStack close handlers.
2026-05-28 22:42:45 +03:00
4960f2a863 Rewrite fullscreen as history entries: each open=push, each back=pop
Single winStack[] array + single popstate handler replaces:
- Per-modal popHandlers (removed)
- _modalPopHandled flag (removed)
- overlay._pushFullscreen (removed)
- _suspended/resume mechanism (removed)

How it works:
- winPush(type, closeFn): push entry + history.pushState
- winPopTop(): pop top entry, call close(), return true
- closeModal(): pop all entries down to stackBase, history.go(-count)
- _skipPops counter: closeModal's history.go fires popstate — skip N events

Every open (modal, fullscreen map, fullscreen chart) = 1 winPush.
Every back = 1 winPopTop via popstate.
closeModal for buttons = pop N entries + history.go(-N) + skip N pops.

Sub-modals work because stackBase tracks where each modal started.
Ingredient modal pops only its own entry, recipe editor stays.
2026-05-28 22:24:57 +03:00
94ad61f5b9 Fix ingredient modal closing recipe editor — no setTimeout hacks
Root cause: closeModal of sub-modal called history.back(), parent
modal's popHandler was resumed and caught the popstate, closing itself.

Fix: popHandler checks _modalPopHandled flag first. When sub-modal's
closeModal sets the flag before history.back(), parent popHandler sees
it and skips. Global handler also sees it and skips.

Removed suspend/resume mechanism — replaced with simple flag check.
Each popHandler also checks overlay.parentNode for safety.
2026-05-28 14:31:20 +03:00
f395e8ec07 Fix sub-modal (ingredient) closing parent modal (recipe editor)
Problem: ingredient modal's closeModal called history.back(), which
triggered recipe editor's popHandler → closed recipe editor too.

Fix: modal suspend/resume system.
- When sub-modal opens, parent modal's popHandler is suspended
- When sub-modal closes, parent is resumed
- Parent popHandler ignores popstate events while suspended
- closeModal resumes parent before calling history.back()

Also: closeModal now pops exactly 1 history entry (not _historyDepth).
The _historyDepth counter tracks fullscreen entries within the modal,
which are handled by popHandler's decrements. closeModal only needs
to pop the modal's own base entry.
2026-05-27 22:55:43 +03:00
d59013cc62 Fix ingredient modal closing recipe editor
Ingredient modal's onSubmit returned undefined → showModal treated it
as success → closeModal + renderScreen → killed the parent recipe editor.

Fix: ingredient onSubmit returns "norender", showModal checks for it
and skips renderScreen. Modal still closes via closeModal/history.back,
returning to the recipe editor with the new ingredient added.
2026-05-27 20:53:00 +03:00
cb5a916100 Rewrite fullscreen as history entries: each open=push, each back=pop
Every fullscreen (map/chart) inside a modal now pushes its own history
entry via overlay._pushFullscreen(). Back pops it, popHandler collapses.
No more re-pushing, no flag juggling between handlers.

History stack trace:
  click track → push {modal}
  expand map  → push {modalFs}
  back        → pop {modalFs}, collapse map, stay in modal
  back        → pop {modal}, close modal, return to track list
  back        → pop {screen:gps}, return to previous screen

Map toggle: expand pushes via _pushFullscreen, collapse calls history.back()
Chart toggle: same pattern — push on expand, back on X button
closeModal: pops all entries this modal owns (_historyDepth counter)

Fixes: back from fullscreen map/chart no longer closes app,
expand button works correctly after back-collapse
2026-05-27 20:41:33 +03:00
a032af3b00 Fix map fullscreen state sync, closeModal flag, remove duplicate code
Map fullscreen:
- Removed mapIsFullscreen variable, use CSS class as single source of truth
- popHandler and toggleMapFs now both check mapEl.classList
- After back-gesture collapse, expand button works on first click (not second)

closeModal:
- Sets _modalPopHandled flag before history.back()
- Prevents global popstate handler from double-processing the event
- Fixes: back from modal no longer closes the app

Removed duplicate fsChart block (caused SyntaxError)
2026-05-27 19:36:34 +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
7d1e4b3069 Add IDEAS.MD — roadmap ideas for BalDav
8 feature ideas: tire pressure Crr, hill progress markers,
lap comparison, auto climb segments, bike maintenance log,
kcal balance widget, weight-calorie correlation, food cost of climbs
2026-05-27 16:28:05 +03:00
d7527bfd45 Fix back-gesture app exit, screen wake lock, multi-segment tracks
Navigation:
- _modalPopHandled flag prevents global popstate handler from firing
  on the same event that modal popHandler already processed
- Fixes: back from fullscreen map/chart no longer exits the app

Screen wake lock:
- JS: navigator.wakeLock.request("screen") on recording screen
- Kotlin: FLAG_KEEP_SCREEN_ON on GPS start, clearFlags on stop
- Screen stays on while recording is visible in foreground
- Released when navigating away from recording screen

Multi-segment tracks:
- GPS gaps > gapThresholdSec (default 30, configurable) split track
  into separate polyline segments on the map
- Each segment has own green start / red end markers
- Shows clearly where GPS signal was lost vs continuous tracking

Settings:
- "Макс. пауза GPS (сек)" — gap threshold for segment splitting
- Physics constants (aero drag, air density, pedal efficiency)
  moved from hardcoded to GPS.config + settings UI
2026-05-27 15:29:22 +03:00
7cbfd98e27 Physics settings in UI, recipe modal cleanup, weight parseFloat fixes
Settings:
- Aero drag (CdA), air density, pedal efficiency now in GPS config
- Human-readable names: "Аэросопротивление", "Плотность воздуха", "КПД педалирования"
- Saved to localStorage, used in computeStats

Bug fixes:
- Recipe detail: overlay.remove without popHandler cleanup → now uses history.back()
- weightKg parseFloat in startRecording and importGPX (string "88" bug)
- profile.weight_kg stored as number not string
- getDailyActivityTotals: separate params for UNION ALL subqueries

Docs:
- PROJECT_MAP.md — full project structure, DB schema, screens, formulas
- README.md — overview, build instructions, data sources
2026-05-26 02:14:43 +03:00
0701fbd723 Rewrite modal navigation: fix 5 back-gesture bugs
Root causes fixed:
1. closeModal had replaceState AFTER async history.back() — replaceState
   overwrote the current entry before back() fired, causing skip-to-today
2. Chart fullscreen in modals used pushState — each open/close cycle
   added extra history entries, exhausting the stack
3. Map collapse in popHandler didn't call invalidateSize/fitBounds

Changes:
- closeModal() simplified: just remove overlay + history.back()
  No more replaceState race condition
- Chart/map fullscreen inside modals: NO pushState, CSS-only
  popHandler detects .chart-full/.sd-map-full and collapses, re-pushes modal
- Map collapse: uses saved _leafletMap ref and _bounds for proper resize
- Init: renderScreen with fromPopstate=true to avoid navStack duplication
- Modal popHandler searches within overlay only (not global document)

Fixed user-reported bugs:
1. Map collapse keeps zoom → now calls invalidateSize + fitBounds
2. Chart open → back → back = app exit → now back closes chart, back closes modal
3. "<-" goes to Today instead of track list → fixed closeModal race
4. X on chart → back = correct → was already working
5. Chart open → back back = app exit → same fix as #2
2026-05-25 18:18:07 +03:00
30b0ac49f3 Max speed filter in native service, fix recalculate bugs
Native service (Kotlin):
- maxSpeedMs filter: points with speed > max are not recorded
- Config passed as 4th param: start:minSpeed:maxAcc:minDist:maxSpeed
- Default 60 km/h (16.67 m/s)

Settings:
- "Макс. скорость (км/ч)" field in GPS config section
- Saved to localStorage with other GPS settings

Recalculate fixes:
- Used cleaned sorted array instead of original points (was computing
  stats on dirty data with glitches -> hundreds of thousands of kcal)
- parseFloat on weight from profile (string "88" + 12 = "8812" bug)
- seq re-assignment only runs when points were actually deleted
- Glitch points deleted from DB on recalculate (not just skipped)
2026-05-25 15:20:24 +03:00
0dd4a67e7b Sort by timestamp not seq, filter teleportation glitches
Track 22 May showed 31km instead of 12.6km because 4 points had
wrong seq order — native service and WebView GPS interleaved.
Sorting by seq placed them wrong, creating 2km+ teleport jumps.

Fixes:
- All sort calls now use timestamp only (seq was unreliable)
- computeStats skips segments > 180 km/h (GPS teleportation)
- After recalculate: 22 May track should show ~12.6 km
2026-05-25 09:25:50 +03:00
9b5c9a204c Fix string weight causing 40k kcal, navigation, remove egg hardcode
Critical kcal fix:
- Profile weight is stored as string ("88"), not number
- "88" + 12 = "8812" (string concat) → mass 8812kg → 40000 kcal
- Fix: parseFloat(weightKg) at top of computeStats
- Also clamp gradient to +-50% to prevent NaN from GPS spikes

Navigation:
- Chart fullscreen on Today screen now has popstate handler
- Global popstate: collapse any visible .chart-full before skipping stale entries
- Both X button and back gesture properly collapse chart

Removed egg hardcode:
- product_units no longer auto-seeded with egg data
- User adds per-product unit mappings manually
- Eliminates slow LIKE query on 13000 products at every startup

Noted bike settings from user: weight 16kg, wheel 24", chainring 40, cog 18, mixed
2026-05-25 03:00:47 +03:00
606481103c Faster migrations, recalculate fixes point order, egg seed guard
Migrations:
- Batch PRAGMA table_info per table instead of N separate hasColumn calls
- Reduced IPC calls from ~22 to ~10 per startup
- Egg product_units seed only runs once (checks COUNT first)
- Removed unused hasColumn function

Recalculation:
- recalculateSession now re-orders points by timestamp
- Assigns seq = 1,2,3... in timestamp order
- Only updates if order is wrong (skips if already correct)
- Fixes tracks where points were recorded out of order
2026-05-25 02:42:52 +03:00
23db25dc8c Delete session returns to track list without history.back race 2026-05-25 02:34:43 +03:00
165e7e7e9c Weight in profile, nav stack persistence, GPS kcal in daily expenditure
Profile:
- Added weight_kg field to profile modal (was only in weight_log)
- Weight fallback chain: weight_log -> profile -> 80kg

Bike modal:
- Active bike toggle: replaced checkbox with Да/Нет chip buttons

Navigation:
- navStack persisted to localStorage, restored on app restart
- Browser history seeded from saved navStack
- Last screen restored after restart (not always "today")

Daily expenditure:
- getActivityTotalsForDate now includes gps_sessions.total_kcal
- getDailyActivityTotals (stats chart) also includes GPS sessions
- GPS bike rides now appear in daily kcal expenditure

Physics kcal fix:
- Use raw altitude (not smoothed) for gradient in power calculation
- Smoothed altitude was killing real slopes (296 vs 460 kcal)
- Smoothed altitude kept only for displayed elevGain/Loss
2026-05-25 02:33:04 +03:00
4740bdbe11 Bike profiles, physics-based kcal, session recalculation
Bike profiles:
- bikes table: name, weight, wheel, chainring/cog teeth, surface type
- CRUD in settings screen, active bike indicator
- Default bike: 12kg, 26", 42/18, mixed surface
- bike_id linked to gps_sessions

Physics-based kcal (replaces MET*0.97):
- Per-segment power calculation: gravity + rolling (Crr) + aero (CdA)
- Power > 0 = pedaling: mechanical energy / 25% efficiency = metabolic cost
- Power <= 0 = coasting: basal metabolic rate only
- Walk/run: proper MET table from Compendium of Physical Activities
- Raw altitude for gradient (smoothed was killing real slopes, 460 vs 296 kcal)
- Smoothed altitude kept for displayed elevGain/Loss only

Session recalculation:
- "Recalculate" button on session detail page
- Uses current weight + bike settings, re-reads all points
- Bike selector per session (change bike -> auto recalculate)

Other fixes:
- User weight fallback chain: weight_log -> profile -> 80kg
- Theoretical cadence from gear ratio + wheel circumference
- Crash recovery preserves bike data
2026-05-25 02:09:50 +03:00
62cdd7740f Fix back navigation after modal fullscreen, fix SQL operator precedence
Navigation:
- Stale {modal:true}/{chartFull:true} history entries after app restart
  caused back gesture to do nothing (no screen to render, no modal open).
  Now global popstate handler skips these stale entries with history.back().

Rust seed import:
- Fixed SQL operator precedence in nutrients_guessed UPDATE:
  WHERE (guessed IS NULL OR guessed = 0) AND name_lower IN (...)
  was previously parsed as: guessed IS NULL OR (guessed = 0 AND ...)
  which matched ALL products with NULL guessed regardless of name.
2026-05-23 18:39:28 +03:00
18738dc0aa Clean up nutrients_guessed: removed 222 false positives
Removed guessed flag + micronutrients from:
- Pet food, cosmetics, cleaning products (25)
- Chips, crackers, snacks (44)
- Chocolate, candy, cakes, waffles, bars (42)
- Cookies/biscuits (23, kept liver "печень")
- Pelmeni, sausages, kolbasa, kotlety (43)
- Baby food purees, ready meals, baked goods (45)

Kept: dairy (yogurt, kefir, tvorog, milk, smetana, slivki),
eggs, grains, vegetables, fruits, raw meats, fish, oils.
1398 legitimately guessed products remain. Seed version 5.
2026-05-23 18:10:00 +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
c5bf6da79e Android: always refresh seed DB from assets when size differs
Old logic: copy only if file doesn't exist. App updates with
newer seed DB were ignored because the old file was already there.
Now compares file size with asset size and overwrites on mismatch.
2026-05-21 01:10:15 +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
1ad4cd5d1f Chart pinch zoom: only in fullscreen, blocked in normal view via onZoomStart callback 2026-05-16 22:10:02 +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
2ca2181e09 Seed products DB + Settings screen + build fixes
Products seed: 9299 products bundled in products_seed.db, Kotlin
copies from assets to filesDir on Android, Rust imports into main
DB on first launch with version check for future updates.

Settings: profile button, offline toggle, GPS permission, app info
with git hash and build date from build.rs.

Build: version synced to Cargo.toml, unified import (GPX/FTB by
extension), chart zoom Ctrl+scroll only, scroll wheel zoom disabled
on detail map.
2026-05-14 17:35:29 +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