fix: assistant text rendering, toolResult plumbing leak; add render smoke tests
- Markdown.tsx never passed text as children to ReactMarkdown (valid JSX, compiled clean, rendered nothing) — the 'thinking shows, text doesn't' bug - toolResult entries (quiz/ask dispatch instructions for the LLM) leaked into the transcript as visible text; now never rendered - render smoke tests (renderToStaticMarkup, no DOM): pin markdown content, KaTeX, button-based quiz options, ask answers not graded, toolResult and [Quiz answer] hiding — negative-tested against the actual bugs - React default import in all .tsx: tsx test runner uses classic JSX transform (ignores tsconfig jsx option), Vite uses automatic runtime; explicit import works under both - katex css moved to main.tsx (tests can't import css) - verified live in browser: all assistant messages render, ask card has no grading marks, pending quiz re-arms on resume
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@
|
||||
* resumes via POST before connecting.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
import type { NewSessionResponse, SessionSummary } from "../../src/shared/protocol.ts";
|
||||
import { Home } from "./views/Home.tsx";
|
||||
import { SessionView } from "./views/SessionView.tsx";
|
||||
|
||||
@@ -311,14 +311,6 @@ textarea:disabled {
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.tool-result {
|
||||
padding: 2px 24px;
|
||||
font-size: 12.5px;
|
||||
color: var(--text-muted);
|
||||
font-style: italic;
|
||||
max-width: 820px;
|
||||
}
|
||||
|
||||
.composer {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
import type { QuestionAnswerData } from "../../../src/shared/protocol.ts";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import React from "react";
|
||||
|
||||
/**
|
||||
* GraphPlaceholder — the home view's main panel. Deliberately isolated: the
|
||||
* real knowledge graph (territory map, domain clusters, frontier) is a
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { memo, useEffect, useRef, useState } from "react";
|
||||
import React, { memo, useEffect, useRef, useState } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkMath from "remark-math";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import "katex/dist/katex.min.css";
|
||||
|
||||
/**
|
||||
* Markdown — renders assistant prose with LaTeX (KaTeX) and mermaid diagrams
|
||||
@@ -73,7 +72,9 @@ export const Markdown = memo(function Markdown({ text }: { text: string }) {
|
||||
return <pre>{children}</pre>;
|
||||
},
|
||||
}}
|
||||
/>
|
||||
>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* survives reloads and resumes.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import type { ClientToolCall } from "../../../src/shared/protocol.ts";
|
||||
import { Markdown } from "./Markdown.tsx";
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* - text mode (ask): free-form answer
|
||||
*/
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import type { ClientQuestion } from "../../../src/shared/protocol.ts";
|
||||
|
||||
interface QuizCardProps {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* auto-scrolling pauses and a "jump to latest" pill appears.
|
||||
*/
|
||||
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import React, { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
|
||||
export function Transcript({ children, scrollToken }: { children: ReactNode; scrollToken: string | number }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
import { StrictMode } from "react";
|
||||
import React, { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./App.tsx";
|
||||
import "katex/dist/katex.min.css";
|
||||
import "./app.css";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import type { SessionSummary } from "../../../src/shared/protocol.ts";
|
||||
import { GraphPlaceholder } from "../components/GraphPlaceholder.tsx";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* and the composer.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import type { DisplayEntry, SessionState } from "../useSession.ts";
|
||||
import { useSession } from "../useSession.ts";
|
||||
import { Markdown } from "../components/Markdown.tsx";
|
||||
@@ -29,7 +29,7 @@ interface SessionViewProps {
|
||||
/** User messages already rendered as answer cards — don't duplicate them. */
|
||||
const HIDDEN_USER_PREFIXES = ["[Quiz answer]", "[Answer to your question]"];
|
||||
|
||||
function EntryBlock({ entry }: { entry: DisplayEntry }) {
|
||||
export function EntryBlock({ entry }: { entry: DisplayEntry }) {
|
||||
switch (entry.type) {
|
||||
case "user":
|
||||
if (HIDDEN_USER_PREFIXES.some((p) => entry.text.startsWith(p))) return null;
|
||||
@@ -37,7 +37,9 @@ function EntryBlock({ entry }: { entry: DisplayEntry }) {
|
||||
case "assistant":
|
||||
return <AssistantMessage text={entry.text} thinking={entry.thinking} toolCalls={entry.toolCalls} />;
|
||||
case "toolResult":
|
||||
return <div className="tool-result">{entry.text}</div>;
|
||||
// Internal plumbing for the LLM ("Quiz displayed to the learner,
|
||||
// end your turn…") — never learner-visible.
|
||||
return null;
|
||||
case "question":
|
||||
// Rendered by the pending-question flow; when answered, the
|
||||
// questionAnswered entry renders instead.
|
||||
|
||||
Reference in New Issue
Block a user