Follow the copy pass: tests, docs, version, SW

The pushed copy pass renames the library heading to "Your freezer"
("Tvůj mrazák" in Czech) and reworks the gate subtitle plus several
Czech strings. Five suites pinned the old heading — regression, i18n,
native, wizard and providers — and now assert the new one; the README
prose and project tree follow. Key parity is unchanged.

Version 0.5.9, SW -> freezmo-v81.
This commit is contained in:
Martin Dvořák 2026-10-14 20:22:12 +00:00
commit 8153fc8dae
8 changed files with 17 additions and 10 deletions

View file

@ -307,7 +307,7 @@ TEXT_MODEL=gpt-oss:120b-cloud
1. Open the app — the **key gate** asks for a key on first visit.
2. Create one at <https://ollama.com/settings/keys>, paste it in and press **Continue**.
3. The gate validates it with a real 1-token request, then drops you into the icebox.
3. The gate validates it with a real 1-token request, then drops you into the freezer.
**Settings → Test connection** re-checks any time and shows the model list.
Where the key lives:
@ -832,7 +832,7 @@ app/
manifest.ts web app manifest (installable PWA)
offline/page.tsx offline fallback served by the service worker
globals.css Tailwind v4 theme, frosty palette, animations, reduced-motion
page.tsx library ("Your icebox")
page.tsx library ("Your freezer")
create/page.tsx upload screen
deck/[id]/page.tsx study feed for a saved deck
homework/page.tsx “Make my homework” (assignment in, homework or guide out)
@ -1423,6 +1423,13 @@ Things I chose while building, with the reasoning:
(measured: 268..1280 at a 1280 window) — a flat pane floating 40px inside the scene read as
"cut off next to the sidebar".
90. **Copy pass on the shell (`lib/i18n-dict.ts`).** A user-authored wording pass: the library heading
is now "Your freezer" (matching the Czech "Tvůj mrazák"), the key gate's subtitle speaks about
grades ("První krok k dobrým známkám"), and several Czech strings read more naturally ("Schovat /
Prodloužit panel", "Vibrace na podporovaných zařízení", "Všechno je uloženo v tomto prohlížeči",
"Vizuální model (překládá fotky na text)", "radši ověř"). The five suites that pinned the old
library heading were updated with it; key parity is unchanged.
89. **Codebase-wide audit (bugs, performance, dead code, edge cases).**
- *Bugs fixed:* Backup schema now accepts `theme: "aero"` (backups exported under Frutiger Aero
used to fail `parseBackup` on import), and `applyImport` in `"replace"` mode now replaces SRS

View file

@ -1,6 +1,6 @@
{
"name": "freezmo",
"version": "0.5.8",
"version": "0.5.9",
"private": true,
"license": "GPL-3.0-only",
"scripts": {

View file

@ -9,7 +9,7 @@
*
* Bumping CACHE_VERSION is how a new deploy invalidates old assets.
*/
const CACHE_VERSION = "freezmo-v80";
const CACHE_VERSION = "freezmo-v81";
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
const ASSET_CACHE = `${CACHE_VERSION}-assets`;

View file

@ -46,7 +46,7 @@ const browser = await launchBrowser();
await page.waitForTimeout(500);
await page.keyboard.press("Escape");
await page.waitForTimeout(600);
check("locale switch to English updates the UI", await page.getByRole("heading", { name: "Your icebox" }).isVisible());
check("locale switch to English updates the UI", await page.getByRole("heading", { name: "Your freezer" }).isVisible());
await context.close();
}

View file

@ -119,7 +119,7 @@ const browser = await launchBrowser();
await page.waitForTimeout(700);
await page.goBack();
await page.waitForTimeout(600);
check("back: returns from the Progress tab", await page.getByRole("heading", { name: "Your icebox" }).isVisible());
check("back: returns from the Progress tab", await page.getByRole("heading", { name: "Your freezer" }).isVisible());
await page.locator("nav.tabbar").getByRole("button").click();
await page.getByRole("dialog").waitFor({ timeout: 6000 });

View file

@ -262,7 +262,7 @@ try {
await page.waitForTimeout(2200);
check(
"gate: a saved provider key lets the app through",
await page.getByRole("heading", { name: "Your icebox" }).isVisible(),
await page.getByRole("heading", { name: "Your freezer" }).isVisible(),
);
check(
"gate: the provider became the study-generation model",

View file

@ -14,7 +14,7 @@ const browser = await launchBrowser();
await page.goto(BASE, { waitUntil: "networkidle" });
await page.waitForTimeout(900);
check("library title", await page.getByRole("heading", { name: "Your icebox" }).isVisible());
check("library title", await page.getByRole("heading", { name: "Your freezer" }).isVisible());
const sidebar = page.getByRole("complementary", { name: "Sidebar" });
for (const row of ["New deck", "New homework", "All decks", "Unorganized", "Biology", "Progress", "Exam mode", "Settings"]) {
check(`sidebar row: ${row}`, (await sidebar.getByText(row, { exact: true }).count()) >= 1);

View file

@ -85,7 +85,7 @@ for (const viewport of [{ width: 390, height: 844 }, { width: 360, height: 740 }
await sampleBar(page, "empty state → create");
await page.getByRole("button", { name: "Cancel" }).click();
await page.waitForTimeout(600);
check("Cancel returns to the library", await page.getByRole("heading", { name: "Your icebox" }).isVisible());
check("Cancel returns to the library", await page.getByRole("heading", { name: "Your freezer" }).isVisible());
await page.getByRole("link", { name: "Homework", exact: true }).click();
await page.waitForTimeout(600);
@ -151,7 +151,7 @@ for (const viewport of [{ width: 390, height: 844 }, { width: 360, height: 740 }
check("reduced: step 1 usable", await page.getByRole("heading", { name: "Your notes" }).isVisible());
await page.getByRole("button", { name: "Cancel" }).click();
await page.waitForTimeout(400);
check("reduced: cancel works", await page.getByRole("heading", { name: "Your icebox" }).isVisible());
check("reduced: cancel works", await page.getByRole("heading", { name: "Your freezer" }).isVisible());
await context.close();
}