Mismerge is a modern two-way and one-way merge editor for the web, built with Svelte. You can visit the demo and start merging now, or use it as a component for your project. It is also available in React and Vue.
npm i @mismerge/core
<script>
import { MisMerge3 } from '@mismerge/core';
// Core styles, required for the editor to work properly
import '@mismerge/core/styles.css';
import '@mismerge/core/light.css';
// Or '@mismerge/core/dark.css';
let lhs = 'foo';
let ctr = 'bar';
let rhs = 'baz';
</script>
<!-- Left-hand side and right-hand side constant text -->
<MisMerge3 {lhs} {rhs} bind:ctr />
<!-- All sides editable -->
<MisMerge3 bind:lhs bind:ctr bind:rhs lhsEditable rhsEditable >
<style>
:global(.mismerge) {
font-family: monospace;
min-height: 600px;
}
</style>
Install the additional adapter package:
npm i @mismerge/react
import { DefaultDarkColors, MisMerge3 } from '@mismerge/react';
import { useEffect, useState } from 'react';
import '@mismerge/core/styles.css';
import '@mismerge/core/dark.css';
function App() {
const [ctr, setCtr] = useState('Hello world!');
useEffect(() => {
console.log(ctr);
}, [ctr]);
return (
<>
<MisMerge3
lhs="Hello world!"
ctr={ctr}
rhs="Hello world!"
onCtrChange={setCtr}
colors={DefaultDarkColors}
wrapLines={true}
/>
</>
);
}
Install the additional adapter package:
npm i @mismerge/vue
[!NOTE]
Due to some differences in how Vue treats boolean attributes, some default properties may not correspond to the ones described in the API section.
<script setup lang="ts">
import { MisMerge3, DefaultDarkColors } from '@mismerge/vue';
import '@mismerge/core/styles.css';
import '@mismerge/core/dark.css';
</script>
<template>
<MisMerge3
lhs="Hello"
ctr="World"
rhs="!"
ctr-editable
:colors="DefaultDarkColors"
:on-ctr-change="console.log"
/>
</template>
You need to provide your own syntax highlighter. Example and demo using Shiki-JS. The highlighter can be either sync or async.
<script>
import { codeToHtml } from 'shiki';
// ...
const highlight = async (text: string) =>
await codeToHtml(text, {
lang: 'js',
theme: 'min-dark'
});
</script>
<MisMerge3 ... {highlight} />
<script>
import { DefaultDarkColors } from '@mismerge/core';
// ...
</script>
<MisMerge3 ... colors={DefaultDarkColors} />
If you want to customize the editor styles, you can copy the default light or dark theme and adapt it to your need.
Here is a basic explanation of how the the rendered html looks like:
<div class="mismerge">
<div>
<!-- Main -->
<div class="msm__main">
<!-- View -->
<div class="msm__view">
<!-- Content -->
<div class="msm__view-content">
<!-- Blocks wrapper -->
<div class="msm__wrapper">
<!-- Blocks -->
<div data-component-id="abcdefgh" class="msm__block block-type">
<!-- Lines -->
<div class="msm__line">
<!-- ... -->
</div>
<!-- ... -->
</div>
<!-- ... -->
</div>
<!-- Highlight overlay -->
<div class="msm__highlight-overlay">
<!-- ... -->
</div>
<!-- Input -->
<textarea />
</div>
<!-- Side panel -->
<div class="msm__side-panel">
<!-- ... -->
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="msm__footer">
<!-- ... -->
</div>
</div>
A list of properties for <MisMerge2>
(2), <MisMerge3>
(3), or both.
Property | Type | Default | Description | Component |
---|---|---|---|---|
lhs |
string |
"" |
Left-hand side text | Both |
ctr |
string |
"" |
Center text | 3 |
rhs |
string |
"" |
Right-hand side text | Both |
colors |
EditorColors |
DefaultLightColors |
Connections colors | Both |
highlight |
(text: string) => string | Promise<string> |
undefined |
Syntax highlighter | Both |
lhsEditable |
boolean |
true (2), false (3) |
Can edit left panel | Both |
ctrEditable |
boolean |
true |
Can edit center panel | 3 |
rhsEditable |
boolean |
true (2), false (3) |
Can edit right panel | Both |
lineDiffAlgorithm |
'characters' | 'words' | 'words_with_space' |
words_with_space |
Diff algorithm for same line side by side diff | Both |
disableMerging |
boolean |
false |
Disables merging | Both |
wrapLines |
boolean |
false |
Enables lines wrapping | Both |
disableWordsCounter |
boolean |
false |
Disables words counter | Both |
disableCharsCounter |
boolean |
false |
Disables chars counter | Both |
disableBlocksCounters |
boolean |
false |
Disables blocks counter | Both |
disableFooter |
boolean |
false |
Disables footer | Both |
ignoreWhitespace |
boolean |
false |
Ignore whitespace in diff | Both |
ignoreCase |
boolean |
false |
Ignore case in diff | Both |
conflictsResolved |
boolean |
- | Binding for when all conflicts have been resolved | 3 |
Events (available for Svelte):
Name | Description |
---|---|
on:merge |
Fired when a block is merged from one side to an adjacent one |
on:resolve |
Fired when a conflict has its resolved status toggled |
on:delete |
Fired when a block is deleted in the center side |
on:input |
Default textarea event |
on:keydown |
Default textarea event |
on:keypress |
Default textarea event |
on:keyup |
Default textarea event |
Clone the repo:
git clone https://github.com/BearToCode/mismerge.git
cd mismerge
Download dependencies for all packages in the monorepo:
npm install
The core package is inside packages/core
. You can run the associated sveltekit app using npm run core
or cd packages/core
& npm run dev
.
The demo is inside the demo
root folder. You can run it from root using npm run demo
or cd demo
& npm run dev
.
It is automatically deployed to Github Pages with every push to master.
This repository uses commitizen to enforce similar commit messages. Commit using:
npm run commit
# or
git cz