From f76cdfff9b28b7632bcbcf2a58172ce2f5e6cec0 Mon Sep 17 00:00:00 2001
From: Dax Raad <d@ironbay.co>
Date: Wed, 4 Jun 2025 18:32:10 -0400
Subject: sync

---
 packages/web/src/components/DiffView.tsx | 106 ++++++++++++++++++++++++++-----
 1 file changed, 91 insertions(+), 15 deletions(-)

(limited to 'packages/web/src/components/DiffView.tsx')

diff --git a/packages/web/src/components/DiffView.tsx b/packages/web/src/components/DiffView.tsx
index 6f89d6a97..a7464e2e6 100644
--- a/packages/web/src/components/DiffView.tsx
+++ b/packages/web/src/components/DiffView.tsx
@@ -1,35 +1,111 @@
-import { type Component, createMemo, createSignal, onMount } from "solid-js"
-import { diffLines, type ChangeObject } from "diff"
+import { type Component, createMemo } from "solid-js"
+import { parsePatch } from "diff"
 import CodeBlock from "./CodeBlock"
 import styles from "./diffview.module.css"
 
 type DiffRow = {
   left: string
   right: string
-  type: "added" | "removed" | "unchanged"
+  type: "added" | "removed" | "unchanged" | "modified"
 }
 
 interface DiffViewProps {
-  changes: ChangeObject<string>[]
+  diff: string
   lang?: string
   class?: string
 }
 
 const DiffView: Component<DiffViewProps> = (props) => {
+
   const rows = createMemo(() => {
     const diffRows: DiffRow[] = []
 
-    for (const item of props.changes) {
-      const lines = item.value.split(/\r?\n/)
-      if (lines.at(-1) === "") lines.pop()
+    try {
+      const patches = parsePatch(props.diff)
+
+      for (const patch of patches) {
+        for (const hunk of patch.hunks) {
+          const lines = hunk.lines
+          let i = 0
+
+          while (i < lines.length) {
+            const line = lines[i]
+            const content = line.slice(1)
+            const prefix = line[0]
+
+            if (prefix === '-') {
+              // Look ahead for consecutive additions to pair with removals
+              const removals: string[] = [content]
+              let j = i + 1
+
+              // Collect all consecutive removals
+              while (j < lines.length && lines[j][0] === '-') {
+                removals.push(lines[j].slice(1))
+                j++
+              }
+
+              // Collect all consecutive additions that follow
+              const additions: string[] = []
+              while (j < lines.length && lines[j][0] === '+') {
+                additions.push(lines[j].slice(1))
+                j++
+              }
+
+              // Pair removals with additions
+              const maxLength = Math.max(removals.length, additions.length)
+              for (let k = 0; k < maxLength; k++) {
+                const hasLeft = !!removals[k]
+                const hasRight = !!additions[k]
+
+                if (hasLeft && hasRight) {
+                  // Replacement - left is removed, right is added
+                  diffRows.push({
+                    left: removals[k],
+                    right: additions[k],
+                    type: "modified"
+                  })
+                } else if (hasLeft) {
+                  // Pure removal
+                  diffRows.push({
+                    left: removals[k],
+                    right: "",
+                    type: "removed"
+                  })
+                } else {
+                  // Pure addition
+                  diffRows.push({
+                    left: "",
+                    right: additions[k],
+                    type: "added"
+                  })
+                }
+              }
 
-      for (const line of lines) {
-        diffRows.push({
-          left: item.removed ? line : item.added ? "" : line,
-          right: item.added ? line : item.removed ? "" : line,
-          type: item.added ? "added" : item.removed ? "removed" : "unchanged",
-        })
+              i = j
+            } else if (prefix === '+') {
+              // Standalone addition (not paired with removal)
+              diffRows.push({
+                left: "",
+                right: content,
+                type: "added"
+              })
+              i++
+            } else if (prefix === ' ') {
+              diffRows.push({
+                left: content,
+                right: content,
+                type: "unchanged"
+              })
+              i++
+            } else {
+              i++
+            }
+          }
+        }
       }
+    } catch (error) {
+      console.error("Failed to parse patch:", error)
+      return []
     }
 
     return diffRows
@@ -43,7 +119,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
             code={r.left}
             lang={props.lang}
             data-section="cell"
-            data-diff-type={r.type === "removed" ? "removed" : ""}
+            data-diff-type={r.type === "removed" || r.type === "modified" ? "removed" : ""}
           />
         ))}
       </div>
@@ -54,7 +130,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
             code={r.right}
             lang={props.lang}
             data-section="cell"
-            data-diff-type={r.type === "added" ? "added" : ""}
+            data-diff-type={r.type === "added" || r.type === "modified" ? "added" : ""}
           />
         ))}
       </div>
-- 
cgit v1.2.3

