Все UI-строки приложения переведены и переключаются на лету через
Settings → Язык интерфейса.
== i18n архитектура ==
- src/renderer/src/i18n/dict.ts — плоский словарь ru/en с ~190 ключами,
поддержка интерполяции {var} и плюрализации
- src/renderer/src/i18n/index.ts — useT() React hook + чистые
translate/translateN функции (для ReminderApp вне store context)
- Settings.language: 'ru' | 'en', default 'ru'
- Изменение языка применяется немедленно через Zustand reactive update
== Что переведено ==
- Sidebar nav + slogan + status
- Titlebar window controls (aria-labels)
- Dashboard: hero, 3 stat-карточки (Активных / До следующего /
Трекинг матчей), Paused banner, empty state
- Exercises: hero, секции (активные / выключенные), row meta, empty
- Challenges: hero, formula subtitle, warning, row format
«{stat} × {mult} → {exercise}», empty
- Games: hero, status badges (Live/Ready/Queued/Installed/Not found),
queued/no_user banners, dev panel
- Settings: все секции + новый Language selector
- UpdaterCard: все состояния (checking/available/downloading/
downloaded/error/idle) с интерполяцией версии и MB/s
- ReminderApp: kicker «Время тренировки», reps подпись, snooze label
с динамическими минутами, кнопки done/skip
- Match summary: победа/поражение, плюрализация «N челлендж/-а/-ей»
vs «N challenge/-s»
- Format helpers (formatCountdown, formatInterval) — теперь принимают
Language параметр
== Локалезависимая дата ==
Dashboard hero показывает today в текущей локали:
ru-RU → "воскресенье, 17 мая"
en-US → "Sunday, May 17"
== STAT_LABELS bilingual ==
- shared/types.ts: STAT_LABELS_EN + statLabel(stat, lang) helper
- ChallengeResult получил поле stat?: GameStat (для resolve на стороне
renderer'а с актуальным языком, вместо baked-in label)
- main/games/registry.ts кладёт stat в результат
== Тесты ==
- src/renderer/src/i18n/i18n.test.ts: 10 кейсов
* translate: lookup, fallback, interpolation, multi-var, lang fallback
* translateN: ru plural rules (1/21/101 → one; 2-4 → few; 0/5-20 → many)
и en (1 → one, else → many)
- Всего 33 теста зелёные
== Известное ограничение ==
SAMPLE_EXERCISES (5-6 русских "Приседания / Отжимания / ...") остаются
русскими — это seed данных на первый запуск. Английский юзер сразу
переключит язык и сможет переименовать вручную. Делать seed-per-locale
оверкилл — слишком много кода ради малого.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
6 часов было выбрано произвольно как "вежливо для сервера". На
практике слишком долго для backgound-приложения: новый релиз
доезжает до пользователя только через полдня.
Меняем на 1 час — все сравнимые приложения (Discord 30 мин,
Slack 30 мин, VS Code 1 ч) используют похожие интервалы.
Стартовая проверка (5 сек после запуска) остаётся.
Нагрузка минимальна: запрос на latest.yml = 362 байта.
UI текст «Авто-проверка раз в 6 часов» → «раз в час».
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
После того как репозиторий стал публичным, токен в Authorization
header больше не нужен. Убираю __UPDATE_TOKEN__ define из
electron.vite.config.ts и весь связанный код в updater.ts.
Преимущества:
- Никаких секретов в распространяемом .exe
- Билд не требует UPDATE_TOKEN env переменной
- Любой может склонировать и собрать без доп. конфига
== Действие пользователя ==
Если v0.3.5 .exe был скачан кем-то с публичного репо (был доступен
~30 минут до того как мы это поняли) — токен из него можно
извлечь и использовать для записи в репо. Рекомендую ротировать:
1. Gitea → Settings → Applications → удалить старый токен
2. Создать новый, скопировать
3. PowerShell:
[Environment]::SetEnvironmentVariable('GITEA_TOKEN', '<новый>', 'User')
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Корень проблемы 404: репо приватный, Gitea требует Authorization
header для release assets даже на browser_download_url. Без токена
запрос возвращает 404 (не 401), поэтому electron-updater сообщал
"Cannot find channel latest.yml update info".
Решение — embed read-only токена в build:
- electron.vite.config.ts: vite `define` для __UPDATE_TOKEN__
читает process.env.UPDATE_TOKEN на этапе сборки
- src/main/updater.ts: если __UPDATE_TOKEN__ непустой, выставляет
autoUpdater.requestHeaders = { Authorization: 'token ...' }
- Декларация declare const локально в модуле, vite заменяет литерал
Сборка теперь требует:
$env:UPDATE_TOKEN = '<gitea-token>'; npm run dist
Если переменная не задана — токен пустой, auto-update тихо отключается
(статус 'unsupported' не показывается, просто запросы будут падать).
Альтернатива на будущее (без токена в .exe):
1. Сделать репо публичным в Gitea Settings
2. Или сделать только Releases публичными если в этой версии Gitea
есть такая опция
Тогда токен в коде не нужен.
== Важно для существующих пользователей ==
Установленные v0.2.x / v0.3.0-0.3.4 не могут получить апдейт
автоматически — у них в бинаре старый updater без токена и они
продолжат получать 404. Им нужно скачать v0.3.5 .exe вручную
и переустановить. После 0.3.5 auto-update заработает.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Manrope воспринимался слишком строгим и корпоративным. Замена даёт
больше характера и тёплый "попсовый" feel:
- Body/UI: Manrope → Plus Jakarta Sans (мягкие округлые формы 'a' 'g',
очень распространён в современных трендовых приложениях)
- Display/hero: Fraunces → Bricolage Grotesque (variable шрифт с opsz
axis: 24 для нормальных заголовков, 96 для hero — гротеск с
характерными слегка сжатыми формами и большим контрастом штрихов)
- Mono: JetBrains Mono без изменений
Все hero-заголовки пробампаны до 34→40px и font-bold (700), Bricolage
лучше всего смотрится в полужирном/жирном. Sidebar логотип «Laude»
тоже font-bold.
Также:
- body line-height: 1.45 → 1.5 для лучшей читаемости
- Reminder exercise name: 28→30, semibold→bold
- Match summary title: 24→26, semibold→bold
- Sidebar slogan: 12→13/medium, контраст 45→55
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Включает полный реворк UI в стиле Apple iOS/macOS:
- Geist + Instrument Serif шрифты вместо Rajdhani
- Apple HIG палитра (systemOrange, systemGreen, systemRed, true black dark)
- macOS vibrancy sidebar, iOS grouped lists, UISwitch, action sheets
- Spring анимации, active:scale press feedback
Установщик ведёт себя как install-or-update — обновляет существующую
0.2.x/0.3.0 копию с сохранением настроек.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Релиз новой спортивной палитры и адаптивной вёрстки.
- Strava orange + rose цветовая гамма
- Полная адаптивность: collapsible sidebar, drawer на mobile
- 23 unit-теста, typecheck чистый
- Установщик Exercise-Reminder-Setup-0.3.0.exe собран
Установщик ведёт себя как install-or-update: ставится на чистую
систему, обновляет существующую 0.2.x с сохранением настроек.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Подготовка к auto-update и тестам.
- electron-updater для in-app апдейтов через generic provider
- vitest для unit-тестов
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Маркируем новый билд установщика после полного UI-редизайна
(phase 1 + phase 2 esports HUD).
Установщик Exercise-Reminder-Setup-0.2.0.exe уже корректно ведёт
себя как install-or-update:
- appId com.anril.exercise-reminder неизменен → NSIS находит
предыдущую инсталляцию 0.1.x и обновляет её
- deleteAppDataOnUninstall=false → настройки и история юзера
сохраняются при апдейте
- perMachine=false → апдейт без прав админа
- differentialPackage=true → готовность к дифф-апдейтам через
electron-updater (если позже подключим)
Сам .exe не коммитится — release/ в .gitignore (бинарь 85 МБ).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>