import { ChevronLeft, ChevronRight, RotateCcw } from "lucide-react" import { useEffect, useMemo, useState } from "react" import { Shimmer } from "@/components/ai-elements/shimmer" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { useDiagram } from "@/contexts/diagram-context" import { useDictionary } from "@/hooks/use-dictionary" import { diffDiagrams, isSameDocument } from "@/lib/diagram-diff" import { formatMessage } from "@/lib/i18n/utils" import { describeChanges } from "@/lib/version-text" import { useUiStore } from "@/stores/ui-store" import { useVersionsStore } from "@/stores/versions-store" import { useChatEngine } from "./chat-engine" function Sheet({ label, svg, loadingText, emptyText, }: { label: string svg: string | null | undefined loadingText: string emptyText: string }) { return ( {label} {svg === "" ? ( {emptyText} ) : svg ? ( // biome-ignore lint/performance/noImgElement: data URL thumbnail ) : ( {loadingText} )} ) } /** A saved version next to the current canvas, with restore */ export function CompareDialog() { const dict = useDictionary() const t = dict.versions const engine = useChatEngine() const { getVersionSvg, chartXML } = useDiagram() const versionId = useUiStore((s) => s.compareVersionId) const openCompare = useUiStore((s) => s.openCompare) const closeCompare = useUiStore((s) => s.closeCompare) const versions = useVersionsStore((s) => s.versions) const [currentSvg, setCurrentSvg] = useState(null) const index = versions.findIndex((v) => v.id === versionId) const version = index >= 0 ? versions[index] : null // Whether the canvas is this version, on every page const same = useMemo( () => !version || isSameDocument(version.xml, chartXML), [version, chartXML], ) // What the canvas has changed since this version on the first page // (null: nothing there) const changes = useMemo(() => { if (!version || same) return null const { summary } = diffDiagrams(version.xml, chartXML) return Object.values(summary).some((n) => n > 0) ? summary : null }, [version, chartXML, same]) // Picture of the canvas as it is now useEffect(() => { if (!versionId) return let cancelled = false setCurrentSvg(null) getVersionSvg().then((svg) => { if (!cancelled) setCurrentSvg(svg) }) return () => { cancelled = true } }, [versionId]) return ( !open && closeCompare()} > {t.compareTitle} {!version ? "" : same ? t.isOnCanvas : changes ? `${formatMessage(t.compareSince, { n: index + 1 })} ${describeChanges(changes, t)}` : formatMessage(t.differsElsewhere, { n: index + 1, })} {version && ( )} openCompare(versions[index - 1].id)} className="inline-flex size-8 items-center justify-center rounded-lg border border-border text-muted-foreground hover:text-foreground disabled:opacity-30" aria-label={t.previous} > = versions.length - 1} onClick={() => openCompare(versions[index + 1].id)} className="inline-flex size-8 items-center justify-center rounded-lg border border-border text-muted-foreground hover:text-foreground disabled:opacity-30" aria-label={t.next} > {index + 1} / {versions.length} { if (!version) return engine.restoreVersion(version.id) closeCompare() }} className="ml-auto inline-flex h-9 items-center gap-1.5 rounded-lg bg-primary px-3.5 text-[13px] font-medium text-primary-foreground hover:opacity-90 disabled:opacity-40" data-testid="compare-restore" > {formatMessage(t.restoreVersion, { n: index + 1 })} ) }