diff --git a/app/create/page.tsx b/app/create/page.tsx index ffece12..15cc8f9 100644 --- a/app/create/page.tsx +++ b/app/create/page.tsx @@ -1,3 +1,5 @@ +import { redirect } from "next/navigation"; + import { ApiKeyGate } from "@/components/ApiKeyGate"; import { AppHeader } from "@/components/AppHeader"; import { MobileSidebar, Sidebar, SidebarOffset } from "@/components/Sidebar"; @@ -8,7 +10,16 @@ export const metadata = { title: "New deck", }; -export default function CreatePage() { +export default async function CreatePage({ + searchParams, +}: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}) { + const { mode } = await searchParams; + // The library's quick actions (and the mode switch) deep-link into a mode; + // a link that points at the other side is handed to the page that owns it. + if (mode === "homework") redirect("/homework?mode=homework"); + return (
diff --git a/app/homework/page.tsx b/app/homework/page.tsx index 24be85e..619a213 100644 --- a/app/homework/page.tsx +++ b/app/homework/page.tsx @@ -1,3 +1,5 @@ +import { redirect } from "next/navigation"; + import { ApiKeyGate } from "@/components/ApiKeyGate"; import { AppHeader } from "@/components/AppHeader"; import { HomeworkScreen } from "@/components/homework/HomeworkScreen"; @@ -8,7 +10,16 @@ export const metadata = { title: "Make my homework", }; -export default function HomeworkPage() { +export default async function HomeworkPage({ + searchParams, +}: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}) { + const { mode } = await searchParams; + // The mode switch and the library quick actions deep-link into a mode; a + // link that points at the Study Deck side is handed back to /create. + if (mode === "deck") redirect("/create?mode=deck"); + return (
diff --git a/components/LibraryScreen.tsx b/components/LibraryScreen.tsx index 2beeea5..137033a 100644 --- a/components/LibraryScreen.tsx +++ b/components/LibraryScreen.tsx @@ -228,7 +228,7 @@ export function LibraryScreen() { assignment. Stacked on phones, side by side from 640px up. */
= { + deck: "/create?mode=deck", + homework: "/homework?mode=homework", +}; + +/** How long the ice pill takes to slide before the route follows. */ +const SLIDE_MS = 180; + +/** + * Compact two-segment mode switch (Study Deck / Homework Solver). The ice pill + * slides to the chosen segment and the router follows a moment later, so the + * toggle reads as one smooth transition instead of a hard jump. The current + * mode stays pressed; the other segment navigates. + */ +export function ModeSwitch({ current }: { current: StudyMode }) { + const { t } = useI18n(); + const router = useRouter(); + const [pending, setPending] = useState(null); + const timer = useRef(null); + + useEffect( + () => () => { + if (timer.current !== null) window.clearTimeout(timer.current); + }, + [], + ); + + const choose = (mode: StudyMode) => { + if (mode === current || pending !== null) return; + setPending(mode); + const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + timer.current = window.setTimeout(() => router.push(MODE_HREF[mode]), reduce ? 0 : SLIDE_MS + 10); + }; + + const shown = pending ?? current; + + const segment = (mode: StudyMode, icon: React.ReactNode, label: string) => ( + + ); + + return ( +
+ + {segment("deck", , t("actionStudyDeck"))} + {segment("homework", , t("actionHomework"))} +
+ ); +} diff --git a/components/UploadScreen.tsx b/components/UploadScreen.tsx index ce488f2..df8f927 100644 --- a/components/UploadScreen.tsx +++ b/components/UploadScreen.tsx @@ -1,7 +1,6 @@ "use client"; import { motion } from "framer-motion"; -import Link from "next/link"; import { useRouter } from "next/navigation"; import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; @@ -33,7 +32,8 @@ import { UNORGANIZED_ID, type Difficulty } from "@/lib/schemas"; import { upsertDeck } from "@/lib/storage"; import { openSettings } from "@/lib/ui-events"; import { cn, uid } from "@/lib/utils"; -import { CameraIcon, FolderIcon, IceCubeIcon, PlusIcon, SnowflakeIcon, SparklesIcon, SunIcon } from "@/components/icons"; +import { CameraIcon, FolderIcon, IceCubeIcon, PlusIcon, SnowflakeIcon, SunIcon } from "@/components/icons"; +import { ModeSwitch } from "@/components/ModeSwitch"; import { Button, ProgressBar } from "@/components/ui"; type Phase = "idle" | "preparing" | "loading" | "error"; @@ -321,39 +321,9 @@ export function UploadScreen() {

- {/* Mode switcher: this page builds a study deck from notes; the other - card hands the same intake over to the homework flow. */} -
- - - - - - {t("actionStudyDeck")} - - - - - - - - {t("actionHomework")} - - -
+ {/* Mode switcher: a compact segmented control. This page is the Study + Deck side; the other segment opens the homework flow. */} + {error ? ( diff --git a/lib/i18n-dict.ts b/lib/i18n-dict.ts index 75d59b4..a996d65 100644 --- a/lib/i18n-dict.ts +++ b/lib/i18n-dict.ts @@ -224,9 +224,9 @@ const en: Record = { "homework.history.deleteConfirm": "Delete this result?", /* quick actions (library empty state + /create switcher) ----------- */ - "emptyStateCreateDeck": "Create interactive test", - "emptyStateHomework": "Help me with my homework", - "actionStudyDeck": "Study Deck & Quizzes", + "emptyStateCreateDeck": "Study Deck", + "emptyStateHomework": "Homework Solver", + "actionStudyDeck": "Study Deck", "actionHomework": "Homework Solver", /* library --------------------------------------------------------- */ @@ -808,9 +808,9 @@ const cs: Record = { "homework.history.deleteConfirm": "Smazat tento výsledek?", /* quick actions (library empty state + /create switcher) ----------- */ - "emptyStateCreateDeck": "Vytvořit interaktivní test", - "emptyStateHomework": "Pomoct s úkolem", - "actionStudyDeck": "Studijní balíček a testy", + "emptyStateCreateDeck": "Studijní balíček", + "emptyStateHomework": "Řešitel úkolů", + "actionStudyDeck": "Studijní balíček", "actionHomework": "Řešitel úkolů", /* library --------------------------------------------------------- */ diff --git a/public/sw.js b/public/sw.js index 717801b..c165750 100644 --- a/public/sw.js +++ b/public/sw.js @@ -9,7 +9,7 @@ * * Bumping CACHE_VERSION is how a new deploy invalidates old assets. */ -const CACHE_VERSION = "freezmo-v8"; +const CACHE_VERSION = "freezmo-v9"; const SHELL_CACHE = `${CACHE_VERSION}-shell`; const ASSET_CACHE = `${CACHE_VERSION}-assets`;