gaaaaaaaaaaa's picture
Publish trajectory_rag
a414867 verified
Raw History Blame Contribute Delete
3.29 kB
"use client";
import { useState } from "react";
import { NotationHoverCard } from "@/components/tree-visualizer/NotationHoverCard";
import { MathOnlyRenderer } from "@/components/tree-visualizer/RichMath";
import { cn } from "@/lib/utils";
import { splitTokenBlocks } from "@/lib/tree/content";
interface MathRendererProps {
content: string;
className?: string;
compact?: boolean;
forceTokenOpen?: boolean;
allowTokenToggle?: boolean;
}
function stopNodeInteraction(event: React.SyntheticEvent) {
event.stopPropagation();
}
function tokenLabel(name: string) {
return name.replace(/[^a-zA-Z0-9_]+/g, "_");
}
function TokenBlob({
name,
value,
raw,
forceOpen,
allowToggle = true
}: {
name: string;
value: string;
raw: string;
forceOpen?: boolean;
allowToggle?: boolean;
}) {
const [open, setOpen] = useState(false);
const isFinalAnswer = name === "final_answer";
const isOpen = Boolean(forceOpen || open);
return (
<div className="my-2 block min-w-0">
<NotationHoverCard label={tokenLabel(name)} rendered={value} notation={raw}>
<button
type="button"
onPointerDown={stopNodeInteraction}
onMouseDown={stopNodeInteraction}
onClick={(event) => {
stopNodeInteraction(event);
if (!forceOpen && allowToggle) setOpen((current) => !current);
}}
className={cn(
"token-blob inline-flex items-center gap-2 rounded-full border px-3 py-1 text-[11px] font-semibold shadow-sm transition",
isFinalAnswer
? "border-claude-300 bg-claude-50 text-claude-700 hover:bg-claude-100"
: "border-paper-300 bg-white text-ink-600 hover:bg-paper-50"
)}
title={allowToggle || forceOpen ? (isOpen ? `Hide ${tokenLabel(name)}` : `Show ${tokenLabel(name)}`) : tokenLabel(name)}
>
<span className="h-1.5 w-1.5 rounded-full bg-current opacity-70" />
{tokenLabel(name)}
</button>
</NotationHoverCard>
{isOpen && (
<div className="token-content mt-2 block min-w-0 rounded-lg border border-paper-200 bg-white/86 px-3 py-2 text-sm leading-6 text-ink-700 shadow-sm">
<MathOnlyRenderer content={value} />
</div>
)}
</div>
);
}
export function MathRenderer({
content,
className,
compact = false,
forceTokenOpen = false,
allowTokenToggle = true
}: MathRendererProps) {
const segments = splitTokenBlocks(content || "");
if (!content.trim()) {
return <p className={cn("text-sm text-ink-300", className)}>No math content.</p>;
}
return (
<div
className={cn(
"whitespace-pre-wrap text-sm leading-6 text-ink-700",
compact && "line-clamp-4",
className
)}
>
{segments.map((segment, index) => {
if (segment.type === "token") {
return (
<TokenBlob
key={`${segment.name}-${index}`}
name={segment.name}
value={segment.value}
raw={segment.raw}
forceOpen={forceTokenOpen}
allowToggle={allowTokenToggle}
/>
);
}
return <MathOnlyRenderer key={`text-${index}`} content={segment.value} />;
})}
</div>
);
}