Commit graph

21 commits

Author SHA1 Message Date
a8c752828e Drop the sticky bar: the Freeze my deck CTA now sits in the flow like the homework one
The last visible difference between the two CTAs was the container: the
deck button lived in a full-bleed sticky strip (translucent background,
hairline top border, its own padding), which also tinted the disabled chip
differently. The homework button sits plainly in the page flow.

- The CTA block is now a plain space-y-2 stack at the end of the form:
  full-width button + centred helper line, exactly like /homework.
- /create's page padding switches from the sticky-era .pb-cta (9rem, phone
  override 4.6rem) to .pb-tabbar, and lg:pb-20 to lg:pb-16 — identical to
  the homework page. The now-unused .pb-cta rules are gone.
- Result: same button width (358px @390 / 688px @1280), height, radius,
  helper placement and trailing space on both pages; neither has a sticky
  ancestor any more.

Service worker: freezmo-v16.

Verified: CTA-flow suite 26/26 (below-the-fold at rest, no sticky ancestor,
full-width, helper centred underneath, width/height/radius/trailing-space
parity at 390px and 1280px), desktop regression 74/74, mobile 51/51,
i18n 27/27. Lint + build clean.
2026-10-09 10:52:23 +00:00
9d6ef90f02 Keep the library header steady across tabs; simplify the empty states
Header & action bar
- Import JSON no longer disappears on the Homework tab: it sits directly
  under the "Your icebox" header/subtitle, above the tab switcher, on every
  tab — so switching pools no longer shifts the layout or hides actions. It
  is one secondary button at all sizes now, not a quiet link when empty.
- The decks-only "Homework Solver" header action is gone; the homework tab's
  empty-state CTA and the drawer cover starting a new solve.

Empty states
- Dropped the "Using your own Ollama models — check the connection in
  Settings." line (and its two dictionary keys) everywhere.
- Both tabs share one structure: ice icon -> heading -> short description ->
  primary CTA button, starting at the same height:
    Study Decks: "No decks yet" -> "Freeze a page of notes into flashcards
    and quizzes." -> "Create study deck"
    Homework: "No homework yet" -> "Solved assignments and step-by-step
    guides show up here." -> "Solve homework"
- CTA labels read as actions on the standardized nouns; removed the now
  unused actionStudyDeck/actionHomework keys.

Tabs
- Link segments are 44px tall at every breakpoint (min-height in the base
  .segmented > a rule).

Service worker: freezmo-v11.

Verified: library-consistency suite 49/49 (Import visible with a fixed
position on both tabs, no Ollama text, empty states at the same y, primary
ice CTA buttons >=44px, deep links, deletion, redirects, desktop, 360px, axe
en+cs light+dark), mobile 51/51, i18n 27/27, desktop regression 74/74.
Lint + build clean.
2026-10-08 21:50:18 +00:00
8c44f47881 Separate Study Decks and Homework in the library; drop the /create mode switch
Library ("Your icebox")
- Two tabs, never mixed: Study Decks (the folder + card library) and Homework
  (saved solutions and step-by-step guides). The active tab lives in the URL
  (/?tab=homework), so the sections are linkable and survive a reload.
- Decks tab keeps Import, the folder bar, stats and its own empty state — now
  a single decks-only CTA.
- Homework tab lists saved results as cards (mode icon, date, step count,
  preview) with Open and delete-with-confirm; its empty state has its own CTA
  ("Homework Solver") that opens the solver directly. Opening a card
  deep-links to /homework?open=<id>, which the screen opens on arrival.
- .segmented now also styles link segments (same control as settings).

/create
- The redundant "Study Deck | Homework Solver" toggle is gone: /create is the
  deck uploader only, and the bottom bar's + goes straight to it. Homework has
  its own screen, reached from the library tab, the drawer and CTAs.

Copy
- New keys (en + cs): tabDecks, tabHomework, homeworkHeroTitle, plus homework
  empty-state/open/step-count strings. The homework hero is now "Solve your
  homework" / "Vyřeš svůj úkol" — no study-deck text in the homework flow.
- Removed the now-unused ModeSwitch component and homework.title key.

Service worker: freezmo-v10.

Verified: library-tabs suite 37/37 (tab switching, context empty states,
deep-link opening, deletion, no switch on /create, heroes, redirects, desktop
grid, 360px + 44px targets, axe contrast en+cs light+dark), mobile 49/49,
i18n 27/27, desktop regression 74/74. Lint + build clean.
2026-10-08 21:26:08 +00:00
4ac2f33e3f Fix the dead space between /create's sticky CTA and the phone tab bar
On phones the "Freeze my deck" strip floats one tab-bar height (4.6rem)
above the viewport bottom, but the page reserved env + 9rem of trailing
room — so at the end of the scroll the strip docked ~70px higher than where
it had been floating, leaving a visible cavity above the tab bar.

Phones now get exactly the sticky offset (env + 4.6rem) as trailing space:
the strip settles where it was floating and the bar is never double-counted.
Tablet/desktop (>=640px) keep the roomier 9rem — unchanged.

Verified at 360/390px: gap CTA-to-bar 70.3px -> 0.3px, ~70px less dead
scroll; tablet 800px and desktop 1280px measurements identical to before.
Service worker: freezmo-v7.
2026-10-08 19:06:36 +00:00
ef2a6a3589 Add “Make my homework”, and finish the mobile polish pass
Homework (/homework — entry points in the drawer and on the create screen)

- Assignment intake reuses the study pipeline: pasted text, photos
  (camera/picker, compressed in the browser) and documents (PDF/DOCX/PPTX/
  ODT/text) through lib/image + lib/documents, the same vision step and the
  same language guard. Raw files never leave the device — only text and JPEG
  data URLs reach the server.
- Two large choice cards: “Just make it” returns the finished homework
  (answer only, plain text, copy + make it again); “Explain how to make this
  homework” builds an interactive guide.
- The guide is one step per screen in the study-feed snap layout:
  explanation, three progressive hints (nudge -> hint -> the answer) and an
  optional checkpoint rendered by the existing activity components (graded
  through /api/grade). The last screen assembles the solution from the step
  answers, with a copy button. Checkpoints carry xp 0 so the guide stays free
  of XP/streak chrome.
- Results live in freezmo.homework.v1 with a small history (open/delete);
  “Make it again” re-runs from the stored assignment text (the vision
  transcription when photos were used) — no original files are stored.
- The answer and the guide are written in the assignment's language, never
  translated into the UI language.

Server

- POST /api/homework: NDJSON stage events like /api/generate, the same
  http-guard, auth checked before parsing, and its own RATE_LIMIT_HOMEWORK
  (documented in .env.example + the README table).
- New prompts in lib/prompts.ts and zod schemas in lib/schemas.ts with the
  same single repair retry; TEXT_MODEL for content, VISION_MODEL only for
  photos. New shared client pieces: useNotesIntake, lib/homework store,
  lib/clipboard, and a shared NDJSON reader in lib/client-api.

Also finishing the mobile polish pass from the previous request

- Phone ice scene: the top-left orb moved behind the glass header (it sat
  behind the page heading/subtitle). The settings sheet's scroll cue is now a
  bare chevron inside a content fade (mask) instead of a chip over the status
  banner, and the API key card's note/disclosure/actions are tightened.
- theme-color now follows the in-app theme (SSR metas still fall back to the
  OS preference); apple-mobile-web-app-status-bar-style stays “default”
  because iOS fixes the status-bar text colour at launch.

Verified: 78/78 homework checks (text/photo/PDF, both modes, EN+CS, dark+light,
360/390px, copy, history persistence, storage privacy, API language, axe), plus
the existing suites re-run (mobile 47/47, pixel contrast 0 below AA, i18n 27/27,
desktop regression 72/72). Lint + build clean; service worker freezmo-v6.
2026-10-08 18:30:33 +00:00
9fb728d735 Mobile UI pass: sheet swipe, ice-scene layering, contrast, touch targets
Phones (<=639px) only for layout/gestures; the theme-level fixes apply
everywhere, as they are token/design changes.

Settings sheet
- Swipe-to-dismiss reusing the InstallSheet gesture: the sheet follows the
  finger, closes past ~90px or on a fast flick, snaps back otherwise. Drag
  starts only from the handle/header and only while the body is scrolled to
  the top. Backdrop tap still closes.
- Fixed header + scrollable body: pb-safe + spacer, so nothing is clipped;
  a scroll cue (chevron) fades in while more content is below and out at
  the end.
- Show/Save stack under the field at 44px on phones; close, Test connection
  and Remove key are 44px too. Remove key is now a rose destructive action
  with a confirmation that the full-screen key setup comes back.
- API key note split into a short privacy line plus a collapsible
  'Using the local daemon?' tip (OLLAMA_AUTH=local / .env.local / signin).
- Fixed the missing space in the English tip ('ollama signin instead.').
- Health: kept the auto-check and banner; added a loading row and a
  'Checked just now' / 'Checked at HH:MM' freshness label (also for errors).

Ice scene / readability
- Verified the orbs were never above content (fixed z-index:-1 layer,
  pointer-events:none); they were just shining through the glass. Phones get
  lower blob/orb opacity and orbs tucked to the edges; the bottom-right orb
  is moved out from under the tab bar and the tab bar paints a more opaque
  surface so its labels hold contrast.

Contrast / themes
- Dark --text-subtle raised to #9db1d0 (8.6:1 page, 7.2:1 glass); helper
  text now uses the tokens.
- Disabled buttons are a visible muted chip (.btn-disabled) instead of a
  40%-opacity ghost.
- One ice primary gradient in both themes; danger/success now actually show
  their colour (bg-none strips the primary background-image that used to
  paint over !bg-rose-600).
- Light mode: cool card rims instead of white-on-white, stronger shadow.

Redundancy / touch
- Removed only the header gear; the hamburger drawer (folders, Settings,
  theme) and the tab-bar Settings stay.
- Theme toggle is a bordered chip with an action aria-label (drawer row too).
- Empty library hides the folder pills, demotes Import to a quiet link and
  keeps a single primary CTA. 44px targets for nav rows, segmented control
  and folder pills on phones.
- Fixed a pre-existing 16px horizontal overflow (header -mx-4 vs a
  padding-less parent) and let info values wrap instead of truncating.

Verified: mobile suite 47/47 (EN+CS, 360/390px), pixel-sampled contrast
audit 0 runs below WCAG AA (8 runs, both themes/locales; axe marks the glass
'incomplete'), i18n 27/27, desktop regression 72/72, lint + build clean.
Service worker cache bumped to freezmo-v5.
2026-10-08 17:37:51 +00:00
52cce42cde Frosted ice glass restyle
Rebuilds the look around the spec: an ice scene, one shared glass
recipe, icy accents and glass on every large surface. Visual only —
layout, behaviour, copy and functionality are untouched.

Background
- .ice-scene: fixed, z-index -1, pointer-events none. Navy base
  (#070b16 to #0c1426) in dark, pale (#f4f9ff to #e6f1fb) in light,
  four drifting glacier blobs (#38bdf8/#60a5fa/#818cf8, 30-60s
  transform-only loops), five glossy ice orbs (40-220px radial
  gradients with a highlight at 30% 25% and a blue shadow) placed so the
  glass blurs them, and an SVG feTurbulence grain at 4-5%.
- prefers-reduced-motion stops the drift; phones get two blobs and two
  orbs fewer.

Glass
- One shared recipe (.glass-panel, .card, .sidebar-surface,
  .study-chrome, .tabbar, .glass, .gate-card): rgba(255,255,255,.06)
  dark / .55 light, blur(24px) saturate(140%) (16px on phones), a bright
  1px rim, an inset top highlight and a blue-tinted outer shadow.
- Nested glass (chips, inputs, secondary buttons) is more opaque and
  never adds a second blur layer. @supports fallback for no
  backdrop-filter. Radii: 24-28px cards/sheets, 14px inputs/buttons,
  pills for chips. Cards hover brighter + cyan rim + 2px lift, 180ms.

Colour and type
- Accent is glacier cyan (#38bdf8) to ice blue (#0ea5e9); progress bars
  and selected states use the gradient; the active sidebar item is a
  frosted highlight with a cyan rim.
- Primary CTA: icy gradient — white to #bfe3ff with ink text in dark,
  deep blue in light (biased so the label sits on the darker half and
  keeps 5.6:1).
- Leftover slate/black greys replaced with navy-tinted frost tokens.
- Headings are semibold with tighter tracking.

Screens
- Dashboard: the sidebar is one tall inset rounded glass panel; deck
  cards are glass.
- Gate: one centred glass card over the scene, orbs visible behind it.
- Sheets/modals: glass over the dimmed, lightly blurred backdrop.
- Study view: question card more opaque (.glass-solid); top bar and
  pager are slim glass pills.

Perf: measured on a phone viewport with 4x CPU throttling. The animated
blobs' filter: blur() was the whole cost (150ms vs 33ms per frame), so
they are soft radial gradients with no filter now; idle 60fps, ~17ms
median while scrolling.

Tests updated for the new tokens: contrast 18/18 AA, deck chrome 15/15,
key gate 49/49, install sheet 64/64, deck fixes 15/15, retry 14/14,
zoom 6/6, final ux 12/12, security 8/8, pwa 16/16.
2026-10-08 10:11:55 +00:00
586e4b5a40 Revert the last two glass iterations
The colourful blob wash (92fe685) and the flat "straight ice" pass
(5d05592) both missed. Reverting both to get back to the icon-driven
navy palette before rebuilding the look as a proper frosted ice glass
design.
2026-10-08 09:27:42 +00:00
5d0559212b Straight ice: flat backdrop, crisp translucent panels, barely any gradients
The colourful multi-blob wash was too much and still didn't read as
frosted glass. Replaced with a cleaner material approach:

- Backdrop is now flat with a single faint top highlight (the only
  gradient left in it); the animated blobs and the four colour radials
  are gone.
- Panels are crisper and more translucent: cards 62%, panels 66%,
  sidebar 64% (now flat, no vertical gradient), study chrome 68%, tab
  bar 70% — with a bright 1px edge (--glass-edge), a light-from-above
  sheen (--glass-sheen) and the inset top highlight.
- Light mode is lighter: page #eef3fa pale ice, sidebar and cards
  frosted white; dark mode is a flatter, slightly lighter navy
  (#121a2c page, #1a2337 surfaces).
- Key-gate glows toned right down to match.

Contrast re-audited against the effective frosted backgrounds: 21/21 AA.
Suites: deck chrome 15/15, deck fixes 15/15, final ux 12/12, key gate
49/49, install sheet 64/64, retry 14/14, zoom 6/6, security 8/8, pwa
16/16.
2026-10-08 09:20:22 +00:00
92fe68524b Make the frosted glass actually visible
The wash was far too faint for backdrop-blur to register, so the glass
read as flat navy. Now:

- The backdrop is a real colour field: four saturated blue/periwinkle/
  cyan radial blobs over the navy base, plus two slow animated blobs at
  higher opacity and less pre-blur, placed behind the content column.
- Surfaces are properly translucent instead of near-opaque: cards 62%,
  glass panels 68%, sidebar 68%, list groups 70%, study chrome 72%,
  tab bar 74%, mobile header 62%.
- Glass gets a specular top edge (inset 0 1px 0 --glass-top) so the
  surface catches light like real glass.
- Light --text-subtle darkened to #55647f so text still clears AA on the
  tinted glass (4.84:1 on the most saturated sidebar area).

Audit updated to the tinted backgrounds: contrast 21/21. Suites: deck
chrome 15/15, deck fixes 15/15, final ux 12/12, key gate 49/49, install
sheet 64/64, retry 14/14, zoom 6/6, security 8/8, pwa 16/16.
2026-10-08 08:01:14 +00:00
979f3ddc39 Navy theme from the app icon, frosted glass, and the new mark in-app
The icon's artwork is now the source of truth for the palette:

- frost-* is a navy-tinted neutral scale and accent-* is the icon's
  snowflake blue; light page #f4f7fd / sidebar #e9eff9 / border
  #dce4f1, dark page #0e1424 / sidebar #0a0f1c / surfaces #171f33.
- Surfaces are frosted glass: .card, .glass-panel, .list-group and
  .sidebar-surface are translucent with backdrop-blur/saturate, and the
  page wrapper no longer paints an opaque --page, so the fixed navy/blue
  backdrop reads through the sidebar, cards, chrome and sheets.
- The primary CTA is the icon's blue (#1d61ab in light, #59a8f7 in dark
  with ink text).
- The gate stage, fields and glows are navy/blue; viewport theme-color
  and the manifest background/theme colour are #0e1424.
- The old monochrome snowflake tile is gone from the brand: sidebar,
  header, key gate and empty state now show the real app icon (the
  install sheet already did). Decorative snowflakes stay.
- --surface-strong is finally defined (it was referenced but never
  declared, so those inputs had no fill).
- Icon URLs carry ?v=2 so browser favicon caches refetch.

Updated the contrast audit (21 pairs) and the deck-chrome token
assertions to the new theme. Verified: contrast 21/21, deck chrome
15/15, key gate 49/49, install sheet 64/64, final ux 12/12, retry
14/14, zoom 6/6, security 8/8, pwa 16/16, deck fixes 15/15.
2026-10-08 06:15:51 +00:00
3aae6f3199 Desktop library pass: alignment, card weight, sidebar and theme
Layout (desktop / lg+ only):
- 40px top padding above the page title.
- Deck grid uses minmax(320px, 1fr), so two cards fill the content width
  and share a right edge with the header (same max-width container).
- Group headings only render once real folders exist; with none, a single
  quiet "All decks" label replaces "Unorganized".
- Import JSON becomes a plain text button (mobile keeps the pill).

Deck cards (desktop):
- The whole card is a link; Study is a compact secondary button instead
  of a full-width solid pill.
- The ... control is a 32px ghost button with a real ellipsis icon,
  aligned to the title's centre.
- Meta line always shows the same fields (activities · runs / Not played
  yet); the score is a separate quiet "Best 53%" stat.
- 12px rhythm between title/meta/tags/summary, 16px before the actions.
- Hover: lighter surface, stronger border, soft shadow and a 2px lift in
  light mode (pointer devices only, so touch has no sticky hover).

Sidebar (desktop rail):
- All decks + Unorganized are views above the FOLDERS label; only
  user-created folders sit under it.
- Count only on All decks. The mobile drawer is unchanged.

Theme:
- Dark: sidebar is a step darker (#181818 to #141414 vs #212121 page).
- Light: cards get a slightly stronger border (#e2e2e2) and a soft
  shadow; the sidebar divider and edge shadow are stronger too.
- Medium difficulty darkened to #7a5300 (6.2:1 on the light chip).

Mobile is untouched: layout changes are behind lg: (or the desktop prop);
theme tweaks are shared tokens.

Verified: key-gate 49/49, install sheet 64/64, retry 14/14, zoom 6/6,
deck chrome 15/15, deck fixes 15/15, final ux 12/12, contrast 20/20,
security 8/8, pwa 16/16.
2026-10-07 22:37:29 +00:00
7ab04e9f62 Screens that already fit no longer scroll or rubber-band
- API-key gate is now a fixed, out-of-flow overlay (inset-0, overflow
  hidden, overscroll-none) with its own internal scroller, so the document
  has no scrollable height on phones. It can't be panned or bounced.
- Home/create mains are flex columns with min-h-dvh and a flex-1 content
  area, and the empty state's mobile padding was trimmed, so a short/empty
  library is exactly viewport height instead of overflowing by ~76px.
  Long pages (the create form, decks) still scroll normally.
- html/body get overscroll-behavior: none to stop pull-to-refresh and
  rubber-banding on fitting screens.

Verified with a scroll probe (gate and empty library: scrollHeight ==
viewport; create still scrolls) plus zoom 6/6, install sheet 64/64,
pwa 16/16, final-ux 12/12, retry 14/14, security 8/8, contrast 20/20.
2026-10-07 12:37:46 +00:00
080306f111 Stop phones zooming when a field is focused
- Viewport: maximum-scale=1 and user-scalable=no (app-like, no pinch or
  double-tap zoom on phones).
- Mobile-only rule keeps every input/textarea/select at 16px so iOS
  Safari's focus auto-zoom never triggers (it ignores user-scalable=no for
  that). Deliberately unlayered so it beats the per-component Tailwind
  sizes; desktop keeps the designed 12-14px fields.

New zoom suite (6 checks): viewport meta, gate input, settings key input,
notes textarea and every study-feed field at >= 16px on a phone, desktop
sizes unchanged. Install sheet 64/64, pwa 16/16, final-ux 12/12,
security 8/8, contrast 20/20.
2026-10-07 12:14:10 +00:00
9e4ecd3ee0 Deck chrome: neutral surfaces, borders and text (no navy leftovers)
- The study feed's progress header and bottom pager no longer use the old
  navy glass: new .study-chrome class uses the neutral surface token
  (same gray as the cards, #2f2f2f in dark), the neutral border
  (8% white in dark, #e5e5e5 in light) and a neutral shadow.
- Close tile, streak pill and XP pill now use --surface-sunken /
  --text-muted, matching the app's other buttons and chips; the close
  glyph is the shared XIcon.
- Bottom pager: counter in --text, arrows in --text-muted.
- .glass (mobile header) no longer has the hardcoded #101720 navy; it is
  now color-mix of the surface token. Removed the unused --color-night-*
  tokens and the last night- usage (create screen sticky bar).
- Hidden the 'Swipe up' hint on desktop, where it collided with the
  bottom pager.

New deck-chrome suite: 15/15 (computed surfaces/borders/text in both
themes, no navy); pwa 16/16, final-ux 12/12, security 8/8, contrast 20/20.
2026-10-07 09:06:18 +00:00
f98795436d Fix the iOS install sheet: exact Safari flow, chips, iOS blue, layout
Steps
- iOS now uses the exact flow: Safari users get 4 steps (⋯ → Share/More →
  Add to Home Screen → Add); non-Safari (CriOS/FxiOS/EdgiOS/in-app) get a
  first 'Open this page in Safari' step and 5 total. The vague 'in some
  browsers it's under…' wording is gone.

Chips and icons
- The ⋯ chip renders a single three-dot icon (icon-only pill) instead of
  the icon plus an '⋯' character (which showed two sets of dots).
- The More chip uses a chevron-down icon instead of the rounded-square.
- The accent pill uses real iOS blue: #0A84FF dark / #007AFF light, still
  the only coloured element.
- Steps and chips share one 16px type scale; chip labels are medium weight.

Typography and layout
- Numbers are small muted circles in a fixed 24px column, aligned with the
  first text line via matching line-height.
- Removed trailing periods after chips; steps tightened.
- Bottom padding is calc(24px + env(safe-area-inset-bottom)) and a
  secondary 'Maybe later' button sits below the steps.
- max-height 90dvh with inner scrolling; 36×4px low-contrast handle with
  real swipe-down-to-close (momentum off, spring back).
- App card shows the hostname only (no port), truncated.

Themes
- Dark (default): layered surfaces #1c1c1e sheet / #2c2c2e card / #3a3a3c
  chips with 8% white borders; subtle logo tile instead of solid white;
  backdrop is 50% black with 4px blur.
- Light only when the app theme was explicitly set to light, ignoring the
  system preference; borders #e0e0e0, chip fills #f2f2f2.

Also fixes a crash the test pressure exposed: a 429 (rate-limited)
/api/health response crashed Settings at health.vision.available.
fetchHealth now rejects non-OK/malformed responses so the dialog shows its
error banner instead of the app error screen.

Install-sheet suite now 64 checks; pwa/final-ux/security/contrast green.
2026-10-07 08:49:32 +00:00
969f7a82a0 Gate polish round two: glows, disabled state, help link, modal
- Disabled Continue is now the primary style at 45% opacity with
  cursor: not-allowed; full opacity while validating and once text is
  entered.
- Move "How do I get a key?" below the Continue button (small ? icon,
  muted, underline on hover) and tighten input+button spacing.
- Shorten the subtitle to "One step to freeze your first deck." and
  balance it.
- Replace the faint gradient with two real blurred blobs (top-right and
  bottom-left, 120px blur, pointer-events: none, behind the card).
- Dark: page #0f0f0f, card #1a1a1a, 8% white border.
- Phone: 24px heading, 15px subtitle, wider card padding, 40px corner
  buttons with safe-area-aware offsets, robust 100dvh centering.
- Modal: 480px wide with text-pretty steps, stronger tinted free-tier
  callout, handle removed from the phone sheet.
- Tests: gate suite now 49 checks incl. disabled opacity, glow pseudo,
  card separation and help-link placement; new state screenshots for
  invalid/offline/validating in both themes.
2026-10-07 06:09:13 +00:00
fee59b9577 Polish the API-key gate
- Add a theme toggle to the gate (sun/moon ghost button next to the help
  button); it switches instantly and persists to localStorage.
- Strengthen the corner glows so the stage reads atmospheric in both themes
  (bigger, slightly stronger radial gradients behind the card).
- Center the logo, heading, subtitle and footnote inside the card.
- Dark mode: replace the solid white logo tile with a subtle raised tile
  (thin border, light snowflake); make the card only slightly lighter than
  the near-black page with a low-opacity border and soft shadow.
- Disabled Continue keeps the primary fill at ~45% opacity with a crisp
  label instead of a faded muddy one; fully solid once text is entered.
- Bigger 36px corner buttons and a visible "How do I get a key?" text link
  under the input that opens the same modal.
- Shorten and balance the reassurance line.
- Help modal: step circles aligned to the first text line, emphasised
  free-tier callout with a check icon, underlined links with an
  external-link icon, muted medium-weight UI labels, 36px close button
  aligned to the title.
- Gate test extended to 44 checks (theme toggle, text link, modal polish).
2026-10-07 05:34:32 +00:00
dfa7b7502a Add full-screen API-key gate
In user-key mode a browser with no stored Ollama key now gets a polished
gate instead of the app shell: one masked input with a show/hide toggle,
a single Continue button, inline validation errors, and a "?" help sheet
with the steps to a free key.

- Validates the draft key through /api/health (real 1-token probe) before
  saving; invalid-key and network failures read differently.
- Key still lives only in this browser's localStorage; nothing server-side.
- Gate wraps /, /create and deck study; server-key and local-daemon modes
  skip it; removing the key in Settings brings it back.
- Removes the now-redundant dashboard/create API-key banner.
- Monochrome corner glows (dark near-black / light near-white), 24px card,
  48px field and button, gentle fade/slide-in, reduced-motion aware.
- Help modal: centred dialog on desktop, bottom sheet on mobile, closes via
  X, Escape or backdrop.
- README and browser suites updated (34 gate checks, 15 key-flow checks,
  12 shipped-default checks; ui/folders/docs/pwa/security/contrast green).
2026-10-06 21:20:22 +00:00
3ea2cea100 Freezmo: self-hosted notes-to-activities study app
Next.js 15 app that turns photos, PDFs, DOCX/PPTX and text into a swipeable
feed of AI-generated activities (flashcards, multiple choice, true/false,
fill-in-the-blank, matching, ordering, short answer) using the user's own
Ollama models.

Highlights:
- All AI calls run in server route handlers; the browser never talks to Ollama.
- Bring-your-own-key mode (default): the key lives only in the browser and is
  sent per-request; server, local-daemon and shared-key modes also supported.
- Client-side document extraction (pdf.js, mammoth, jszip) with scanned-PDF
  page rendering for the vision model; notes stay in their original language.
- ChatGPT-style shell: collapsible sidebar / mobile drawer, neutral token-based
  light + dark themes, folder organisation, PWA (manifest, SW, offline page).
- Strict JSON validation with zod + auto-repair, rate limiting, and request
  guards (cross-origin, body-size, API-key sanitising).
- setup.sh bootstraps deps, .env.local and a production build.

Gitignore excludes .env*, the generated pdf.js worker and local scratch files.
2026-10-06 20:56:55 +00:00
3baa0fa8ce Initial commit from Create Next App 2026-10-05 07:05:33 +00:00