"use client"; import { MessageResponse } from "@/components/ai-elements/message"; import type { ArtifactPartProps } from "@/src/core"; /** * 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) { // Always open, no chevron (owner round, 2026-10-05): the record IS the // statement — same container + eyebrow discipline as the answered card // and the dock (which mirrors this in Slice D). The commitment never // renders — only the call's statement. const input = (part.input ?? {}) as { statement?: string }; const statement = typeof input.statement === "string" ? input.statement : ""; return (
Problem
{statement}
); } const OUTCOME_LABEL: Record = { solved: "Solved", solvedWithHints: "Solved with hints", movedOn: "Moved on", surrendered: "Surrendered", dismissed: "Dismissed", }; export function ConcludeCard({ part }: ArtifactPartProps) { // The conclusion redesigned (owner round, 2026-10-05): the SOLUTION is // the hero — the modelSolution committed at pose time (part.reveal, the // view-seam join), shown verbatim, never re-authored. The verdict sits // below in the quiz-explanation register (small, muted, low-key). // Nothing collapsible. Legacy inputs (pre-slim records) that carry // their own solutionReveal fall back to it. 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 part.reveal === "string" ? part.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 (
{OUTCOME_LABEL[outcome] ?? "Concluded"}
{reveal.length > 0 && (
{reveal}
)} {verdict.length > 0 && (
Verdict: {verdict}
)}
); } export function ThinkingCard({ data }: { data: PracticeThinkingData }) { // The unified row (owner round, 2026-10-05): one style — collapsed // arrival, 50%-italic quote block on expand. The register survives only // in the label ("Your solution" when committed) and the data-commit // flag for the tests. return ( ); }