HistoryDiffView.tsx2.4 KBView on GitHub
'use client';

import { ReadOnlyMarkdownView } from '@/components/read-only-markdown-view';
import { DiffText } from '@/modules/cedar-os/src/cedar-os-components/diffs/DiffText';

export type HistoryDiffMode = 'formatted' | 'diff';

export interface HistoryDiffViewProps {
  /** The selected version's markdown, or null while loading / nothing selected. */
  markdown: string | null;
  /** The previous session's markdown — needed for `mode='diff'`. Null for the
   *  first session (no previous) or while loading. */
  previousMarkdown: string | null;
  /** `formatted` renders via the read-only TipTap editor (full rich formatting,
   *  no diff). `diff` renders the markdown source via DiffText with inline
   *  added/removed highlights. Falls back to formatted when there is no
   *  previous version to diff against. */
  mode: HistoryDiffMode;
  isLoading: boolean;
}

/**
 * Renders the selected historical version of a document.
 *
 *   - `mode='formatted'` (default): the markdown is parsed by the same TipTap
 *     + Markdown stack as the live editor, with `editable: false`. Headings,
 *     lists, code blocks, tables all look identical to the live editor.
 *   - `mode='diff'`: DiffText renders the markdown source with previous-vs-
 *     selected word-level highlights (insertions in green, deletions in red
 *     strikethrough). Raw markdown text — no rich-text rendering — because
 *     diff overlays don't compose with parsed ProseMirror nodes.
 */
export function HistoryDiffView({
  markdown,
  previousMarkdown,
  mode,
  isLoading,
}: HistoryDiffViewProps) {
  if (isLoading && markdown == null) {
    return (
      <div className="text-sm text-muted-foreground" role="status">
        Loading version…
      </div>
    );
  }
  if (markdown == null) {
    return (
      <div className="text-sm italic text-muted-foreground">
        Select a version on the right to preview it.
      </div>
    );
  }

  if (mode === 'diff' && previousMarkdown != null) {
    return (
      <div
        data-testid="history-diff"
        className="whitespace-pre-wrap font-sans text-sm leading-relaxed"
      >
        <DiffText
          oldText={previousMarkdown}
          newText={markdown}
          diffMode="words"
          showRemoved={true}
          animateChanges={false}
        />
      </div>
    );
  }

  return (
    <div data-testid="history-version">
      <ReadOnlyMarkdownView markdown={markdown} />
    </div>
  );
}