2 Commits

Author SHA1 Message Date
AnRil
43ebc8d74c fix(updater): запекаем токен в build для приватного Gitea repo
Some checks failed
CI / Typecheck + Tests (push) Has been cancelled
CI / Build (Windows) (push) Has been cancelled
Release / Build installer + publish release (push) Has been cancelled
Корень проблемы 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>
2026-05-17 18:42:58 +07:00
AnRil
ee2dc19daa release(v0.3.4): шрифты — Plus Jakarta Sans + Bricolage Grotesque
Some checks failed
CI / Typecheck + Tests (push) Has been cancelled
CI / Build (Windows) (push) Has been cancelled
Release / Build installer + publish release (push) Has been cancelled
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>
2026-05-17 18:36:59 +07:00
13 changed files with 60 additions and 28 deletions

View File

@@ -2,9 +2,18 @@ import { resolve } from 'node:path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
// Bake the Gitea update token into the main bundle at build time so
// electron-updater can fetch latest.yml from the private repo. The token
// only needs read access to releases. Falls back to empty string if not set
// (dev/local builds), in which case auto-update is silently disabled.
const updateToken = JSON.stringify(process.env.UPDATE_TOKEN ?? '')
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
define: {
__UPDATE_TOKEN__: updateToken
},
resolve: {
alias: {
'@shared': resolve('src/shared')

View File

@@ -1,6 +1,6 @@
{
"name": "laude",
"version": "0.3.3",
"version": "0.3.5",
"description": "Exercise reminder — Windows desktop app",
"main": "out/main/index.js",
"author": "AnRil",

View File

@@ -3,6 +3,10 @@ import { autoUpdater } from 'electron-updater'
import { IPC } from '@shared/ipc'
import type { UpdaterStatus } from '@shared/types'
// Build-time defined in electron.vite.config.ts.
// Read-only Gitea token, used to fetch latest.yml from the private repo.
declare const __UPDATE_TOKEN__: string
let currentStatus: UpdaterStatus = { kind: 'idle' }
let wired = false
let checkInterval: NodeJS.Timeout | null = null
@@ -38,6 +42,14 @@ export function initUpdater(): void {
autoUpdater.autoInstallOnAppQuit = true
autoUpdater.allowDowngrade = false
// Gitea private-repo release assets require a token in Authorization
// header — without it Gitea responds 404 (not 401) on download URLs.
if (typeof __UPDATE_TOKEN__ === 'string' && __UPDATE_TOKEN__.length > 0) {
autoUpdater.requestHeaders = {
Authorization: `token ${__UPDATE_TOKEN__}`
}
}
autoUpdater.on('checking-for-update', () => setStatus({ kind: 'checking' }))
autoUpdater.on('update-available', (info) => {

View File

@@ -7,7 +7,7 @@
<title>Exercise Reminder</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,600;12..96,700;12..96,800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
</head>
<body>
<div id="root"></div>

View File

@@ -142,7 +142,7 @@ function ExerciseReminder({
<div className="text-[13px] uppercase tracking-[0.18em] text-accent font-bold">
Время тренировки
</div>
<h1 className="font-serif text-[28px] leading-tight tracking-tight mt-2 mb-3 font-medium">
<h1 className="font-serif text-[30px] leading-tight tracking-tight mt-2 mb-3 font-bold">
{exercise.name}
</h1>
@@ -238,7 +238,7 @@ function MatchSummaryView({
<Gamepad2 size={26} strokeWidth={2} />
)}
</motion.div>
<h1 className="font-serif text-[24px] tracking-tight font-medium">
<h1 className="font-serif text-[26px] tracking-tight font-bold">
{won ? 'Победа' : lost ? 'Поражение' : 'Матч завершён'}
</h1>
<p className="text-[13px] text-text/65 mt-1.5 font-medium">

View File

@@ -97,10 +97,10 @@ function SidebarContent({ onNav }: { onNav?: () => void }): JSX.Element {
<>
{/* Brand */}
<div className="px-5 pt-7 pb-6">
<div className="font-serif text-[34px] leading-none tracking-tight font-medium">
<div className="font-serif text-[36px] leading-none tracking-tight font-bold">
Laude
</div>
<div className="text-[12px] text-text/45 mt-2 tracking-tight">
<div className="text-[13px] text-text/55 mt-2 font-medium">
Двигайся осознанно
</div>
</div>

View File

@@ -51,7 +51,7 @@ export default function ChallengesPage(): JSX.Element {
<div className="text-[14px] text-text/65 font-semibold">
Правила за матч
</div>
<h1 className="font-serif text-[32px] sm:text-[36px] leading-[1.05] tracking-tight mt-1 font-medium">
<h1 className="font-serif text-[34px] sm:text-[40px] leading-[1.02] tracking-tight mt-1 font-bold">
Челленджи
</h1>
<p className="text-[15px] text-text/65 mt-2 font-medium">

View File

@@ -72,7 +72,7 @@ export default function Dashboard(): JSX.Element {
<div className="text-[14px] text-text/65 font-semibold capitalize">
{today}
</div>
<h1 className="font-serif text-[32px] sm:text-[36px] leading-[1.05] tracking-tight mt-1 font-medium">
<h1 className="font-serif text-[34px] sm:text-[40px] leading-[1.02] tracking-tight mt-1 font-bold">
Сегодня
</h1>
</div>

View File

@@ -25,7 +25,7 @@ export default function Exercises(): JSX.Element {
<div className="text-[14px] text-text/65 font-semibold">
Программа
</div>
<h1 className="font-serif text-[32px] sm:text-[36px] leading-[1.05] tracking-tight mt-1 font-medium">
<h1 className="font-serif text-[34px] sm:text-[40px] leading-[1.02] tracking-tight mt-1 font-bold">
Упражнения
</h1>
</div>

View File

@@ -64,7 +64,7 @@ export default function GamesPage(): JSX.Element {
<div className="text-[14px] text-text/65 font-semibold">
Трекинг матчей
</div>
<h1 className="font-serif text-[32px] sm:text-[36px] leading-[1.05] tracking-tight mt-1 font-medium">
<h1 className="font-serif text-[34px] sm:text-[40px] leading-[1.02] tracking-tight mt-1 font-bold">
Игры
</h1>
<p className="text-[15px] text-text/65 mt-2 font-medium leading-relaxed">

View File

@@ -24,7 +24,7 @@ export default function SettingsPage(): JSX.Element {
<div className="text-[14px] text-text/65 font-semibold">
Конфигурация
</div>
<h1 className="font-serif text-[32px] sm:text-[36px] leading-[1.05] tracking-tight mt-1 font-medium">
<h1 className="font-serif text-[34px] sm:text-[40px] leading-[1.02] tracking-tight mt-1 font-bold">
Настройки
</h1>
</div>

View File

@@ -67,7 +67,7 @@ body,
body {
font-family:
'Manrope',
'Plus Jakarta Sans',
-apple-system,
'SF Pro Text',
'Segoe UI Variable Text',
@@ -79,28 +79,38 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 14px;
line-height: 1.45;
line-height: 1.5;
letter-spacing: -0.005em;
}
/* Display — same Manrope but slightly tighter for headings */
/* Display — Bricolage Grotesque for headings and brand. Variable opsz axis
gives bigger glyphs slightly more character. */
.font-display {
font-family:
'Manrope',
'Bricolage Grotesque',
'Plus Jakarta Sans',
-apple-system,
'SF Pro Display',
'Segoe UI Variable Display',
system-ui,
sans-serif;
font-optical-sizing: auto;
font-variation-settings: 'opsz' 24;
letter-spacing: -0.02em;
}
/* Serif — Fraunces for hero titles with optical-size axis */
/* Serif → repurposed for the "hero" big titles. Same Bricolage but with the
opsz axis pushed to 96 for distinctive display feel. */
.font-serif {
font-family: 'Fraunces', 'Iowan Old Style', 'New York', Georgia, serif;
font-family:
'Bricolage Grotesque',
'Plus Jakarta Sans',
-apple-system,
'SF Pro Display',
system-ui,
sans-serif;
font-optical-sizing: auto;
font-variation-settings: 'opsz' 144;
letter-spacing: -0.025em;
font-variation-settings: 'opsz' 96;
letter-spacing: -0.035em;
}
.font-mono-num {

View File

@@ -34,7 +34,7 @@ export default {
},
fontFamily: {
sans: [
'Manrope',
'Plus Jakarta Sans',
'-apple-system',
'SF Pro Text',
'Segoe UI Variable Text',
@@ -43,19 +43,20 @@ export default {
'sans-serif'
],
display: [
'Manrope',
'Bricolage Grotesque',
'Plus Jakarta Sans',
'-apple-system',
'SF Pro Display',
'Segoe UI Variable Display',
'system-ui',
'sans-serif'
],
serif: [
'Fraunces',
'Iowan Old Style',
'New York',
'Georgia',
'serif'
'Bricolage Grotesque',
'Plus Jakarta Sans',
'-apple-system',
'SF Pro Display',
'system-ui',
'sans-serif'
],
mono: [
'JetBrains Mono',