math: dollar delimiters end-to-end (exact answers, escaping examples, context rendering)
- switch the math authoring/rendering contract from \(...\)/\[...\] to the standard $...$ inline / $$...$$ display: prompts (20-question-tools, 40-formatting), singleDollarTextMath on the renderer, legacy reader kept, and escape-debris recovery extended to dollar spans (mirroring micromark delimiter-run rules) - exact answer membership now precedes debris cleanup in coerceCorrectAnswer (calculus incident: an exact sentence-final-period label was stripped as model debris, then rejected); option keys are passed at the boundary/grading seams, the dock no longer pre-coerces - a complete, phase-correct JSON-escaping example rides the probe/quiz tool descriptions (serialized from one canonical body; quiz leads with type+nodeId), plus the one-sentence JSON-escaping rule in 40-formatting - ask option descriptions render markdown/LaTeX in the pending dock; settled cards keep the details line and option descriptions retroactively, read from the persisted call arguments (chat passes part.input to AnsweredCard) 1566/1566 tests on both navigator configs; typecheck, lint, production build, standalone packaging, and Electron-runtime smoke all green.
This commit is contained in:
@@ -9,7 +9,6 @@ import { MessageResponse } from "@/components/ai-elements/message";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
buildAnswer,
|
||||
coerceCorrectAnswer,
|
||||
normalizeQuizType,
|
||||
CONFIDENCE_STOPS,
|
||||
DEFAULT_CONFIDENCE_INDEX,
|
||||
@@ -170,7 +169,7 @@ function teaserText(q: string): string {
|
||||
return q
|
||||
.replace(/\\\(([\s\S]*?)\\\)/g, "$1") // \(...\) inline math
|
||||
.replace(/\\\[([\s\S]*?)\\\]/g, "$1") // \[...\] display math
|
||||
.replace(/\$\$?([^$]*)\$\$?/g, "$1") // legacy $...$ (pre-switch sessions)
|
||||
.replace(/\$\$?([^$]*)\$\$?/g, "$1") // standard $...$/$$...$$ math
|
||||
.replace(/\\frac\{([^{}]*)\}\{([^{}]*)\}/g, "$1/$2")
|
||||
.replace(/\\([a-zA-Z]+)/g, "$1")
|
||||
.replace(/[{}]/g, "")
|
||||
@@ -293,7 +292,7 @@ function QuestionDockForm({
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
if (quizInput) {
|
||||
const resolved = resolveCorrect(coerceCorrectAnswer(quizInput.correctAnswer), ordered);
|
||||
const resolved = resolveCorrect(quizInput.correctAnswer, ordered);
|
||||
if (resolved.error) console.error("[quiz] correctAnswer did not resolve:", resolved.error);
|
||||
q.correctIndices = resolved.indices;
|
||||
q.explanation = quizInput.explanation;
|
||||
@@ -607,8 +606,12 @@ function QuestionDockForm({
|
||||
// the "I don't know" copy stays JetBrains Mono. QUIZ options render
|
||||
// NO description (owner ruling 2026-09-26): the model used them to
|
||||
// telegraph correctness — the schema dropped the field and old
|
||||
// records must not leak either.
|
||||
<span className="lh-prose block text-xs text-muted-foreground">{opt.description}</span>
|
||||
// records must not leak either. Same MessageResponse path as the
|
||||
// label (owner, 2026-10-12): descriptions are full markdown —
|
||||
// LaTeX included — not plain text.
|
||||
<MessageResponse className="lh-prose block text-xs text-muted-foreground">
|
||||
{opt.description}
|
||||
</MessageResponse>
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user