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.
This commit is contained in:
parent
9fb728d735
commit
ef2a6a3589
35 changed files with 2334 additions and 100 deletions
118
lib/i18n-dict.ts
118
lib/i18n-dict.ts
|
|
@ -167,6 +167,63 @@ const en: Record<string, string> = {
|
|||
"health.createKey.after": "and paste it above. Tip: with the local daemon you can run",
|
||||
"health.createKey.end": "instead and set",
|
||||
|
||||
/* make my homework ------------------------------------------------- */
|
||||
"homework.nav": "Make my homework",
|
||||
"homework.title": "Make my homework",
|
||||
"homework.subtitle":
|
||||
"Paste the assignment, snap a photo or add a document — then choose whether I should just do it, or guide you through it.",
|
||||
"homework.entry": "Got homework instead of notes?",
|
||||
"homework.source.title": "1. The assignment",
|
||||
"homework.source.help": "Paste its text, drop photos, or add a document.",
|
||||
"homework.source.textLabel": "Assignment text",
|
||||
"homework.source.placeholder": "Paste the assignment here…",
|
||||
"homework.source.add": "Add photos or files",
|
||||
"homework.source.drop": "Drop the assignment here",
|
||||
"homework.source.formats": "Text, photos, PDF, DOCX, PPTX, ODT/ODP, TXT, MD",
|
||||
"homework.source.ready": "Ready when you are.",
|
||||
"homework.source.need": "Add the assignment first.",
|
||||
"homework.source.tooManyPhotos": "Up to {n} photos per assignment.",
|
||||
"homework.source.tooManyDocs": "Up to {n} documents per assignment.",
|
||||
"homework.mode.title": "2. What should I do?",
|
||||
"homework.mode.make.title": "Just make it",
|
||||
"homework.mode.make.subtitle": "The finished homework, ready to copy.",
|
||||
"homework.mode.guide.title": "Explain how to make this homework",
|
||||
"homework.mode.guide.subtitle": "A step-by-step guide with hints and quick checks.",
|
||||
"homework.start.make": "Make my homework",
|
||||
"homework.start.guide": "Guide me through it",
|
||||
"homework.loading.title": "Working on the assignment…",
|
||||
"homework.loading.reading": "Reading the assignment…",
|
||||
"homework.loading.vision2": "Transcribing the questions…",
|
||||
"homework.loading.writing": "Writing your homework…",
|
||||
"homework.loading.planning": "Planning the steps…",
|
||||
"homework.loading.gen2": "Checking every part…",
|
||||
"homework.loading.gen3": "Almost done…",
|
||||
"homework.error.title": "That didn't work",
|
||||
"homework.error.body": "Something went wrong while working on the assignment.",
|
||||
"homework.result.title": "Your homework",
|
||||
"homework.result.copy": "Copy",
|
||||
"homework.result.copied": "Copied!",
|
||||
"homework.result.copyFailed": "Copy didn't work — select the text and copy it manually.",
|
||||
"homework.result.again": "Make it again",
|
||||
"homework.result.new": "New assignment",
|
||||
"homework.guide.stepOf": "Step {n} of {total}",
|
||||
"homework.guide.close": "Close the guide",
|
||||
"homework.guide.tryIt": "Try it yourself",
|
||||
"homework.guide.hint": "Show a hint",
|
||||
"homework.guide.hintMore": "Another hint",
|
||||
"homework.guide.hintAnswer": "Show the answer",
|
||||
"homework.guide.hintN": "Hint {n}",
|
||||
"homework.guide.next": "Next step",
|
||||
"homework.guide.showSolution": "Show the solution",
|
||||
"homework.guide.solution": "The full solution",
|
||||
"homework.guide.solutionNote":
|
||||
"Assembled from the steps above — copy it and hand it in.",
|
||||
"homework.history.title": "Recent homework",
|
||||
"homework.history.made": "Made",
|
||||
"homework.history.guide": "Guide",
|
||||
"homework.history.openTitle": "Open “{title}”",
|
||||
"homework.history.deleteConfirm": "Delete this result?",
|
||||
|
||||
/* library --------------------------------------------------------- */
|
||||
"library.title": "Your icebox",
|
||||
"library.subtitle": "Decks frozen from your notes, saved in this browser.",
|
||||
|
|
@ -534,6 +591,8 @@ const en: Record<string, string> = {
|
|||
"server.bodyNotJson": "Request body was not valid JSON.",
|
||||
"server.invalidRequest": "Invalid request: {detail}",
|
||||
"server.needContent": "Add at least one photo of your notes or paste some text to build a deck.",
|
||||
"server.needAssignment":
|
||||
"Add the assignment first — paste its text, photograph it, or add a document.",
|
||||
"server.invalidGrade": "Invalid grading request.",
|
||||
"server.bodyTooLarge": "Request body is too large ({size}). Limit is {limit}.",
|
||||
"server.invalidOrigin": "Invalid Origin header.",
|
||||
|
|
@ -688,6 +747,63 @@ const cs: Record<string, string> = {
|
|||
"health.createKey.after": "a vlož ho výše. Tip: s místním daemonem můžeš spustit",
|
||||
"health.createKey.end": "a nastavit",
|
||||
|
||||
/* make my homework ------------------------------------------------- */
|
||||
"homework.nav": "Udělej mi úkol",
|
||||
"homework.title": "Udělej mi úkol",
|
||||
"homework.subtitle":
|
||||
"Vlož zadání, vyfoť ho nebo přidej dokument — a pak si vyber, jestli to mám rovnou udělat, nebo tě tím provést.",
|
||||
"homework.entry": "Máš místo poznámek úkol?",
|
||||
"homework.source.title": "1. Zadání",
|
||||
"homework.source.help": "Vlož jeho text, přetáhni fotky nebo přidej dokument.",
|
||||
"homework.source.textLabel": "Text zadání",
|
||||
"homework.source.placeholder": "Sem vlož zadání…",
|
||||
"homework.source.add": "Přidat fotky nebo soubory",
|
||||
"homework.source.drop": "Přetáhni zadání sem",
|
||||
"homework.source.formats": "Text, fotky, PDF, DOCX, PPTX, ODT/ODP, TXT, MD",
|
||||
"homework.source.ready": "Můžeme začít.",
|
||||
"homework.source.need": "Nejdřív přidej zadání.",
|
||||
"homework.source.tooManyPhotos": "Až {n} fotek na zadání.",
|
||||
"homework.source.tooManyDocs": "Až {n} dokumentů na zadání.",
|
||||
"homework.mode.title": "2. Co mám udělat?",
|
||||
"homework.mode.make.title": "Jen to udělej",
|
||||
"homework.mode.make.subtitle": "Hotový úkol, připravený ke zkopírování.",
|
||||
"homework.mode.guide.title": "Vysvětli, jak to udělat",
|
||||
"homework.mode.guide.subtitle": "Návod krok za krokem s nápovědami a rychlými kontrolami.",
|
||||
"homework.start.make": "Udělej mi úkol",
|
||||
"homework.start.guide": "Proveď mě tím",
|
||||
"homework.loading.title": "Pracuji na zadání…",
|
||||
"homework.loading.reading": "Čtu zadání…",
|
||||
"homework.loading.vision2": "Přepisuji otázky…",
|
||||
"homework.loading.writing": "Píšu tvůj úkol…",
|
||||
"homework.loading.planning": "Plánuji postup…",
|
||||
"homework.loading.gen2": "Kontroluji každou část…",
|
||||
"homework.loading.gen3": "Už to bude…",
|
||||
"homework.error.title": "To se nepovedlo",
|
||||
"homework.error.body": "Při zpracování zadání se něco pokazilo.",
|
||||
"homework.result.title": "Tvůj úkol",
|
||||
"homework.result.copy": "Kopírovat",
|
||||
"homework.result.copied": "Zkopírováno!",
|
||||
"homework.result.copyFailed": "Kopírování se nepovedlo — vyber text a zkopíruj ho ručně.",
|
||||
"homework.result.again": "Udělej to znovu",
|
||||
"homework.result.new": "Nové zadání",
|
||||
"homework.guide.stepOf": "Krok {n}/{total}",
|
||||
"homework.guide.close": "Zavřít návod",
|
||||
"homework.guide.tryIt": "Zkus si to",
|
||||
"homework.guide.hint": "Ukaž nápovědu",
|
||||
"homework.guide.hintMore": "Další nápověda",
|
||||
"homework.guide.hintAnswer": "Ukaž odpověď",
|
||||
"homework.guide.hintN": "Nápověda {n}",
|
||||
"homework.guide.next": "Další krok",
|
||||
"homework.guide.showSolution": "Ukaž řešení",
|
||||
"homework.guide.solution": "Celé řešení",
|
||||
"homework.guide.solutionNote":
|
||||
"Složeno ze všech kroků — zkopíruj a odevzdej.",
|
||||
"homework.history.title": "Nedávné úkoly",
|
||||
"homework.history.made": "Hotové",
|
||||
"homework.history.guide": "Návod",
|
||||
"homework.history.openTitle": "Otevřít „{title}“",
|
||||
"homework.history.deleteConfirm": "Smazat tento výsledek?",
|
||||
|
||||
/* library --------------------------------------------------------- */
|
||||
"library.title": "Tvoje mraznička",
|
||||
"library.subtitle": "Sady zmrazené z tvých poznámek, uložené v tomto prohlížeči.",
|
||||
|
|
@ -1055,6 +1171,8 @@ const cs: Record<string, string> = {
|
|||
"server.bodyNotJson": "Tělo požadavku nebylo platné JSON.",
|
||||
"server.invalidRequest": "Neplatný požadavek: {detail}",
|
||||
"server.needContent": "Přidej aspoň jednu fotku poznámek nebo vlož text, ze kterého sada vznikne.",
|
||||
"server.needAssignment":
|
||||
"Nejdřív přidej zadání — vlož jeho text, vyfoť ho nebo přidej dokument.",
|
||||
"server.invalidGrade": "Neplatný požadavek na známkování.",
|
||||
"server.bodyTooLarge": "Tělo požadavku je příliš velké ({size}). Limit je {limit}.",
|
||||
"server.invalidOrigin": "Neplatná hlavička Origin.",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue