import type { SnapShotSource } from "@t3tools/contracts";
import { ImageIcon, TextIcon } from "lucide-react";
import { Suspense, use, useMemo, type CSSProperties } from "react";
import { useTheme } from "../../hooks/useTheme";
import { resolveDiffThemeName } from "../../lib/diffRendering";
import { getSyntaxHighlighterPromise } from "../../lib/syntaxHighlighting";
import { cn } from "../../lib/utils";
import { RenderErrorBoundary } from "../RenderErrorBoundary";
import { Button } from "../ui/button";
import { Popover, PopoverPopup, PopoverTitle, PopoverTrigger } from "../ui/popover";
import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
export const SNAP_SHOT_ATTACHMENT_FRAME_CLASS =
"relative h-28 w-52 max-w-full overflow-hidden rounded-lg border border-border/80";
export interface SnapShotAccessibilityDetails {
content: string;
format: "json" | "text";
}
interface SyntaxToken {
readonly content: string;
readonly offset: number;
readonly color?: string;
readonly fontStyle?: number;
}
function syntaxTokenStyle(token: SyntaxToken): CSSProperties {
const fontStyle = token.fontStyle ?? 0;
return {
...(token.color ? { color: token.color } : {}),
...(fontStyle & 1 ? { fontStyle: "italic" } : {}),
...(fontStyle & 2 ? { fontWeight: 700 } : {}),
...(fontStyle & 4 ? { textDecoration: "underline" } : {}),
};
}
function HighlightedAccessibilityJson({
content,
theme,
}: {
content: string;
theme: "light" | "dark";
}) {
const highlighter = use(getSyntaxHighlighterPromise("json"));
const lines = useMemo(
() =>
highlighter.codeToTokens(content, {
lang: "json",
theme: resolveDiffThemeName(theme),
}).tokens,
[content, highlighter, theme],
);
let lineOffset = 0;
return lines.map((line) => {
const lineContent = line.map((token) => token.content).join("");
const lineKey = `${lineOffset}:${lineContent}`;
const hasNextLine = lineOffset + lineContent.length < content.length;
lineOffset += lineContent.length + 1;
return (
{line.map((token) => (
{token.content}
))}
{hasNextLine ? "\n" : null}
);
});
}
export function SnapShotAccessibilityData({
details,
className,
}: {
details: SnapShotAccessibilityDetails;
className?: string;
}) {
const { resolvedTheme } = useTheme();
const content =
details.format === "json" ? (
{content}
);
}
export function snapShotAccessibilityDetails(
source: SnapShotSource,
): SnapShotAccessibilityDetails | undefined {
if (source.accessibility?.format === "element-tree") {
return {
content: JSON.stringify(source.accessibility, null, 2),
format: "json",
};
}
const text =
source.accessibility?.format === "flat-text"
? source.accessibility.text.trim()
: source.accessibleText?.trim();
return text ? { content: text, format: "text" } : undefined;
}
export function snapShotIncludesAccessibility(source: SnapShotSource): boolean {
return Boolean(source.accessibility || source.accessibleText?.trim());
}
export function SnapShotContentsButton({
source,
className,
side = "top",
}: {
source: SnapShotSource;
className?: string;
side?: "top" | "right" | "bottom" | "left";
}) {
const includesAccessibility = snapShotIncludesAccessibility(source);
const ContentsIcon = includesAccessibility ? TextIcon : ImageIcon;
const accessibilityDetails = snapShotAccessibilityDetails(source);
const tooltip = includesAccessibility ? "Accessibility data" : "No accessibility data";
return (