Files
laude/CHANGELOG.md
2026-05-22 02:01:09 +07:00

19 KiB
Raw Permalink Blame History

Changelog

Все заметные изменения проекта документируются здесь. Формат основан на Keep a Changelog, проект следует Semantic Versioning.

Unreleased

0.5.5 — 2026-05-22

Большой sweep по ревизии: 4 спринта правок (≈14 пунктов), все 135 тестов зелёные. Главное — UI больше не залипает при retry'ях I/O, GSI порт не зависает в TIME_WAIT после выхода, sandbox включён, шрифты self-hosted.

Security

  • sandbox: true на обоих BrowserWindow. Preload использует только contextBridge + ipcRenderer (sandbox-safe), никаких Node-built-ins. OS-уровневый sandbox изолирует renderer на уровне процессов — даже RCE в зависимости рендерера не получит Node-доступа через preload.
  • CSP ужесточён. Убраны https://fonts.googleapis.com и https://fonts.gstatic.com origins (шрифты теперь self-hosted), добавлены connect-src 'self', base-uri 'self', frame-ancestors 'none'.

Added

  • Self-hosted шрифты. Plus Jakarta Sans, Bricolage Grotesque, JetBrains Mono подключены через @fontsource/* пакеты — в bundle лежат локально, без интернета шрифты работают, CSP без внешних origins. +22 .woff/.woff2 (~500KB) в installer.
  • src/main/logger.ts — структурный logger с уровнями (debug/info/warn/error) и ротацией. Пишет в %APPDATA%/Exercise Reminder/logs/latest.log (≤1MB) и дублирует в console. При 1MB ротируется в prev.log. LAUDE_DEBUG=1 включает debug-уровень. Подключён в hot paths: store, updater, GSI server, registry, dota2 provider — особенно полезно для диагностики «челленджи не срабатывают» (видно token verify, POST_GAME detection, фильтрацию challenges).
  • <html lang> синхронизируется с settings.language через ThemeProvider — screen readers корректно произносят язык.
  • dev:simulateMatchEnd channel вынесен в IPC enum (IPC.devSimulateMatchEnd).
  • test:coverage npm script.

Changed

  • broadcastState больше не шлёт history через IPC. Раньше каждый markDone/snooze отправлял весь state включая до 10k history-записей (~500KB JSON) к каждому BrowserWindow. Теперь AppState (renderer-facing) без history, а PersistedState (internal) с историей. Renderer и так дёргал getHistory() отдельно, поведение не изменилось — только perf.
  • lib/icon.tsx: import * as Lucide (wildcard, ~500KB всех 1500+ иконок в bundle) → explicit named imports + ICON_MAP. В bundle только 18 ICON_CHOICES.
  • ChallengeEditor: multiplier клампится в UI до [0.5, 1000] (совпадает с validate.ts). Раньше save с 9999 молча отклонялся IPC-валидатором.

Fixed

  • atomicWrite spin-loop → async setTimeout. Раньше при retry на EBUSY/EPERM (антивирус, OneDrive) main process замораживался на 50/200/800ms × до 3 итераций ≈ секунда залипания UI. Сейчас async sleep — event-loop живёт. Аналогичный фикс в games/steam-launch-options.ts. Сохранён sync-вариант для flushNow в before-quit (там event-loop уже не работает).
  • before-quit дожидается stopGamesRegistry через e.preventDefault() + app.exit(0). Раньше GSI HTTP server не успевал closeAllConnections до exit, и следующий запуск получал EADDRINUSE на порту 4701 (TIME_WAIT) — GSI молча не работал.
  • IPC getState не мутирует кэш. Раньше state.settings.startWithWindows перезаписывалось напрямую, разъезжаясь с persisted-disk-значением до следующего mutation. Сейчас возвращается поверхностная копия.

0.5.4 — 2026-05-19

Обновление приложения теперь по-настоящему фоновое + почти моментальный рестарт в новую версию.

Changed

  • Скачивание апдейта — фоновое. Раньше клик «Скачать» блокировал кнопку (busy=true) до конца download'а (минуты на медленной сети). Теперь IPC updaterDownload — fire-and-forget, прогресс приходит через события. Пользователь сразу может уйти на Dashboard и продолжать упражнения, апдейт качается в фоне.
  • «Рестарт» — почти моментальный. quitAndInstall(true, true): isSilent=true — NSIS без UI установщика (~1-2 сек вместо ~5-10), isForceRunAfter=true — гарантия что приложение откроется после. Раньше показывался диалог установщика с прогрессом, теперь — только мгновение между закрытием и появлением новой версии.
  • Подсказка на экране скачивания: «можно закрыть это окно, продолжится в фоне». На downloaded-экране: «нажми Рестарт — приложение моментально откроется в новой версии».

0.5.3 — 2026-05-19

Полировка кастомного тайтлбара и размера окна.

Added

  • Maximize/Restore. Средняя кнопка тайтлбара (иконка квадрата) раньше была «спрятать в трей» — выглядела как нативная Windows maximize и сбивала с толку. Теперь это настоящий toggle на весь экран: иконка свапается SquareCopy в зависимости от состояния, aria-label локализован.
  • Double-click по тайтлбару тоже toggleMaximize — стандартный Windows-жест.
  • CLAUDE.md в корне — контекст проекта для будущих сессий Claude Code (стек, архитектура, команды, тех. долг).

Fixed

  • Drag-зона тайтлбара. Окно не двигалось, если хватать его рядом с кнопками свернуть/закрыть. Класс titlebar-nodrag стоял на обёртке кластера с flex-1 basis-0, поэтому пустое место слева от иконок тоже было no-drag. Перенесли no-drag на сами кнопки — теперь тащить можно отовсюду, кроме самих квадратиков.

Changed

  • Минимальный размер окна 900×600 → 1100×700. Гарантирует срабатывание Tailwind lg: (4 hero-stat в один ряд, heatmap и сетка упражнений помещаются без горизонтального скролла).

0.5.2 — 2026-05-19

Большая внутренняя итерация: тройной независимый аудит (~220 находок), закрыты топ-приоритеты. Тестов 53, ESLint и Prettier чистые, typecheck OK.

Added

  • Prettier + ESLint + EditorConfig. Конфиги, скрипты npm run format / format:check / lint, CI-готовые правила. Вся src/ единообразно отформатирована.
  • Error Boundary на двух уровнях: вокруг всего App и вокруг роутов. Крах одной страницы (например, malformed history в HistoryHeatmap) больше не блэнкит окно — показывается локализованный fallback с кнопкой «Попробовать снова». Stack trace только в dev.
  • IPC validation layer (src/main/validate.ts) — hand-rolled схемы для всех renderer-supplied payload (intervalMinutes ∈ [1,1440], reps ∈ [1,9999], multiplier ∈ [0,1000], string-cap 200 chars, enum-валидация для theme/lang/notify-mode/stat, regex для HH:MM, дедупликация quietHours.days). Compromised renderer больше не может засунуть reps: NaN или intervalMinutes: -1 в стор.
  • Schema migrations framework. __schemaVersion в persisted-state, MIGRATIONS map для будущих структурных правок.
  • Modal focus trap + focus restore + aria-labelledby. Tab/Shift-Tab больше не вываливаются на нижний слой; на закрытии фокус возвращается на триггер.
  • Sidebar mobile drawer: Esc закрывает, focus trap внутри, focus restore на гамбургер, role="dialog" + aria-modal.
  • Tray menu i18n — пункты меню следуют settings.language.
  • Bilingual heatmap. Title, легенда, weekday-лейблы и tooltip с плюрализацией (1 повтор / 2 повтора / 5 повторов) — всё через i18n. 7 новых ключей weekday.short.*.
  • CHANGELOG.md по формату Keep a Changelog.

Fixed

  • Critical: данные больше не теряются на corrupt JSON. Раньше catch → makeInitial() молча затирал упражнения/историю. Теперь файл уезжает в app-state.json.corrupt-<timestamp>.
  • Atomic write через .tmp + rename + retry на EBUSY/EPERM (антивирус, OneDrive). Раньше обрыв питания мог дать truncate.
  • HIGH security: GSI server теперь верифицирует auth.token через timingSafeEqual против per-install токена. Раньше эндпоинт был полностью неаутентифицирован — любой локальный процесс мог подделать match-end.
  • HIGH security: shell.openExternal allowlist — только http/https/mailto. Раньше file:/javascript:/steam: уходили в OS handler.
  • HIGH security: dev IPC simulateMatchEnd убран из production билдов (gate на !app.isPackaged + import.meta.env.MODE).
  • HIGH security: GSI server reject Origin/Sec-Fetch-Site — блокирует CSRF от browser-вкладок. Body cap 256 KB (OOM-вектор закрыт). Require application/json. Generic 400 без error-echo.
  • isQuietAt wrap-around + day filter. С 22:00 → 07:00, days=[Mon..Fri] теперь правильно проверяется день начала окна (старт Fri 22:00 → активно ночью Sat 02:00).
  • DST drift в history.ts. Календарная арифметика (setDate) вместо ms-арифметики — на границе DST дни больше не дублируются.
  • Scheduler: broadcastState() после fire, защита от двойной регистрации powerMonitor listeners.
  • Settings IPC chatter. QuietTimesRow держит локальное состояние, IPC летит только на onBlur. Раньше скрабинг времени давал ~5 IPC, каждый переписывал app-state.json.
  • Dashboard «До следующего» показывает при паузе вместо обманчиво тикающего таймера.
  • HistoryHeatmap percentile-bucketing (p25/p50/p85) вместо относительной шкалы — outlier-день больше не схлопывает все нормальные дни в самый слабый бакет.
  • ReminderApp: Enter теперь корректно передаёт adjusted reps (раньше всегда planned). key={exercise.id+nextFireAt} сбрасывает степпер на новом fire. Степпер capped at 5× planned. Space не работает когда фокус на кнопке. Esc закрывает MatchSummary.
  • i18n.translate — split/join вместо regex (var-значения с регулярными метасимволами теперь интерполируются буквально).
  • icon.tsx lookup сужен до ICON_CHOICES — произвольное имя больше не зарезолвится в Lucide.default.
  • UpdaterCard NaN guard на download-progress (electron-updater даёт undefined в ранних событиях).
  • format.ts guard от NaN/Infinity в formatCountdown.
  • updateExercise/updateChallenge стрипают id из patch — рендер не может перезаписать identity.
  • clearHistory(undefined) теперь no-op (нужен явный boundary).

Removed

  • .gitea/workflows/*.yml — без runners оставляли queued runs. Релизим через release.ps1. has_actions на репо выключен.

0.5.1 — 2026-05-18

Fixed

  • Auto-update архитектурно переписан. Раньше publish.url включал ${version} и запекался в каждый билд — установленные копии видели только свой собственный релиз. Введён фиксированный …/releases/download/update-channel, который никогда не меняется.
  • Hourly auto-проверка работает в silent-режиме: транзитные сетевые ошибки (504, TLS drops) больше не показывают красный баннер «Ошибка проверки». Только ручной клик «Проверить» поднимает ошибку.
  • Boot-check ретраит 3 раза с backoff 30s/2m/5m.
  • В Up to date показывается «проверено N мин назад».
  • release.ps1 теперь публикует в три-четыре места одной командой: vX.Y.Z, update-channel, и переданные -BridgeTags для миграции пользователей со старых версий.
  • upload-release-assets.ps1 ретраит curl до 4 раз с backoff на 504 / TLS-сбрасывание; до ретрая проверяет, не залился ли файл на самом деле (Gitea часто принимает body, но таймаутит ответ).
  • Скрипты — ASCII-only (PS5.1 без BOM падает на em-dash).

Removed

  • .gitea/workflows/*.yml — Gitea Actions без настроенных runners оставляли queued runs в репозитории. Релизим через release.ps1.

0.5.0 — 2026-05-18

Added

  • История + стрики. Каждое выполненное упражнение пишется в app-state.json (cap 10k записей, trim oldest 10% на overflow). Heatmap-календарь 12 недель на Dashboard, ежедневный счётчик «сделано сегодня», серия дней подряд (с grace-периодом за вчера).
  • Тихие часы. Окно времени, в которое напоминания подавляются. Поддержка wrap-around (22:00 → 08:00) и фильтра по дням недели.
  • Частичное выполнение. Степпер /+ в окне напоминания: можно отметить «сделал 5 из 10», в историю запишется честное число.
  • README.md на русском — описание, фичи, установка, dev-команды, архитектура, стек.

Changed

  • markDone(id, actualReps?) принимает фактическое число повторений.

Tests

  • +18 тестов (5 для тихих часов, 13 для истории/стриков). Всего 51.

0.4.0 — 2026-05-17

Added

  • Английская локализация. Самописная i18n: плоский словарь ~200 ключей × 2 языка + хук useT() + плюрализация (CLDR rules для RU: one/few/many).
  • Селектор языка в Settings, переключение мгновенное.

[0.3.x] — 2026-05-17

Серия мелких релизов с дизайн-итерациями (Apple iOS / macOS aesthetic): шрифты Plus Jakarta Sans + Bricolage Grotesque, светлая/тёмная/системная тема, vibrancy sidebar, iOS-grouped lists, spring-анимации.

0.2.0 — 2026-05-16

Added

  • Dota 2 Game State Integration: локальный HTTP-сервер парсит callbacks от Steam, после Победа/Поражение показывает «причитающиеся» повторения (например 10 смертей × 3 = 30 приседаний).

[0.1.x] — 2026-05-15 .. 2026-05-16

Первые публичные сборки: ядро напоминаний (упражнения, интервалы, иконки), системный трей, автозапуск с Windows, native-уведомления, NSIS-инсталлятор, auto-update через electron-updater.