"use client";
import { useEffect } from "react";
import { createPortal } from "react-dom";
import type { CompactionDetails } from "@/src/core";
/** What one divider carries: the marker's full model-facing content (the
* [SESSION HISTORY COMPACTED] header + the summary) and its details —
* both dividers open the same dialog from the same payload. */
export interface CompactionDividerData {
content: string;
details: CompactionDetails;
}
/** The moment divider (owner round 14): minimal centered small text where
* the user currently is — the retrying-message register (SystemLine's
* exact classes), NO horizontal rule. The transcript's quiet note that
* history was just summarized; the cut divider below carries the seam. */
export function CompactionMomentDivider({ data, onOpen }: { data: CompactionDividerData; onOpen: () => void }) {
return (
);
}
/** The cut divider (owner round 14): the LONGER divider at the actual
* compaction cut — the seam where verbatim history ends and the kept
* recent tail begins. Phase-divider's geometry (dotted rules + centered
* chip) so it reads as a transcript boundary, not a message. */
export function CompactionCutDivider({ data, onOpen }: { data: CompactionDividerData; onOpen: () => void }) {
const kTok = data.details.tokensBefore > 0 ? ` · ${Math.round(data.details.tokensBefore / 1000)}k tok` : "";
return (
);
}
/** The dialog's pure body — exported for SSR pins (the delete-dialog /
* node-dialog body pattern; the wrapper owns the portal + Esc effect). */
export function CompactionSummaryBody({ summary, onClose }: { summary: CompactionDividerData; onClose: () => void }) {
return (
Compacted context
{summary.content}
);
}
/** The full-summary view both dividers open (owner round 14): the
* marker's content verbatim — the compaction header + the summary the
* model itself reads — in a scrollable document block. Esc and the
* Close row both dismiss; the backdrop click does not (the summary is
* reference material, not a decision — an accidental dismissal costs a
* re-click, and a stray click-through mid-read costs more). */
export function CompactionSummaryDialog({
summary,
onClose,
}: {
summary: CompactionDividerData | null;
onClose: () => void;
}) {
useEffect(() => {
if (!summary) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [summary, onClose]);
if (!summary) return null;
return createPortal(