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>
Laude — Exercise Reminder
Windows desktop приложение, которое напоминает делать упражнения во время работы за компьютером. Опционально подключается к Dota 2 и после каждого матча превращает статистику (смерти, убийства, ассисты) в количество повторений.
Что внутри
- Гибкие напоминания — любое количество упражнений, интервал от минуты до часов, разные иконки.
- История и стрики — heatmap-календарь активности, ежедневный счётчик, серия дней подряд.
- Тихие часы — окно времени когда напоминания подавляются (например
22:00 → 08:00), с выбором дней недели. - Сделал частично — степпер
−/+в окне напоминания: если ты сделал 5 из 10, в историю запишется честное число. - Игровая интеграция (Dota 2) — Game State Integration читает статистику матча, после Победа/Поражение показывает экран с «причитающимися» повторениями (например
10 смертей × 3 = 30 приседаний). - Apple-style интерфейс — Plus Jakarta Sans + Bricolage Grotesque, iOS-палитра, vibrancy sidebar, spring-анимации, светлая/тёмная/системная тема.
- Два языка — русский и английский, переключение мгновенное.
- Auto-update — приложение само скачивает новые версии из фиксированного
update-channel(проверка каждый час, силент-ретрай при сетевых сбоях).
Скриншоты
TODO: вставить screenshots Dashboard / Reminder / Match summary (light + dark).
Установка
Скачай последний Exercise-Reminder-Setup-X.Y.Z.exe со страницы релизов и запусти. Установщик:
- Создаёт ярлык на рабочем столе и в Пуске
- Сохраняет настройки в
%APPDATA%\Exercise Reminder\ - При запуске поверх существующей инсталляции — обновляет, настройки сохраняются
Windows SmartScreen может предупредить «не доверено» — приложение не подписано code-signing сертификатом. Нажми Подробнее → Выполнить в любом случае.
Разработка
git clone https://xn--90adajar8af4h.xn--p1ai/git/AnRil/laude.git
cd laude
npm install
npm run dev
Полезные команды:
npm run typecheck # tsc по main + renderer
npm run test # vitest в watch-режиме
npm run test:run # vitest один раз (для CI)
npm run build # сборка без NSIS
npm run dist # сборка + NSIS-инсталлятор → release/
npm run release -- -Bump patch # bump версии + tag + push + upload в Gitea
Документ RELEASING.md описывает процесс выпуска новых версий.
Архитектура
- Electron 33 — multi-process: main (Node/scheduler/GSI) + preload (contextBridge) + renderer (React)
- Renderer — React 18, TypeScript 5, Vite 5, Tailwind 3, framer-motion, react-router, zustand
- Persistence — единственный JSON-файл
%APPDATA%\Exercise Reminder\app-state.json(debounced writes) - IPC — типизированные каналы через
src/shared/ipc.ts, обёрнуто preload-ом - i18n — самописная микро-система:
src/renderer/src/i18n/dict.ts(плоский словарь ~200 ключей × 2 языка) + хукuseT() - Auto-update —
electron-updaterсgenericprovider, манифестlatest.ymlлежит в Gitea release attachments - GSI Dota 2 — локальный HTTP-сервер слушает GameStateIntegration коллбэки от Steam, парсит match-end events
Тесты
src/shared/types.test.ts (4)
src/shared/quiet-hours.test.ts (5)
src/renderer/src/lib/format.test.ts (8)
src/renderer/src/lib/history.test.ts (13)
src/main/games/vdf.test.ts (11)
src/renderer/src/i18n/i18n.test.ts (10)
─────────────────────────────────────────
51 ✓
Покрытие: чистые helpers (форматирование, история/стрики, тихие часы, парсер VDF для Steam-конфигов), i18n с плюрализацией для RU/EN, дефолты shared-типов.
Лицензия
Пока не указана. По умолчанию все права защищены. Если хочешь форк/использование — открой issue.
Stack
- Electron · runtime
- electron-vite · build
- React + TypeScript
- Tailwind CSS · стили
- framer-motion · анимации
- lucide-react · иконки
- electron-updater · auto-update
- Vitest · тесты
- Шрифты: Plus Jakarta Sans, Bricolage Grotesque, JetBrains Mono