Refactor to be more concise

This commit is contained in:
Silvano Cerza 2025-02-19 11:49:09 +01:00
parent 63f9afe738
commit 5fd4c94566
6 changed files with 527 additions and 311 deletions

View file

@ -0,0 +1,63 @@
import * as React from "react";
import { DiffChunk } from "./diff";
interface ActionsGutterProps {
diffChunks: DiffChunk[];
// This is essential to correctly draw the lines between
// the left and right editor
lineHeight: number;
width: number;
}
const ActionsGutter: React.FC<ActionsGutterProps> = ({
diffChunks,
lineHeight,
width,
}) => {
const drawChunk = (chunk: DiffChunk, index: number) => {
const topLeft = (chunk.startLeftLine - 1) * lineHeight;
const bottomLeft = (chunk.endLeftLine - 1) * lineHeight;
const topRight = (chunk.startRightLine - 1) * lineHeight;
const bottomRight = (chunk.endRightLine - 1) * lineHeight;
const color =
chunk.type == "add"
? "var(--color-green)"
: chunk.type == "remove"
? "var(--color-red)"
: "var(--color-yellow)";
return (
<g key={index}>
<path
d={`
M 0 ${topLeft}
C ${width * 0.4} ${topLeft}, ${width * 0.6} ${topRight}, ${width} ${topRight}
L ${width} ${bottomRight}
C ${width * 0.6} ${bottomRight}, ${width * 0.4} ${bottomLeft}, 0 ${bottomLeft}
Z
`}
fill={color}
fillOpacity="0.1"
stroke={color}
strokeWidth="1"
/>
</g>
);
};
return (
<div style={{ width: width }}>
<svg
style={{
width: "100%",
height: "100%",
position: "absolute",
overflow: "visible",
}}
>
{diffChunks.map(drawChunk)}
</svg>
</div>
);
};
export default ActionsGutter;

View file

@ -5,6 +5,9 @@ import { markdown } from "@codemirror/lang-markdown";
import diff from "./diff"; import diff from "./diff";
import { createDiffHighlightPlugin } from "./diff-highlight-plugin"; import { createDiffHighlightPlugin } from "./diff-highlight-plugin";
import DiffConnections, { ConnectionChunk } from "./diff-connections"; import DiffConnections, { ConnectionChunk } from "./diff-connections";
import EditorPane from "./editor-pane";
import ActionsGutter from "./actions-gutter";
import * as React from "react";
// Add styles for diff highlighting // Add styles for diff highlighting
const styles = document.createElement("style"); const styles = document.createElement("style");
@ -24,254 +27,315 @@ document.head.appendChild(styles);
interface DiffViewProps { interface DiffViewProps {
oldText: string; oldText: string;
newText: string; newText: string;
onResolve: (resolvedText: string) => void; onOldTextChange: (content: string) => void;
onNewTextChange: (content: string) => void;
} }
const DiffView: React.FC<DiffViewProps> = ({ oldText, newText, onResolve }) => { const DiffView: React.FC<DiffViewProps> = ({
const [originalEditorView, setOriginalEditorView] = oldText,
useState<EditorView | null>(null); newText,
const [modifiedEditorView, setModifiedEditorView] = onOldTextChange,
useState<EditorView | null>(null); onNewTextChange,
}) => {
const containerRef = useRef<HTMLDivElement>(null); // We need to know the line height to correctly draw the ribbon between the left
const editorViewsRef = useRef<EditorView[]>([]); // and right editor in the actions gutter
const [lineHeight, setLineHeight] = React.useState<number>(0);
useEffect(() => { const handleEditorReady = (editor: EditorView) => {
if (!containerRef.current) return; setLineHeight(editor.defaultLineHeight);
const createEditor = (
container: HTMLElement,
content: string,
readOnly: boolean = false,
isOriginal: boolean = false,
) => {
const differences = diff(oldText || "", newText || "");
const highlightPlugin = createDiffHighlightPlugin({
diff: differences,
isOriginal,
});
const changeListener = !readOnly
? [
EditorView.updateListener.of((update) => {
if (update.docChanged) {
onResolve(update.state.doc.toString());
}
}),
]
: [];
const editorState = EditorState.create({
doc: content,
extensions: [
// basicSetup minus line numbers
EditorView.lineWrapping,
EditorView.editable.of(!readOnly),
highlightPlugin,
EditorView.theme({
"&": {
backgroundColor: "var(--background-primary)",
color: "var(--text-normal)",
},
".cm-line": {
padding: "0 4px",
},
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground": {
background: "var(--text-selection)",
},
"&.cm-focused .cm-cursor": {
borderLeftColor: "var(--text-normal)",
},
}),
markdown(),
...changeListener,
],
});
const view = new EditorView({
state: editorState,
parent: container,
});
if (isOriginal) {
setOriginalEditorView(view);
} else if (readOnly) {
setModifiedEditorView(view);
}
editorViewsRef.current.push(view);
return view;
};
// Create the three editors
const originalView = createEditor(
containerRef.current.querySelector(".original-editor")!,
oldText,
true,
true,
);
const modifiedView = createEditor(
containerRef.current.querySelector(".modified-editor")!,
newText,
true,
false,
);
const resultView = createEditor(
containerRef.current.querySelector(".result-editor")!,
newText,
false,
);
return () => {
editorViewsRef.current.forEach((view) => view.destroy());
editorViewsRef.current = [];
};
}, [oldText, newText]);
const handleMerge = (direction: "left" | "right", chunk: ConnectionChunk) => {
if (!originalEditorView || !modifiedEditorView) {
return;
}
const sourceEditor =
direction === "left" ? modifiedEditorView : originalEditorView;
const targetEditor =
direction === "left" ? originalEditorView : modifiedEditorView;
const sourceLine =
direction === "left" ? chunk.targetStartLine : chunk.startLine;
const sourceEndLine =
direction === "left" ? chunk.targetEndLine : chunk.endLine;
const targetLine =
direction === "left" ? chunk.startLine : chunk.targetStartLine;
const targetEndLine =
direction === "left" ? chunk.endLine : chunk.targetEndLine;
const sourceDoc = sourceEditor.state.doc;
const targetDoc = targetEditor.state.doc;
// Handle source content
let contentToInsert = "";
if (sourceLine > sourceDoc.lines) {
// Adding new lines at the end
contentToInsert = "\n" + chunk.content.join("\n");
} else {
const sourceFrom = sourceDoc.line(sourceLine).from;
const sourceTo =
sourceEndLine <= sourceDoc.lines
? sourceDoc.line(sourceEndLine).to
: sourceDoc.length;
contentToInsert = sourceEditor.state.sliceDoc(sourceFrom, sourceTo);
}
// Handle target position
let targetFrom = 0;
let targetTo = 0;
if (targetLine > targetDoc.lines) {
// Appending to end of file
targetFrom = targetTo = targetDoc.length;
if (!contentToInsert.startsWith("\n") && targetDoc.length > 0) {
contentToInsert = "\n" + contentToInsert;
}
} else {
targetFrom = targetDoc.line(targetLine).from;
targetTo =
targetEndLine <= targetDoc.lines
? targetDoc.line(targetEndLine).to
: targetDoc.length;
}
// Apply the change
const change = {
from: targetFrom,
to: targetTo,
insert: contentToInsert,
};
targetEditor.dispatch({ changes: change });
// Update result editor if it exists
// if (resultEditor) {
// const resultDoc = resultEditor.state.doc;
// let resultFrom = targetFrom;
// let resultTo = targetTo;
// // Handle case where result editor might have different content length
// if (resultFrom > resultDoc.length) {
// resultFrom = resultTo = resultDoc.length;
// if (!contentToInsert.startsWith("\n") && resultDoc.length > 0) {
// contentToInsert = "\n" + contentToInsert;
// }
// }
// resultEditor.dispatch({
// changes: {
// from: resultFrom,
// to: resultTo,
// insert: contentToInsert,
// },
// });
// }
// Notify parent
onResolve(targetEditor.state.doc.toString());
}; };
// const [leftWidth, setLeftWidth] = useState("33%");
const [gutterWidth, setGutterWidth] = useState(100);
// const [rightWidth, setRightWidth] = useState("33%");
const diffs = diff(oldText, newText);
return ( return (
<div <div
ref={containerRef}
style={{ style={{
display: "grid", display: "grid",
gridTemplateColumns: "1fr auto 1fr", gridTemplateColumns: "1fr auto 1fr",
gridTemplateRows: "1fr 1fr", gridTemplateRows: "1fr",
gap: "10px", gap: "0px",
height: "100%",
}} }}
> >
<div <div>
className="original-editor" <EditorPane
style={{ content={oldText}
border: "1px solid var(--background-modifier-border)", highlightPlugin={createDiffHighlightPlugin({
backgroundColor: "var(--background-primary)", diff: diffs,
}} isOriginal: true,
/> })}
<div onEditorUpdate={handleEditorReady}
className="diff-overlay" onContentChange={onOldTextChange}
style={{ />
gridRow: "1 / 2", </div>
gridColumn: "2 / 3", <div>
position: "relative", <ActionsGutter
width: "50px", diffChunks={diffs}
}} lineHeight={lineHeight}
> width={gutterWidth}
<DiffConnections />
differences={diff(oldText, newText)} </div>
originalEditor={originalEditorView} <div>
modifiedEditor={modifiedEditorView} <EditorPane
onMerge={handleMerge} content={newText}
highlightPlugin={createDiffHighlightPlugin({
diff: diffs,
isOriginal: false,
})}
onContentChange={onNewTextChange}
/> />
</div> </div>
<div
className="modified-editor"
style={{
border: "1px solid var(--background-modifier-border)",
backgroundColor: "var(--background-primary)",
}}
/>
<div
className="result-editor"
style={{
gridColumn: "1 / 4",
border: "1px solid var(--background-modifier-border)",
backgroundColor: "var(--background-primary)",
}}
/>
</div> </div>
); );
}; };
// const DiffView: React.FC<DiffViewProps> = ({ oldText, newText, onResolve }) => {
// const [originalEditorView, setOriginalEditorView] =
// useState<EditorView | null>(null);
// const [modifiedEditorView, setModifiedEditorView] =
// useState<EditorView | null>(null);
// const containerRef = useRef<HTMLDivElement>(null);
// const editorViewsRef = useRef<EditorView[]>([]);
// useEffect(() => {
// if (!containerRef.current) return;
// const createEditor = (
// container: HTMLElement,
// content: string,
// readOnly: boolean = false,
// isOriginal: boolean = false,
// ) => {
// const differences = diff(oldText || "", newText || "");
// const highlightPlugin = createDiffHighlightPlugin({
// diff: differences,
// isOriginal,
// });
// const changeListener = !readOnly
// ? [
// EditorView.updateListener.of((update) => {
// if (update.docChanged) {
// onResolve(update.state.doc.toString());
// }
// }),
// ]
// : [];
// const editorState = EditorState.create({
// doc: content,
// extensions: [
// // basicSetup minus line numbers
// EditorView.lineWrapping,
// EditorView.editable.of(!readOnly),
// highlightPlugin,
// EditorView.theme({
// "&": {
// backgroundColor: "var(--background-primary)",
// color: "var(--text-normal)",
// },
// ".cm-line": {
// padding: "0 4px",
// },
// "&.cm-focused .cm-selectionBackground, .cm-selectionBackground": {
// background: "var(--text-selection)",
// },
// "&.cm-focused .cm-cursor": {
// borderLeftColor: "var(--text-normal)",
// },
// }),
// markdown(),
// ...changeListener,
// ],
// });
// const view = new EditorView({
// state: editorState,
// parent: container,
// });
// if (isOriginal) {
// setOriginalEditorView(view);
// } else if (readOnly) {
// setModifiedEditorView(view);
// }
// editorViewsRef.current.push(view);
// return view;
// };
// // Create the three editors
// const originalView = createEditor(
// containerRef.current.querySelector(".original-editor")!,
// oldText,
// true,
// true,
// );
// const modifiedView = createEditor(
// containerRef.current.querySelector(".modified-editor")!,
// newText,
// true,
// false,
// );
// const resultView = createEditor(
// containerRef.current.querySelector(".result-editor")!,
// newText,
// false,
// );
// return () => {
// editorViewsRef.current.forEach((view) => view.destroy());
// editorViewsRef.current = [];
// };
// }, [oldText, newText]);
// const handleMerge = (direction: "left" | "right", chunk: ConnectionChunk) => {
// if (!originalEditorView || !modifiedEditorView) {
// return;
// }
// const sourceEditor =
// direction === "left" ? modifiedEditorView : originalEditorView;
// const targetEditor =
// direction === "left" ? originalEditorView : modifiedEditorView;
// const sourceLine =
// direction === "left" ? chunk.targetStartLine : chunk.startLine;
// const sourceEndLine =
// direction === "left" ? chunk.targetEndLine : chunk.endLine;
// const targetLine =
// direction === "left" ? chunk.startLine : chunk.targetStartLine;
// const targetEndLine =
// direction === "left" ? chunk.endLine : chunk.targetEndLine;
// const sourceDoc = sourceEditor.state.doc;
// const targetDoc = targetEditor.state.doc;
// // Handle source content
// let contentToInsert = "";
// if (sourceLine > sourceDoc.lines) {
// // Adding new lines at the end
// contentToInsert = "\n" + chunk.content.join("\n");
// } else {
// const sourceFrom = sourceDoc.line(sourceLine).from;
// const sourceTo =
// sourceEndLine <= sourceDoc.lines
// ? sourceDoc.line(sourceEndLine).to
// : sourceDoc.length;
// contentToInsert = sourceEditor.state.sliceDoc(sourceFrom, sourceTo);
// }
// // Handle target position
// let targetFrom = 0;
// let targetTo = 0;
// if (targetLine > targetDoc.lines) {
// // Appending to end of file
// targetFrom = targetTo = targetDoc.length;
// if (!contentToInsert.startsWith("\n") && targetDoc.length > 0) {
// contentToInsert = "\n" + contentToInsert;
// }
// } else {
// targetFrom = targetDoc.line(targetLine).from;
// targetTo =
// targetEndLine <= targetDoc.lines
// ? targetDoc.line(targetEndLine).to
// : targetDoc.length;
// }
// // Apply the change
// const change = {
// from: targetFrom,
// to: targetTo,
// insert: contentToInsert,
// };
// targetEditor.dispatch({ changes: change });
// // Update result editor if it exists
// // if (resultEditor) {
// // const resultDoc = resultEditor.state.doc;
// // let resultFrom = targetFrom;
// // let resultTo = targetTo;
// // // Handle case where result editor might have different content length
// // if (resultFrom > resultDoc.length) {
// // resultFrom = resultTo = resultDoc.length;
// // if (!contentToInsert.startsWith("\n") && resultDoc.length > 0) {
// // contentToInsert = "\n" + contentToInsert;
// // }
// // }
// // resultEditor.dispatch({
// // changes: {
// // from: resultFrom,
// // to: resultTo,
// // insert: contentToInsert,
// // },
// // });
// // }
// // Notify parent
// onResolve(targetEditor.state.doc.toString());
// };
// return (
// <div
// ref={containerRef}
// style={{
// display: "grid",
// gridTemplateColumns: "1fr auto 1fr",
// gridTemplateRows: "1fr 1fr",
// gap: "10px",
// height: "100%",
// }}
// >
// <div
// className="original-editor"
// style={{
// border: "1px solid var(--background-modifier-border)",
// backgroundColor: "var(--background-primary)",
// }}
// />
// <div
// className="diff-overlay"
// style={{
// gridRow: "1 / 2",
// gridColumn: "2 / 3",
// position: "relative",
// width: "50px",
// }}
// >
// <DiffConnections
// differences={diff(oldText, newText)}
// originalEditor={originalEditorView}
// modifiedEditor={modifiedEditorView}
// onMerge={handleMerge}
// />
// </div>
// <div
// className="modified-editor"
// style={{
// border: "1px solid var(--background-modifier-border)",
// backgroundColor: "var(--background-primary)",
// }}
// />
// <div
// className="result-editor"
// style={{
// gridColumn: "1 / 4",
// border: "1px solid var(--background-modifier-border)",
// backgroundColor: "var(--background-primary)",
// }}
// />
// </div>
// );
// };
export default DiffView; export default DiffView;

View file

@ -3,11 +3,12 @@ import {
Decoration, Decoration,
DecorationSet, DecorationSet,
EditorView, EditorView,
ViewUpdate,
} from "@codemirror/view"; } from "@codemirror/view";
import { DiffResult } from "./diff"; import { DiffChunk } from "./diff";
interface DiffHighlightPluginSpec { interface DiffHighlightPluginSpec {
diff: DiffResult[]; diff: DiffChunk[];
isOriginal: boolean; isOriginal: boolean;
} }
@ -20,40 +21,47 @@ export function createDiffHighlightPlugin(spec: DiffHighlightPluginSpec) {
this.decorations = this.buildDecorations(view); this.decorations = this.buildDecorations(view);
} }
update(update: ViewUpdate) {
if (update.docChanged) {
this.decorations = this.buildDecorations(update.view);
}
}
buildDecorations(view: EditorView): DecorationSet { buildDecorations(view: EditorView): DecorationSet {
const decorations = []; const decorations = [];
const doc = view.state.doc; const doc = view.state.doc;
for (let i = 1; i <= doc.lines; i++) { for (let i = 1; i <= doc.lines; i++) {
const line = doc.line(i); const line = doc.line(i);
const diffResult = spec.diff.find((d) => {
const diffResult = spec.diff.find((chunk) => {
if (spec.isOriginal) { if (spec.isOriginal) {
return ( return (
d.oldValue === line.text || (chunk.type === "remove" || chunk.type === "modify") &&
(d.type === "remove" && d.value === line.text) i >= chunk.startLeftLine &&
i < chunk.endLeftLine
); );
} else {
return d.type !== "remove" && d.value === line.text;
} }
return (
(chunk.type === "add" || chunk.type === "modify") &&
i >= chunk.startRightLine &&
i < chunk.endRightLine
);
}); });
if (diffResult && diffResult.type !== "equal") { if (diffResult) {
let className = ""; const className =
if (diffResult.type === "modify") { diffResult.type === "modify"
className = "diff-modify-background"; ? "diff-modify-background"
} else if (diffResult.type === "add" && !spec.isOriginal) { : diffResult.type === "add"
className = "diff-add-background"; ? "diff-add-background"
} else if (diffResult.type === "remove" && spec.isOriginal) { : "diff-remove-background";
className = "diff-remove-background";
}
if (className) { decorations.push(
decorations.push( Decoration.line({
Decoration.line({ class: className,
class: className, }).range(line.from),
}).range(line.from), );
);
}
} }
} }

View file

@ -1,68 +1,70 @@
type DiffType = "add" | "remove" | "modify" | "equal"; export interface DiffChunk {
type: "add" | "remove" | "modify";
export interface DiffResult { startLeftLine: number;
type: DiffType; endLeftLine: number;
value: string; startRightLine: number;
oldValue?: string; // For modifications, store both values endRightLine: number;
newValue?: string;
from: number;
to: number;
} }
function diff(oldText: string, newText: string): DiffResult[] { function diff(oldText: string, newText: string): DiffChunk[] {
const oldLines = oldText.split("\n"); const oldLines = oldText.split("\n");
const newLines = newText.split("\n"); const newLines = newText.split("\n");
const result: DiffResult[] = []; const result: DiffChunk[] = [];
// This is an index in oldText
let position = 0;
// First pass: find exact matches and obvious modifications
for (let i = 0; i < Math.max(oldLines.length, newLines.length); i++) { for (let i = 0; i < Math.max(oldLines.length, newLines.length); i++) {
const oldLine = oldLines[i]; const oldLine = oldLines[i];
const newLine = newLines[i]; const newLine = newLines[i];
if (!oldLine && newLine) { if (!oldLine && newLine) {
// Pure addition
result.push({ result.push({
type: "add", type: "add",
value: newLine, startLeftLine: i + 1,
from: position, endLeftLine: i + 1,
to: position + newLine.length, startRightLine: i + 1,
endRightLine: i + 2,
}); });
} else if (oldLine && !newLine) { } else if (oldLine && !newLine) {
// Pure removal
result.push({ result.push({
type: "remove", type: "remove",
value: oldLine, startLeftLine: i + 1,
from: position, endLeftLine: i + 2,
to: position + oldLine.length, startRightLine: i + 1,
endRightLine: i + 1,
}); });
} else if (oldLine === newLine) { } else if (oldLine !== newLine) {
// Exact match
result.push({
type: "equal",
value: oldLine,
from: position,
to: position + oldLine.length,
});
} else {
// Different content at same line number - treat as modification
result.push({ result.push({
type: "modify", type: "modify",
value: newLine, startLeftLine: i + 1,
oldValue: oldLine, endLeftLine: i + 2,
newValue: newLine, startRightLine: i + 1,
from: position, endRightLine: i + 2,
to: position + Math.max(oldLine.length, newLine.length),
}); });
} }
const oldLineLength = oldLine ? oldLine.length : 0;
const newLineLength = newLine ? newLine.length : 0;
position += Math.max(oldLineLength, newLineLength) + 1;
} }
// return result;
return mergeDiffs(result);
}
return result; function mergeDiffs(chunks: DiffChunk[]): DiffChunk[] {
if (chunks.length <= 1) return chunks;
return chunks.reduce((merged: DiffChunk[], current) => {
const previous = merged[merged.length - 1];
if (
previous &&
(previous.endLeftLine === current.startLeftLine ||
previous.endRightLine === current.startRightLine)
) {
previous.endLeftLine = current.endLeftLine;
previous.endRightLine = current.endRightLine;
previous.type = "modify";
return merged;
}
merged.push({ ...current });
return merged;
}, []);
} }
export default diff; export default diff;

View file

@ -0,0 +1,71 @@
import { markdown } from "@codemirror/lang-markdown";
import { EditorState, StateEffect } from "@codemirror/state";
import { EditorView, ViewPlugin } from "@codemirror/view";
import * as React from "react";
interface EditorPaneProps {
content: string;
highlightPlugin: ViewPlugin<any>;
onEditorUpdate?: (editor: EditorView) => void;
onContentChange: (content: string) => void;
}
const EditorPane: React.FC<EditorPaneProps> = ({
content,
highlightPlugin,
onEditorUpdate,
onContentChange,
}) => {
const editorRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (editorRef.current) {
const editorState = EditorState.create({
doc: content,
extensions: [
// basicSetup minus line numbers
// EditorView.lineWrapping,
EditorView.editable.of(true),
highlightPlugin,
EditorView.theme({
"&": {
backgroundColor: "var(--background-primary)",
color: "var(--text-normal)",
},
".cm-content": {
padding: 0,
},
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground": {
background: "var(--text-selection)",
},
"&.cm-focused .cm-cursor": {
borderLeftColor: "var(--text-normal)",
},
}),
markdown(),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
onContentChange(update.state.doc.toString());
}
// We want to know when it updates in case the line height changes
onEditorUpdate?.(update.view);
}),
],
});
const editor = new EditorView({
parent: editorRef.current,
state: editorState,
});
return () => {
// Cleanup
editor.destroy();
};
}
}, []);
return <div ref={editorRef} />;
};
export default EditorPane;

View file

@ -3,25 +3,26 @@ import { Root, createRoot } from "react-dom/client";
import DiffView from "./component"; import DiffView from "./component";
import GitHubSyncPlugin from "src/main"; import GitHubSyncPlugin from "src/main";
import { PluginContext } from "../hooks"; import { PluginContext } from "../hooks";
import * as React from "react";
export const CONFLICTS_RESOLUTION_VIEW_TYPE = "conflicts-resolution-view"; export const CONFLICTS_RESOLUTION_VIEW_TYPE = "conflicts-resolution-view";
// Test Case 1: Simple line changes // Test Case 1: Simple line changes
const oldText1 = `# My Document let oldText1 = `# My Document
This is a test This is a test
Some content here Some content here
Some line Some line
Another line Another line
Final line`; Final line`;
const newText1 = `# My Document let newText1 = `# My Document
This is a modified test This is a modified test
Some new content here Some new content here
Some line Some line
Final line`; Final line`;
// Test Case 2: Markdown with formatting // Test Case 2: Markdown with formatting
const oldText2 = `# Title let oldText2 = `# Title
## Subtitle ## Subtitle
- List item 1 - List item 1
- List item 2 - List item 2
@ -29,7 +30,7 @@ const oldText2 = `# Title
**Bold text** and *italic* text **Bold text** and *italic* text
Regular paragraph`; Regular paragraph`;
const newText2 = `# Modified Title let newText2 = `# Modified Title
## Subtitle ## Subtitle
- List item 1 - List item 1
- List item 2 - List item 2
@ -61,15 +62,22 @@ export class ConflictsResolutionView extends ItemView {
const container = this.containerEl.children[1]; const container = this.containerEl.children[1];
container.empty(); container.empty();
const root: Root = createRoot(container); const root: Root = createRoot(container);
root.render( const App = () => {
<PluginContext.Provider value={this.plugin}> const [oldText, setOldText] = React.useState(oldText1);
<DiffView const [newText, setNewText] = React.useState(newText1);
oldText={oldText1}
newText={newText1} return (
onResolve={(text) => console.log("Resolved:", text)} <PluginContext.Provider value={this.plugin}>
/> <DiffView
</PluginContext.Provider>, oldText={oldText}
); newText={newText}
onOldTextChange={setOldText}
onNewTextChange={setNewText}
/>
</PluginContext.Provider>
);
};
root.render(<App />);
} }
async onClose() { async onClose() {