Extraction surfaces: sidebar coverage block, chat-bar ring, phase un-pill, panel death
The owner-locked extraction-UX redesign (dispatch 2026-09-24), UI chunk over Platform's data layer (eed4b4e — GET /api/chats/[id] carries `coverage`, the threshold is Graph ▸ extractionChunkTokens): 1. SIDEBAR BLOCK (components/extraction-coverage.tsx + sidebar's session menu): the coverage info block at the TOP of the context menu — uncovered tokens, uncovered messages, running state — then the Extract affordance (optimistic "extracting… / Running" until the server says running; 409 = benign, the trigger raced us), fetched ON MENU OPEN (the block mounts with the menu — on-demand, never eagerly), polled at 2s while a run is in flight. Visually separated from Rename/Delete by a hairline; those stay the two session-management actions. Failed read → nothing (never fake zeros). 2. RING METER (CoverageRing, chat top bar, before the PhaseChip): a 16px donut filling uncoveredTokens / extractionChunkTokens — "how close this session is to its next extraction run". Click → the exact numbers (covered/uncovered tokens, messages, the threshold, lastFoldedAt via timeAgo). Refresh chains: mount, the turn-settle bump (chat.tsx coverageTick), the settings signal (threshold/kill-switch re-read live), a 2s drain-poll while a run is in flight, and the ACTIVITY FEED — a run for this session appearing or settling in the dock's feed re-reads coverage (runs fire from other surfaces — the sidebar menu, the turn pipeline — and no turn-settle bump announces them; the runs-empty-transition the graph view rides). Vanishes entirely when the graph is off or before the first honest read. Running = the whole ring PULSES (implementer's visual call: motion is the working-state signal in this app's quiet register). 3. PHASECHIP UN-PILL: the phase label is a flat tag — no border, no rounding, no padding. The Plan chip is the only button-shaped thing in the top bar. Drag-surface (pointer-events-none) and aria semantics unchanged; the transcript PhaseDivider keeps its look. 4. PANEL DEATH: components/graph-coverage.tsx, its graph-view mount, and tests/render-graph-coverage.test.tsx die whole. Confirmed: after this, NOTHING client-side consumes GET /api/graph/coverage — the only remaining client use of the coverage endpoints is the Extract POST (kept, by design); the routes stay (Platform's call). Pure seams exported for tests: ringFill (clamp math), CoverageMenuBody, CoverageRingGlyph, CoverageRingDetail. 901/901. Live-verified on :3001: ring fill arcs match the API byte-for-byte (483/4096 = 4.45/37.70; 1991/4096 = 18.32/37.70); the sidebar block reads "2k uncovered / 4 messages unextracted"; a real Extract run went optimistic → dock run → menu settled "fully extracted" AND the ring drained to the empty arc (the feed chain); graph off → the ring vanishes; the flat phase label renders chrome-free beside the Plan pill.
This commit is contained in:
@@ -35,6 +35,7 @@ import { QuestionDock } from "@/components/question-dock";
|
||||
import { SidebarToggle } from "@/components/sidebar";
|
||||
import type { ArtifactPartProps, JobRecord, Phase3, QuestionAnswerData, TeacherUIMessage, TurnUsage } from "@/src/core";
|
||||
import { PhaseChip, PhaseDivider, planNodeCount } from "@/components/phase-divider";
|
||||
import { CoverageRing } from "@/components/extraction-coverage";
|
||||
import { findPendingToolCall, phaseAt3, phaseTimeline, streamingToolActivity } from "@/src/core";
|
||||
import { slotsConfigured, type ModelsSettings } from "@/src/core";
|
||||
import { useSettingsSaved } from "@/components/settings-signal";
|
||||
@@ -478,6 +479,9 @@ export function Chat({
|
||||
// The live plan popup (wave 4): opened by any transcript plan block, the
|
||||
// top-bar plan chip, and the dock band's marginalia.
|
||||
const [planOpen, setPlanOpen] = useState(false);
|
||||
// The coverage ring's refresh chain (extraction-UX redesign): bumped at
|
||||
// turn settle — the moment this session's uncovered mass changes.
|
||||
const [coverageTick, setCoverageTick] = useState(0);
|
||||
// The mermaid focus (chrome-melt wave, 2026-09-19): md-blocks' expand
|
||||
// button registers the chart source here; the overlay (and the plan
|
||||
// popup) mount INSIDE the session-area div — both are session-scoped, so
|
||||
@@ -584,6 +588,10 @@ export function Chat({
|
||||
// immediate: turns-only refetch — the stats bar updates even while
|
||||
// the server's persist/endTurn lags the client by milliseconds
|
||||
void syncTurns();
|
||||
// The coverage ring re-reads at turn settle too (extraction-UX
|
||||
// redesign, 2026-09-24): this turn's messages just joined the
|
||||
// uncovered mass; the ring's own running-poll handles runs.
|
||||
setCoverageTick((t) => t + 1);
|
||||
// Turn-end nudge (accuracy wave, 2026-09-19): the extraction
|
||||
// pipeline fires at this turn's onFinish — the dock's poll would
|
||||
// discover a started run up to 10s late. Refetch the activity feed
|
||||
@@ -922,6 +930,11 @@ export function Chat({
|
||||
Plan
|
||||
</button>
|
||||
)}
|
||||
{/* The extraction ring (owner-locked redesign, 2026-09-24): fills
|
||||
with uncovered vs the chunk threshold, pulses while a run is in
|
||||
flight, vanishes when the graph is off. Click → the exact
|
||||
numbers. Before the phase label — the chips cluster right. */}
|
||||
<CoverageRing chatId={chatId} refreshKey={coverageTick} />
|
||||
<PhaseChip phase={phases.at(-1) ?? "probe"} />
|
||||
</header>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user