From 008af1815647cf2cfb465127641d20e5cb93643b Mon Sep 17 00:00:00 2001
From: Jay V <air@live.ca>
Date: Thu, 26 Jun 2025 13:06:37 -0400
Subject: docs: share page responsive diff

---
 packages/web/src/components/DiffView.tsx | 191 ++++++++++++++++++++-----------
 1 file changed, 121 insertions(+), 70 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 237f25bdf..66dd7f0fc 100644
--- a/packages/web/src/components/DiffView.tsx
+++ b/packages/web/src/components/DiffView.tsx
@@ -54,8 +54,8 @@ const DiffView: Component<DiffViewProps> = (props) => {
               // 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]
+                const hasLeft = k < removals.length
+                const hasRight = k < additions.length
 
                 if (hasLeft && hasRight) {
                   // Replacement - left is removed, right is added
@@ -71,8 +71,8 @@ const DiffView: Component<DiffViewProps> = (props) => {
                     right: "",
                     type: "removed"
                   })
-                } else {
-                  // Pure addition
+                } else if (hasRight) {
+                  // Pure addition - only create if we actually have content
                   diffRows.push({
                     left: "",
                     right: additions[k],
@@ -111,85 +111,136 @@ const DiffView: Component<DiffViewProps> = (props) => {
     return diffRows
   })
 
+  const mobileRows = createMemo(() => {
+    const mobileBlocks: { type: 'removed' | 'added' | 'unchanged', lines: string[] }[] = []
+    const currentRows = rows()
+
+    let i = 0
+    while (i < currentRows.length) {
+      const removedLines: string[] = []
+      const addedLines: string[] = []
+
+      // Collect consecutive modified/removed/added rows
+      while (i < currentRows.length &&
+        (currentRows[i].type === 'modified' ||
+          currentRows[i].type === 'removed' ||
+          currentRows[i].type === 'added')) {
+        const row = currentRows[i]
+        if (row.left && (row.type === 'removed' || row.type === 'modified')) {
+          removedLines.push(row.left)
+        }
+        if (row.right && (row.type === 'added' || row.type === 'modified')) {
+          addedLines.push(row.right)
+        }
+        i++
+      }
+
+      // Add grouped blocks
+      if (removedLines.length > 0) {
+        mobileBlocks.push({ type: 'removed', lines: removedLines })
+      }
+      if (addedLines.length > 0) {
+        mobileBlocks.push({ type: 'added', lines: addedLines })
+      }
+
+      // Add unchanged rows as-is
+      if (i < currentRows.length && currentRows[i].type === 'unchanged') {
+        mobileBlocks.push({
+          type: 'unchanged',
+          lines: [currentRows[i].left]
+        })
+        i++
+      }
+    }
+
+    return mobileBlocks
+  })
+
   return (
     <div class={`${styles.diff} ${props.class ?? ""}`}>
-      {rows().map((r) => (
-        <div class={styles.row}>
-          <div class={styles.beforeColumn}>
-            <CodeBlock
-              code={r.left}
-              lang={props.lang}
-              data-section="cell"
-              data-diff-type={r.type === "removed" || r.type === "modified" ? "removed" : ""}
-              data-display-mobile={r.type === "added" && !r.left ? "false" : undefined}
-            />
-            {(r.type === "added" || r.type === "modified") && r.right !== undefined && (
+      <div class={styles.desktopView}>
+        {rows().map((r) => (
+          <div class={styles.row}>
+            <div class={styles.beforeColumn}>
+              <CodeBlock
+                code={r.left}
+                lang={props.lang}
+                data-section="cell"
+                data-diff-type={r.type === "removed" || r.type === "modified" ? "removed" : ""}
+              />
+            </div>
+            <div class={styles.afterColumn}>
               <CodeBlock
                 code={r.right}
                 lang={props.lang}
                 data-section="cell"
-                data-diff-type="added"
-                data-display-mobile="true"
+                data-diff-type={r.type === "added" || r.type === "modified" ? "added" : ""}
               />
-            )}
+            </div>
           </div>
+        ))}
+      </div>
 
-          <div class={styles.afterColumn}>
-            <CodeBlock
-              code={r.right}
-              lang={props.lang}
-              data-section="cell"
-              data-diff-type={r.type === "added" || r.type === "modified" ? "added" : ""}
-            />
+      <div class={styles.mobileView}>
+        {mobileRows().map((block) => (
+          <div class={styles.mobileBlock}>
+            {block.lines.map((line) => (
+              <CodeBlock
+                code={line}
+                lang={props.lang}
+                data-section="cell"
+                data-diff-type={block.type === 'removed' ? 'removed' :
+                  block.type === 'added' ? 'added' : ''}
+              />
+            ))}
           </div>
-        </div>
-      ))}
+        ))}
+      </div>
     </div>
   )
 }
 
 export default DiffView
 
-// String to test diff viewer with
-const testDiff = `--- combined_before.txt	2025-06-24 16:38:08
-+++ combined_after.txt	2025-06-24 16:38:12
-@@ -1,21 +1,25 @@
- unchanged line
--deleted line
--old content
-+added line
-+new content
- 
--removed empty line below
-+added empty line above
- 
--	tab indented
--trailing spaces   
--very long line that will definitely wrap in most editors and cause potential alignment issues when displayed in a two column diff view
--unicode content: 🚀 ✨ 中文
--mixed	content with	tabs and spaces
-+    space indented
-+no trailing spaces
-+short line
-+very long replacement line that will also wrap and test how the diff viewer handles long line additions after short line removals
-+different unicode: 🎉 💻 日本語
-+normalized content with consistent spacing
-+newline to content
- 
--content to remove
--whitespace only:    	  
--multiple
--consecutive
--deletions
--single deletion
-+    	  
-+single addition
-+first addition
-+second addition
-+third addition
- line before addition
-+first added line
-+
-+third added line
- line after addition
- final unchanged line`
+// const testDiff = `--- combined_before.txt	2025-06-24 16:38:08
+// +++ combined_after.txt	2025-06-24 16:38:12
+// @@ -1,21 +1,25 @@
+//  unchanged line
+// -deleted line
+// -old content
+// +added line
+// +new content
+//  
+// -removed empty line below
+// +added empty line above
+//  
+// -	tab indented
+// -trailing spaces   
+// -very long line that will definitely wrap in most editors and cause potential alignment issues when displayed in a two column diff view
+// -unicode content: 🚀 ✨ 中文
+// -mixed	content with	tabs and spaces
+// +    space indented
+// +no trailing spaces
+// +short line
+// +very long replacement line that will also wrap and test how the diff viewer handles long line additions after short line removals
+// +different unicode: 🎉 💻 日本語
+// +normalized content with consistent spacing
+// +newline to content
+//  
+// -content to remove
+// -whitespace only:    	  
+// -multiple
+// -consecutive
+// -deletions
+// -single deletion
+// +    	  
+// +single addition
+// +first addition
+// +second addition
+// +third addition
+//  line before addition
+// +first added line
+// +
+// +third added line
+//  line after addition
+//  final unchanged line`
-- 
cgit v1.2.3

