import { Suspense, lazy, onMount, type Accessor, type JSXElement } from "solid-js" import type { ToolState } from "@opencode-ai/sdk/v2" import type { RenderCache } from "../../types/message" import type { DiffViewMode } from "../../stores/preferences" import type { DiffPayload, DiffRenderOptions, ToolScrollHelpers } from "./types" import { getRelativePath } from "./utils" import { getCacheEntry } from "../../lib/global-cache" const LazyToolCallDiffViewer = lazy(() => import("../diff-viewer").then((module) => ({ default: module.ToolCallDiffViewer })), ) function CachedDiffMarkup(props: { html: string; onRendered?: () => void }) { onMount(() => { props.onRendered?.() }) return (
) } type CacheHandle = { get(): T | undefined params(): unknown } type DiffPrefs = { diffViewMode?: DiffViewMode } export function createDiffContentRenderer(params: { toolState: Accessor preferences: Accessor setDiffViewMode: (mode: DiffViewMode) => void isDark: Accessor t: (key: string, params?: Record) => string diffCache: CacheHandle permissionDiffCache: CacheHandle scrollHelpers: ToolScrollHelpers handleScrollRendered: () => void onContentRendered?: () => void }) { const registerTracked = (element: HTMLDivElement | null) => { params.scrollHelpers.registerContainer(element) } const registerUntracked = (element: HTMLDivElement | null) => { params.scrollHelpers.registerContainer(element, { disableTracking: true }) } function renderDiffContent(payload: DiffPayload, options?: DiffRenderOptions): JSXElement | null { const relativePath = payload.filePath ? getRelativePath(payload.filePath) : "" const toolbarLabel = options?.label || (relativePath ? params.t("toolCall.diff.label.withPath", { path: relativePath }) : params.t("toolCall.diff.label")) const selectedVariant = options?.variant === "permission-diff" ? "permission-diff" : "diff" const cacheHandle = selectedVariant === "permission-diff" ? params.permissionDiffCache : params.diffCache const diffMode = () => (params.preferences().diffViewMode || "split") as DiffViewMode const themeKey = params.isDark() ? "dark" : "light" const state = params.toolState() const disableScrollTracking = Boolean( options?.disableScrollTracking || (state?.status !== "running" && state?.status !== "pending"), ) const registerRef = disableScrollTracking ? registerUntracked : registerTracked const baseEntryParams = cacheHandle.params() as any const cacheEntryParams = (() => { const suffix = typeof options?.cacheKey === "string" ? options.cacheKey.trim() : "" if (!suffix) return baseEntryParams return { ...baseEntryParams, cacheId: `${baseEntryParams.cacheId}:${suffix}`, } })() let cachedHtml: string | undefined const cached = getCacheEntry(cacheEntryParams) const currentMode = diffMode() if (cached && cached.text === payload.diffText && cached.theme === themeKey && cached.mode === currentMode) { cachedHtml = cached.html } const handleModeChange = (mode: DiffViewMode) => { params.setDiffViewMode(mode) } const handleDiffRendered = () => { if (!disableScrollTracking) { params.handleScrollRendered() } params.onContentRendered?.() } return (
{toolbarLabel}
{cachedHtml ? ( ) : ( {payload.diffText}}> )} {params.scrollHelpers.renderSentinel({ disableTracking: disableScrollTracking })}
) } return { renderDiffContent } }