Adopt the portal's design language so the app embeds seamlessly into its header/sidebar chrome later: - Token layer rewritten as raw OKLCH triplets (the previous oklch()-wrapped vars produced invalid colors; the UI was effectively unstyled). Values mirror GSD: Manrope + Fraunces fonts, #cb333b red brand, warm off-white surfaces, pill buttons, brand focus rings. Dark tokens follow .dark or the portal's [data-bs-theme=dark]. - Layout: no left sidebar, no header (the portal supplies them). Conversation column flush-left with a slim F1-F8 phase strip; session rail moved to the RIGHT, mirroring the portal's left sidebar (red section label, red-tint active item). Editorial empty state. - Transcript component now actually renders the streamed assistant turns (the store accumulated them but nothing displayed them). Toaster mounted for errors (matchMedia polyfill added to the jsdom test setup). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
29 lines
951 B
TypeScript
29 lines
951 B
TypeScript
// frontend/src/shell/Transcript.tsx
|
|
import ReactMarkdown from "react-markdown";
|
|
import remarkGfm from "remark-gfm";
|
|
import { useSessionStore } from "../store/sessionStore";
|
|
|
|
/**
|
|
* Renders the streamed assistant conversation. The store accumulates assistant
|
|
* text deltas in `transcript`; this is the surface that shows them.
|
|
*/
|
|
export function Transcript() {
|
|
const transcript = useSessionStore((s) => s.transcript);
|
|
if (transcript.length === 0) return null;
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
{transcript.map((entry, i) => (
|
|
<article key={i} className="flex flex-col gap-1.5">
|
|
<span className="font-heading text-xs font-semibold uppercase tracking-[0.12em] text-primary/80">
|
|
ThothII
|
|
</span>
|
|
<div className="thot-prose">
|
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>{entry.text}</ReactMarkdown>
|
|
</div>
|
|
</article>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|