"use client"; import { useState } from "react"; import { ChevronRightIcon } from "lucide-react"; import { MessageResponse } from "@/components/ai-elements/message"; import type { ArtifactPartProps } from "@/src/core"; import { cn } from "@/lib/utils"; /** * The practice transcript cards (practice sessions, owner design * 2026-10-04): three quiet records in the stream — * * - ProblemCard — a posed problem: the statement, compact-expandable * (like every card here; the dock is the working surface, this is the * record). The commitment NEVER renders — only the call's statement. * - ConcludeCard — the conclusion: outcome badge + verdict + the reveal * (the committed solution, shown when the problem ended). A dismissal * synthesis (outcome:"dismissed") renders as one muted line. * - ThinkingCard — the learner's own submission, user-side: the register * is the two buttons' (Nudge = working trace, Commit = answer). The * UNIFIED ThinkingBlock (owner round 2026-10-05): the quiz quote-block * language in a faint user-side shell, never collapsible. */ import { ThinkingBlock } from "@/components/thinking-block"; /** The practice-thinking marker's details (native-messages.ts). */ export interface PracticeThinkingData { problemCallId: string; commit: boolean; text: string; } export function ProblemCard({ part }: ArtifactPartProps) { const [open, setOpen] = useState(false); const input = (part.input ?? {}) as { statement?: string }; const statement = typeof input.statement === "string" ? input.statement : ""; return (
{open && (
{statement}
)}
); } const OUTCOME_LABEL: Record = { solved: "Solved", solvedWithHints: "Solved with hints", movedOn: "Moved on", surrendered: "Surrendered", dismissed: "Dismissed", }; export function ConcludeCard({ part }: ArtifactPartProps) { const [open, setOpen] = useState(false); const input = (part.input ?? {}) as { outcome?: string; verdict?: string; solutionReveal?: string; }; const outcome = typeof input.outcome === "string" ? input.outcome : ""; const verdict = typeof input.verdict === "string" ? input.verdict : ""; const reveal = typeof input.solutionReveal === "string" ? input.solutionReveal : ""; // The dismissal synthesis: no verdict, no reveal — one muted line (the // learner clicked; the record needs only the fact). if (outcome === "dismissed") { return (
Problem dismissed.
); } return (
{open && (
{verdict.length > 0 && (
{verdict}
)} {reveal.length > 0 && (
The solution
{reveal}
)}
)}
); } export function ThinkingCard({ data }: { data: PracticeThinkingData }) { // One component, two registers (owner ruling): the committed register // carries the primary rail (the quiz answer thinking's paint), the // working register the muted one. return ( ); }