From 8b17a175c3e7e27b789812eba4e3cd760beadb10 Mon Sep 17 00:00:00 2001 From: soryu Date: Tue, 6 Jan 2026 04:08:11 +0000 Subject: Initial Control system --- .../src/components/mesh/MergeConflictResolver.tsx | 504 +++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 makima/frontend/src/components/mesh/MergeConflictResolver.tsx (limited to 'makima/frontend/src/components/mesh/MergeConflictResolver.tsx') diff --git a/makima/frontend/src/components/mesh/MergeConflictResolver.tsx b/makima/frontend/src/components/mesh/MergeConflictResolver.tsx new file mode 100644 index 0000000..4479705 --- /dev/null +++ b/makima/frontend/src/components/mesh/MergeConflictResolver.tsx @@ -0,0 +1,504 @@ +import { useState, useMemo, useCallback } from "react"; + +interface ConflictHunk { + id: string; + filePath: string; + startLine: number; + endLine: number; + ours: string[]; // Changes from current branch + theirs: string[]; // Changes from incoming branch + base?: string[]; // Original content (if 3-way merge) + resolved?: "ours" | "theirs" | "both" | "custom"; + customResolution?: string[]; +} + +interface ConflictFile { + path: string; + hunks: ConflictHunk[]; + resolved: boolean; +} + +interface MergeConflictResolverProps { + conflicts: ConflictFile[]; + sourceBranch: string; + targetBranch: string; + loading?: boolean; + onResolve: (resolutions: Map) => Promise; + onAbort: () => void; + onAskLLM?: (hunk: ConflictHunk) => Promise; +} + +type ResolutionChoice = "ours" | "theirs" | "both" | "custom"; + +function ConflictHunkView({ + hunk, + sourceBranch, + targetBranch, + onResolve, + onAskLLM, +}: { + hunk: ConflictHunk; + sourceBranch: string; + targetBranch: string; + onResolve: (resolution: ResolutionChoice, customLines?: string[]) => void; + onAskLLM?: () => Promise; +}) { + const [showCustomEditor, setShowCustomEditor] = useState(false); + const [customText, setCustomText] = useState( + hunk.customResolution?.join("\n") || [...hunk.ours, ...hunk.theirs].join("\n") + ); + const [askingLLM, setAskingLLM] = useState(false); + + const handleAskLLM = async () => { + if (!onAskLLM || askingLLM) return; + setAskingLLM(true); + try { + await onAskLLM(); + } finally { + setAskingLLM(false); + } + }; + + const handleCustomSave = () => { + const lines = customText.split("\n"); + onResolve("custom", lines); + setShowCustomEditor(false); + }; + + const isResolved = hunk.resolved !== undefined; + + return ( +
+ {/* Hunk header */} +
+
+ Lines {hunk.startLine}-{hunk.endLine} + {isResolved && ( + + (Resolved: {hunk.resolved}) + + )} +
+
+ {onAskLLM && ( + + )} + +
+
+ + {/* Conflict content */} + {!showCustomEditor ? ( +
+ {/* Ours (current branch) */} +
+
+ + {targetBranch} (ours) + + +
+
+              {hunk.ours.map((line, i) => (
+                
+ - + {line} +
+ ))} +
+
+ + {/* Theirs (incoming branch) */} +
+
+ + {sourceBranch} (theirs) + + +
+
+              {hunk.theirs.map((line, i) => (
+                
+ + + {line} +
+ ))} +
+
+
+ ) : ( + /* Custom editor */ +
+
+ + Custom Resolution + +
+ + +
+
+