summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/diff-ssr.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-26 18:23:04 -0600
committerGitHub <[email protected]>2026-02-26 18:23:04 -0600
commitfc52e4b2d3a41efde772e6de8fb2e01f27821701 (patch)
treecf23af294a00a10e55f230232585344c111f0bb9 /packages/ui/src/components/diff-ssr.tsx
parent9a6bfeb782766099d4ce3a98bb9e7b4e79f8bfe6 (diff)
downloadopencode-fc52e4b2d3a41efde772e6de8fb2e01f27821701.tar.gz
opencode-fc52e4b2d3a41efde772e6de8fb2e01f27821701.zip
feat(app): better diff/code comments (#14621)
Co-authored-by: adamelmore <[email protected]> Co-authored-by: David Hill <[email protected]>
Diffstat (limited to 'packages/ui/src/components/diff-ssr.tsx')
-rw-r--r--packages/ui/src/components/diff-ssr.tsx317
1 files changed, 0 insertions, 317 deletions
diff --git a/packages/ui/src/components/diff-ssr.tsx b/packages/ui/src/components/diff-ssr.tsx
deleted file mode 100644
index e739afc16..000000000
--- a/packages/ui/src/components/diff-ssr.tsx
+++ /dev/null
@@ -1,317 +0,0 @@
-import { DIFFS_TAG_NAME, FileDiff, type SelectedLineRange, VirtualizedFileDiff } from "@pierre/diffs"
-import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr"
-import { createEffect, onCleanup, onMount, Show, splitProps } from "solid-js"
-import { Dynamic, isServer } from "solid-js/web"
-import { createDefaultOptions, styleVariables, type DiffProps } from "../pierre"
-import { acquireVirtualizer, virtualMetrics } from "../pierre/virtualizer"
-import { useWorkerPool } from "../context/worker-pool"
-
-export type SSRDiffProps<T = {}> = DiffProps<T> & {
- preloadedDiff: PreloadMultiFileDiffResult<T>
-}
-
-export function Diff<T>(props: SSRDiffProps<T>) {
- let container!: HTMLDivElement
- let fileDiffRef!: HTMLElement
- const [local, others] = splitProps(props, [
- "before",
- "after",
- "class",
- "classList",
- "annotations",
- "selectedLines",
- "commentedLines",
- ])
- const workerPool = useWorkerPool(props.diffStyle)
-
- let fileDiffInstance: FileDiff<T> | undefined
- let sharedVirtualizer: NonNullable<ReturnType<typeof acquireVirtualizer>> | undefined
- const cleanupFunctions: Array<() => void> = []
-
- const getRoot = () => fileDiffRef?.shadowRoot ?? undefined
-
- const getVirtualizer = () => {
- if (sharedVirtualizer) return sharedVirtualizer.virtualizer
-
- const result = acquireVirtualizer(container)
- if (!result) return
-
- sharedVirtualizer = result
- return result.virtualizer
- }
-
- const applyScheme = () => {
- const scheme = document.documentElement.dataset.colorScheme
- if (scheme === "dark" || scheme === "light") {
- fileDiffRef.dataset.colorScheme = scheme
- return
- }
-
- fileDiffRef.removeAttribute("data-color-scheme")
- }
-
- const lineIndex = (split: boolean, element: HTMLElement) => {
- const raw = element.dataset.lineIndex
- if (!raw) return
- const values = raw
- .split(",")
- .map((value) => parseInt(value, 10))
- .filter((value) => !Number.isNaN(value))
- if (values.length === 0) return
- if (!split) return values[0]
- if (values.length === 2) return values[1]
- return values[0]
- }
-
- const rowIndex = (root: ShadowRoot, split: boolean, line: number, side: "additions" | "deletions" | undefined) => {
- const nodes = Array.from(root.querySelectorAll(`[data-line="${line}"], [data-alt-line="${line}"]`)).filter(
- (node): node is HTMLElement => node instanceof HTMLElement,
- )
- if (nodes.length === 0) return
-
- const targetSide = side ?? "additions"
-
- for (const node of nodes) {
- if (findSide(node) === targetSide) return lineIndex(split, node)
- if (parseInt(node.dataset.altLine ?? "", 10) === line) return lineIndex(split, node)
- }
- }
-
- const fixSelection = (range: SelectedLineRange | null) => {
- if (!range) return range
- const root = getRoot()
- if (!root) return
-
- const diffs = root.querySelector("[data-diff]")
- if (!(diffs instanceof HTMLElement)) return
-
- const split = diffs.dataset.diffType === "split"
-
- const start = rowIndex(root, split, range.start, range.side)
- const end = rowIndex(root, split, range.end, range.endSide ?? range.side)
-
- if (start === undefined || end === undefined) {
- if (root.querySelector("[data-line], [data-alt-line]") == null) return
- return null
- }
- if (start <= end) return range
-
- const side = range.endSide ?? range.side
- const swapped: SelectedLineRange = {
- start: range.end,
- end: range.start,
- }
- if (side) swapped.side = side
- if (range.endSide && range.side) swapped.endSide = range.side
-
- return swapped
- }
-
- const setSelectedLines = (range: SelectedLineRange | null, attempt = 0) => {
- const diff = fileDiffInstance
- if (!diff) return
-
- const fixed = fixSelection(range)
- if (fixed === undefined) {
- if (attempt >= 120) return
- requestAnimationFrame(() => setSelectedLines(range, attempt + 1))
- return
- }
-
- diff.setSelectedLines(fixed)
- }
-
- const findSide = (element: HTMLElement): "additions" | "deletions" => {
- const line = element.closest("[data-line], [data-alt-line]")
- if (line instanceof HTMLElement) {
- const type = line.dataset.lineType
- if (type === "change-deletion") return "deletions"
- if (type === "change-addition" || type === "change-additions") return "additions"
- }
-
- const code = element.closest("[data-code]")
- if (!(code instanceof HTMLElement)) return "additions"
- return code.hasAttribute("data-deletions") ? "deletions" : "additions"
- }
-
- const applyCommentedLines = (ranges: SelectedLineRange[]) => {
- const root = getRoot()
- if (!root) return
-
- const existing = Array.from(root.querySelectorAll("[data-comment-selected]"))
- for (const node of existing) {
- if (!(node instanceof HTMLElement)) continue
- node.removeAttribute("data-comment-selected")
- }
-
- const diffs = root.querySelector("[data-diff]")
- if (!(diffs instanceof HTMLElement)) return
-
- const split = diffs.dataset.diffType === "split"
-
- const rows = Array.from(diffs.querySelectorAll("[data-line-index]")).filter(
- (node): node is HTMLElement => node instanceof HTMLElement,
- )
- if (rows.length === 0) return
-
- const annotations = Array.from(diffs.querySelectorAll("[data-line-annotation]")).filter(
- (node): node is HTMLElement => node instanceof HTMLElement,
- )
-
- const lineIndex = (element: HTMLElement) => {
- const raw = element.dataset.lineIndex
- if (!raw) return
- const values = raw
- .split(",")
- .map((value) => parseInt(value, 10))
- .filter((value) => !Number.isNaN(value))
- if (values.length === 0) return
- if (!split) return values[0]
- if (values.length === 2) return values[1]
- return values[0]
- }
-
- const rowIndex = (line: number, side: "additions" | "deletions" | undefined) => {
- const nodes = Array.from(root.querySelectorAll(`[data-line="${line}"], [data-alt-line="${line}"]`)).filter(
- (node): node is HTMLElement => node instanceof HTMLElement,
- )
- if (nodes.length === 0) return
-
- const targetSide = side ?? "additions"
-
- for (const node of nodes) {
- if (findSide(node) === targetSide) return lineIndex(node)
- if (parseInt(node.dataset.altLine ?? "", 10) === line) return lineIndex(node)
- }
- }
-
- for (const range of ranges) {
- const start = rowIndex(range.start, range.side)
- if (start === undefined) continue
-
- const end = (() => {
- const same = range.end === range.start && (range.endSide == null || range.endSide === range.side)
- if (same) return start
- return rowIndex(range.end, range.endSide ?? range.side)
- })()
- if (end === undefined) continue
-
- const first = Math.min(start, end)
- const last = Math.max(start, end)
-
- for (const row of rows) {
- const idx = lineIndex(row)
- if (idx === undefined) continue
- if (idx < first || idx > last) continue
- row.setAttribute("data-comment-selected", "")
- }
-
- for (const annotation of annotations) {
- const idx = parseInt(annotation.dataset.lineAnnotation?.split(",")[1] ?? "", 10)
- if (Number.isNaN(idx)) continue
- if (idx < first || idx > last) continue
- annotation.setAttribute("data-comment-selected", "")
- }
- }
- }
-
- onMount(() => {
- if (isServer || !props.preloadedDiff) return
-
- applyScheme()
-
- if (typeof MutationObserver !== "undefined") {
- const root = document.documentElement
- const monitor = new MutationObserver(() => applyScheme())
- monitor.observe(root, { attributes: true, attributeFilter: ["data-color-scheme"] })
- onCleanup(() => monitor.disconnect())
- }
-
- const virtualizer = getVirtualizer()
-
- fileDiffInstance = virtualizer
- ? new VirtualizedFileDiff<T>(
- {
- ...createDefaultOptions(props.diffStyle),
- ...others,
- ...props.preloadedDiff,
- },
- virtualizer,
- virtualMetrics,
- workerPool,
- )
- : new FileDiff<T>(
- {
- ...createDefaultOptions(props.diffStyle),
- ...others,
- ...props.preloadedDiff,
- },
- workerPool,
- )
- // @ts-expect-error - fileContainer is private but needed for SSR hydration
- fileDiffInstance.fileContainer = fileDiffRef
- fileDiffInstance.hydrate({
- oldFile: local.before,
- newFile: local.after,
- lineAnnotations: local.annotations,
- fileContainer: fileDiffRef,
- containerWrapper: container,
- })
-
- setSelectedLines(local.selectedLines ?? null)
-
- createEffect(() => {
- fileDiffInstance?.setLineAnnotations(local.annotations ?? [])
- })
-
- createEffect(() => {
- setSelectedLines(local.selectedLines ?? null)
- })
-
- createEffect(() => {
- const ranges = local.commentedLines ?? []
- requestAnimationFrame(() => applyCommentedLines(ranges))
- })
-
- // Hydrate annotation slots with interactive SolidJS components
- // if (props.annotations.length > 0 && props.renderAnnotation != null) {
- // for (const annotation of props.annotations) {
- // const slotName = `annotation-${annotation.side}-${annotation.lineNumber}`;
- // const slotElement = fileDiffRef.querySelector(
- // `[slot="${slotName}"]`
- // ) as HTMLElement;
- //
- // if (slotElement != null) {
- // // Clear the static server-rendered content from the slot
- // slotElement.innerHTML = '';
- //
- // // Mount a fresh SolidJS component into this slot using render().
- // // This enables full SolidJS reactivity (signals, effects, etc.)
- // const dispose = render(
- // () => props.renderAnnotation!(annotation),
- // slotElement
- // );
- // cleanupFunctions.push(dispose);
- // }
- // }
- // }
- })
-
- onCleanup(() => {
- // Clean up FileDiff event handlers and dispose SolidJS components
- fileDiffInstance?.cleanUp()
- cleanupFunctions.forEach((dispose) => dispose())
- sharedVirtualizer?.release()
- sharedVirtualizer = undefined
- })
-
- return (
- <div data-component="diff" style={styleVariables} ref={container}>
- <Dynamic component={DIFFS_TAG_NAME} ref={fileDiffRef} id="ssr-diff">
- <Show when={isServer}>
- <template shadowrootmode="open" innerHTML={props.preloadedDiff.prerenderedHTML} />
- </Show>
- </Dynamic>
- </div>
- )
-}