Commit Graph

10 Commits

Author SHA1 Message Date
Codex
7c40558cd3 feat(app): add diagnostics and update runtime 2026-06-06 20:42:34 +07:00
Codex
ffe80b62c4 fix: harden reminders and state handling 2026-06-06 02:27:04 +07:00
AnRil
bef733a877 feat(meals): вкладка «Питание» — напоминания о еде по времени суток
Новая модель Meal — напоминание по настенным часам (time HH:MM + дни недели),
в отличие от interval-based Exercise. Отдельная вкладка «Питание» с пресетами
быстрого добавления (Завтрак/Обед/Ужин/Перекус).

- shared: тип Meal, meals в AppState, nextMealOccurrence (DST-safe), SAMPLE_MEALS,
  MEAL_PRESETS; IPC-каналы meal:* + evtFireMeal
- main: валидация (строгая HH:MM-проверка диапазона), store-мутаторы с пересчётом
  nextFireAt, scheduler.checkDueMeals (гейт только globalEnabled, grace-окно 120с,
  игнор тихих часов/ВКС), notifications.fireMealReminder, IPC-хендлеры
- renderer: вкладка Meals + MealEditor (время/дни/иконка), MealReminder в окне
  напоминания (Поел/Отложить, TTS), пункт в Sidebar, маршрут, i18n RU/EN, иконки
  UtensilsCrossed/Soup
- persistence: meals additive (без bump схемы — старые state'ы получают [])
- +24 теста (203 -> 227): nextMealOccurrence, валидаторы приёмов пищи,
  scheduler meal-gating (вкл/выкл, grace, игнор тихих часов)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 23:45:34 +07:00
AnRil
5a9ec04ba8 feat(whatsnew): экран «Что нового» — автопоказ после апдейта + кнопка в Settings
- src/shared/release-notes.ts — статический реестр заметок per-version
  (RU + EN), с тегами new/fix/security/perf для tint'а иконок.
- Settings.lastSeenVersion — версия, для которой пользователь видел
  модалку. Валидатор регэксом /^\d+\.\d+\.\d+(-[\w.]+)?$/.
- IPC.getAppVersion → app.getVersion() для renderer'а.
- WhatsNewModal — список пунктов с цветовыми иконками. Footer-кнопка
  «Понятно» / «Got it».
- App.tsx: после hydrate смотрит lastSeenVersion → current. Если
  расходятся и есть пропущенные заметки → автопоказ. На первой
  записи (lastSeenVersion === undefined) — тихо записываем, без
  модалки, чтобы не бить нового пользователя CHANGELOG'ом.
- Settings → раздел «О приложении» → кнопка «Открыть» показывает
  модалку с заметками всех релизов.
2026-05-22 13:59:29 +07:00
AnRil
a0b89ddf71 feat(#2): адаптивный шедулер — сдвигает напоминания на «хорошие» часы из истории 2026-05-22 13:52:38 +07:00
AnRil
81481f2131 feat(#5): авто-пауза напоминаний во время ВКС (Zoom/Teams/Discord/Webex) 2026-05-22 13:48:42 +07:00
AnRil
a6ae931461 feat(#12): дневная цель — soft cap reps/день, после которого упражнение умолкает 2026-05-22 13:45:37 +07:00
AnRil
68998607e8 feat(#7): категории напоминаний (exercise/hydration/eyes/posture) 2026-05-22 13:39:40 +07:00
AnRil
50c56fec79 feat(#8): TTS-голосовые подсказки в окне напоминания (opt-in) 2026-05-22 13:36:29 +07:00
AnRil
f0dc5b2cc3 feat: a11y + Error Boundary + IPC validation + schema migrations
Second pass through the audit punch-list. ESLint and Prettier now clean
(0 errors, 0 warnings), typecheck clean, 53 tests pass.

ACCESSIBILITY (Modal)
- Full focus trap: Tab/Shift-Tab cycle within the dialog and never
  escape to the underlying page.
- Focus restoration: closing returns focus to the trigger button.
- First focusable child is focused on open (skipping the X button).
- aria-labelledby links the dialog to its <h2> via useId().
- Close button's hardcoded "Закрыть" replaced with i18n key.

ERROR RECOVERY
- Add ErrorBoundary component (class — only way) with localized
  fallback and a "try again" reset button. Stack trace shown only in
  dev. Wrapped around the whole App + a nested boundary around the
  routed pages so a crash in one route doesn't blank the chrome.
- Module-level guard on subscribeToBackend so React 18 StrictMode's
  dev-mode double-mount doesn't subscribe twice.
- Loading placeholder is now blank (was hardcoded Russian "Загрузка…"
  that English users would see during initial hydration).

TRAY i18n
- 5 tray strings now follow the current settings.language. Falls back
  to Russian when the store isn't loaded yet or the lang is unknown.
- refreshMenu() called on settings.language change and on
  pauseAll/resumeAll so the pause label stays in sync with state.

IPC VALIDATION (src/main/validate.ts)
- Hand-rolled validators for every renderer-supplied payload:
  exercise input/patch, challenge input/patch, settings patch, id,
  actualReps, snoozeMinutes. Range-check numeric fields
  (intervalMinutes ∈ [1, 1440], reps ∈ [1, 9999], multiplier ∈ [0,
  1000], snooze ∈ [1, 1440]), cap string lengths at 200, restrict
  enums (theme/lang/notify-mode/stat) to known values, validate
  quietHours.from/to with HH:MM regex and dedup quietHours.days.
- Every ipcMain.handle for mutations now runs the validator first and
  returns null on rejection instead of pushing junk into the store.
  A compromised renderer can no longer corrupt persisted state via
  out-of-range numbers or wrong-type fields.

SCHEMA MIGRATIONS (src/main/store.ts)
- Add __schemaVersion field to persisted state with CURRENT = 1.
- MIGRATIONS map: { 0: (s) => s } as a no-op seed; future structural
  changes (e.g. quietHours shape revision) get a single explicit slot.
- runMigrations() applies migrations in order; coerce() normalises the
  result into a fully-formed AppState. Both first-write and every
  flush() persist the version field.

EXHAUSTIVE-DEPS WARNINGS
- Dashboard: memoise `exercises` so downstream useMemos don't fire on
  every parent render; gate the history fetch on exercises change
  instead of any state change.
- HistoryHeatmap: wrap `weeks` in useMemo so monthLabels' deps are
  stable.

LINT POLISH
- updater.ts: refactor a Prettier-vs-no-extra-semi conflict by
  extracting the cast into a local binding.
- Remove dead import of `Challenge` from ipc.ts (now imported via
  validators).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 23:21:27 +07:00