/** * How to reach what the agent changed while you were reading somewhere else. * * A mark is held back until it is on screen, which answers the question of * whether it was worth showing but not the question of how to find it. These * are the other half: one chip per edge, counting only what is still unread in * that direction, and going to the nearest one when clicked — which reveals it * the ordinary way, by putting it in front of the reader. * * The list behind them shows every live change, seen or not. That is the only * place a removal can be read: the hole in the prose can say that something * was here, but the block itself is gone and cannot be asked what it was. */ import { useCallback, useRef, useState, useSyncExternalStore } from "react"; import { ArrowUpIcon, ChevronIcon } from "@chopin/icons"; import { MotionDisclosureIcon } from "./disclosure-motion"; import { usePopoverDismissal } from "./popover-dismissal"; import type { ChangeStore, Entry, Snapshot } from "./changes"; /** Past this the number stops being informative and starts being noise. */ const MANY = 9; export function useChanges(store: ChangeStore): Snapshot { return useSyncExternalStore(store.subscribe, store.snapshot, store.snapshot); } function count(value: number): string { return value > MANY ? `${MANY}+` : String(value); } function label(entry: Entry): string { switch (entry.kind) { case "added": return "Written"; case "moved": return "Moved"; case "removed": return entry.blocks.length > 1 ? `Removed ${entry.blocks.length} blocks` : "Removed"; } } /** * Who wrote it: the verified caller first, then the client they used. The * client names itself and could claim to be anyone, so it never stands alone; * one that gave no name is reported by the server as `unknown`. */ export function author(entry: Entry): string | undefined { let attribution = entry.attribution; if (!attribution) return undefined; let user = `@${attribution.user}`; return attribution.client.name === "unknown" ? user : `${user} via ${attribution.client.name}`; } function provenance(entry: Entry): string | undefined { let attribution = entry.attribution; if (!attribution) return undefined; let client = attribution.client.name === "unknown" ? "An unnamed MCP client" : `${attribution.client.name} ${attribution.client.version}`; return `${client}, revisions ${attribution.fromRevision} to ${attribution.revision}`; } function describe(entry: Entry): string { let first = entry.blocks[0]; if (!first) return ""; let text = first.preview.trim(); // A block with no words of its own — a divider, an image, a component with // nothing written in it — is named by what it is instead. return text || first.type; } function List({ entries }: { entries: Entry[] }) { if (entries.length === 0) { return
Nothing recent.
; } return (