fix(frontend,harness): phase-stepper wiring, central tail, activity-panel markdown
- WorkflowBar: center the phase pills (add justify-center).
- tht-gate.js: the gate was sending the descriptive phase name (e.g.
"chiarimento") instead of the workflow.yaml id ("F1") in the widget's
`phase` field, so the frontend's F1..F8 match never hit and pills never
lit up. Rename phaseName -> phaseId, return p.id.
- CentralStatus: expand the live "working" tail from a single truncated
line to up to 5 lines (line-clamp-5 safety net for unbroken paragraphs).
- ModelActivityPanel: render the streamed tail through react-markdown +
remark-gfm instead of raw per-line <p> tags, so emphasis/headings render
and blank-line paragraph breaks (previously stripped) are preserved;
tail-cut now operates on paragraphs instead of physical lines.
Updated tests accordingly (WorkflowBar/CentralStatus/ModelActivityPanel/
AppShell.session-mgmt); full frontend suite (98/98) + harness gate node
tests (34/34) + tsc -b pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,14 @@ import { useEffect, useReducer, useState } from "react";
|
||||
import { useSessionStore } from "../store/sessionStore";
|
||||
import { WorkingSpinner } from "./WorkingSpinner";
|
||||
|
||||
/** Last few words of a string — a compact "what the model is doing now" tail. */
|
||||
function lastWords(s: string, n = 8): string {
|
||||
const w = s.trim().split(/\s+/).filter(Boolean);
|
||||
return w.slice(-n).join(" ");
|
||||
/** Last few non-blank lines of a string — a compact multi-line "what the model is
|
||||
* doing now" tail (mirrors ModelActivityPanel's tail-line logic, at a smaller size). */
|
||||
function tailLines(s: string, n = 5): string {
|
||||
const lines = s
|
||||
.split("\n")
|
||||
.map((l) => l.trimEnd())
|
||||
.filter((l) => l.trim() !== "");
|
||||
return lines.slice(-n).join("\n");
|
||||
}
|
||||
|
||||
/** The minimal central view: the user's last input/choice, a live "working" indicator
|
||||
@@ -41,7 +45,7 @@ export function CentralStatus({
|
||||
if (!working && !lastUserEntry && stepMessages.length === 0) return null;
|
||||
|
||||
const elapsed = startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1000)) : 0;
|
||||
const tail = lastWords(transcript.at(-1)?.text ?? "");
|
||||
const tail = tailLines(transcript.at(-1)?.text ?? "");
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
@@ -55,19 +59,25 @@ export function CentralStatus({
|
||||
)}
|
||||
|
||||
{working && (
|
||||
<div className="flex min-w-0 items-center gap-2 text-sm text-muted-foreground">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenActivity}
|
||||
aria-label="Show model activity"
|
||||
title="Model activity"
|
||||
className="grid size-5 shrink-0 place-items-center rounded text-primary transition-colors hover:bg-muted"
|
||||
>
|
||||
<WorkingSpinner spinning className="size-3.5" />
|
||||
</button>
|
||||
<span className="shrink-0 tabular-nums">{elapsed}s</span>
|
||||
{tail && <span className="min-w-0 truncate text-muted-foreground/70">· {tail}</span>}
|
||||
<span className="sr-only">ThothII is working</span>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex min-w-0 items-center gap-2 text-sm text-muted-foreground">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOpenActivity}
|
||||
aria-label="Show model activity"
|
||||
title="Model activity"
|
||||
className="grid size-5 shrink-0 place-items-center rounded text-primary transition-colors hover:bg-muted"
|
||||
>
|
||||
<WorkingSpinner spinning className="size-3.5" />
|
||||
</button>
|
||||
<span className="shrink-0 tabular-nums">{elapsed}s</span>
|
||||
<span className="sr-only">ThothII is working</span>
|
||||
</div>
|
||||
{tail && (
|
||||
<p className="line-clamp-5 whitespace-pre-wrap break-words pl-7 text-sm text-muted-foreground/70">
|
||||
{tail}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user