feat: enhance turn display with time and diagnostics styling

This commit is contained in:
2026-04-26 14:15:46 -04:00
parent fc10e46ccc
commit fca69d3cb5
2 changed files with 94 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
import { FormEvent, useEffect, useState } from "react";
import { FormEvent, useEffect, useMemo, useState } from "react";
type Entity = {
id: string;
@@ -28,7 +28,22 @@ type Turn = {
validation: ValidationResult[];
createdAt: number;
interpreter?: {
interpreterVersion: string;
resolutionSource: "deterministic" | "llm" | "hybrid";
minConfidence: number;
status: "resolved" | "needs_clarification" | "rejected";
selectedConfidence?: number;
diagnostics: string[];
clarification?: {
reasonCode: string;
question: string;
field?: string;
options?: Array<{
id: string;
label: string;
value: string;
}>;
};
};
};
@@ -51,12 +66,15 @@ type ProcessTurnResponse = {
worldState: WorldState;
interpreter: {
interpreterVersion: string;
resolutionSource: "deterministic" | "llm" | "hybrid";
minConfidence: number;
status: "resolved" | "needs_clarification" | "rejected";
selectedConfidence?: number;
diagnostics: string[];
clarification?: {
reasonCode: string;
question: string;
field?: string;
options?: Array<{
id: string;
label: string;
@@ -94,11 +112,27 @@ type SceneRulebook = {
const starterPrompts = [
"look around",
"take key",
"take lantern",
"give key to groundskeeper",
"introduce jeff",
"describe groundskeeper as patient",
"open door",
"move to exit",
];
function formatConfidence(value: number | undefined): string {
if (typeof value !== "number") return "n/a";
return `${Math.round(value * 100)}%`;
}
function formatTurnTime(epochMs: number): string {
try {
return new Date(epochMs).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
} catch {
return String(epochMs);
}
}
async function fetchJson<T>(input: RequestInfo, init?: RequestInit): Promise<T> {
const response = await fetch(input, init);
if (!response.ok) {
@@ -198,6 +232,9 @@ function RulebookEditor() {
<div className="rulebook-header">
<div>
<p className="rulebook-name">{rulebook.name}</p>
<p className="rulebook-desc">
Version {rulebook.version} | Updated {formatTurnTime(rulebook.updatedAt)}
</p>
{rulebook.description ? (
<p className="rulebook-desc">{rulebook.description}</p>
) : null}
@@ -340,6 +377,15 @@ export default function App() {
}
const entities = snapshot ? Object.values(snapshot.worldState.entities) : [];
const turns = useMemo(() => snapshot?.turns.slice().reverse() ?? [], [snapshot]);
function applyClarificationOption(optionValue: string) {
setInput((prev) => {
const trimmed = prev.trim();
if (!trimmed) return optionValue;
return `${trimmed} ${optionValue}`;
});
}
return (
<main className="page-shell">
@@ -382,20 +428,32 @@ export default function App() {
<p><strong>Input:</strong> {latest.rawText}</p>
<p>
<strong>Interpreter:</strong> {latest.interpreter.status}
{typeof latest.interpreter.selectedConfidence === "number"
? ` (${Math.round(latest.interpreter.selectedConfidence * 100)}% confidence)`
: ""}
{` via ${latest.interpreter.resolutionSource}`}
{` | model threshold ${formatConfidence(latest.interpreter.minConfidence)}`}
{` | selected ${formatConfidence(latest.interpreter.selectedConfidence)}`}
</p>
<p>
<strong>Interpreter version:</strong> {latest.interpreter.interpreterVersion}
</p>
{latest.interpreter.clarification ? (
<p>
<strong>Clarification:</strong> {latest.interpreter.clarification.question}
<strong>Clarification ({latest.interpreter.clarification.reasonCode}):</strong>{" "}
{latest.interpreter.clarification.question}
</p>
) : null}
{latest.interpreter.clarification?.options?.length ? (
<p>
<strong>Options:</strong>{" "}
{latest.interpreter.clarification.options.map((o) => o.label).join(", ")}
</p>
<div className="chips">
{latest.interpreter.clarification.options.map((o) => (
<button
key={o.id}
type="button"
className="chip"
onClick={() => applyClarificationOption(o.value)}
>
clarify: {o.label}
</button>
))}
</div>
) : null}
<ul className="timeline-list compact">
{latest.validation.map((v) => (
@@ -459,11 +517,25 @@ export default function App() {
<article className="panel">
<h2>Turn log</h2>
<ul className="timeline-list">
{snapshot?.turns.slice().reverse().map((turn) => (
{turns.map((turn) => (
<li key={turn.id}>
<strong>{turn.rawText}</strong>
<span className="turn-time"> at {formatTurnTime(turn.createdAt)}</span>
{turn.interpreter ? (
<span> [interp:{turn.interpreter.status}]</span>
<span>
{" "}[interp:{turn.interpreter.status} via {turn.interpreter.resolutionSource};
conf {formatConfidence(turn.interpreter.selectedConfidence)}]
</span>
) : null}
{turn.interpreter?.clarification ? (
<p className="parser-hint">
Clarification ({turn.interpreter.clarification.reasonCode}): {turn.interpreter.clarification.question}
</p>
) : null}
{turn.interpreter?.diagnostics?.length ? (
<p className="turn-diagnostics">
Diagnostics: {turn.interpreter.diagnostics.join(" | ")}
</p>
) : null}
{turn.validation.map((v) => (
<span key={v.actionIndex}> [{v.success ? "ok" : v.reason}]</span>

View File

@@ -375,4 +375,15 @@ pre {
.panel {
padding: 14px;
}
}
.turn-time {
opacity: 0.7;
font-size: 0.82rem;
}
.turn-diagnostics {
margin: 6px 0 0;
color: rgba(244, 239, 228, 0.74);
font-size: 0.82rem;
}