Update example website
This commit is contained in:
parent
3e87a68dfd
commit
4851e85986
3 changed files with 245 additions and 225 deletions
|
|
@ -3,88 +3,75 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Easily merge three versions of a text document with this 3-way text merge tool."
|
||||||
|
/>
|
||||||
|
<meta property="og:title" content="3-Way Text Merge" />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Easily merge three versions of a text document with this 3-way text merge tool."
|
||||||
|
/>
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta
|
||||||
|
property="og:url"
|
||||||
|
content="https://github.com/schmelczer/reconcile"
|
||||||
|
/>
|
||||||
|
<meta property="og:image" content="/favicon.ico" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
<title>3-Way Text Merge</title>
|
<title>3-Way Text Merge</title>
|
||||||
<link rel="stylesheet" href="style.css" />
|
<link rel="stylesheet" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header>
|
||||||
<h1>3-Way Text Merge</h1>
|
<h1>3-Way Text Merge</h1>
|
||||||
<div class="diamond-layout">
|
<p>Use this tool to merge three versions of a text.</p>
|
||||||
<div class="diamond-row diamond-row-top">
|
</header>
|
||||||
<div class="text-area diamond-parent">
|
|
||||||
<label for="original">Original</label>
|
<main>
|
||||||
<textarea id="original" rows="10"></textarea>
|
<div class="text-area diamond-parent">
|
||||||
</div>
|
<label for="original">Original</label>
|
||||||
</div>
|
<textarea id="original" name="original"></textarea>
|
||||||
<div class="diamond-row diamond-row-middle">
|
|
||||||
<div class="text-area diamond-left">
|
|
||||||
<label for="left">Left</label>
|
|
||||||
<textarea id="left" rows="10"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="text-area diamond-right">
|
|
||||||
<label for="right">Right</label>
|
|
||||||
<textarea id="right" rows="10"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="diamond-row diamond-row-bottom">
|
|
||||||
<div class="diamond-bottom-content">
|
|
||||||
<button id="merge-button">Merge</button>
|
|
||||||
<div class="text-area diamond-result">
|
|
||||||
<label for="merged">Merged</label>
|
|
||||||
<textarea id="merged" rows="10" readonly></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<script type="module">
|
|
||||||
import init, { mergeText } from "./reconcile.js";
|
|
||||||
|
|
||||||
async function run() {
|
<div class="text-area diamond-left">
|
||||||
await init();
|
<label for="left">First concurrent edit</label>
|
||||||
|
<textarea id="left" name="left"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
const originalTextArea = document.getElementById("original");
|
<div class="text-area diamond-right">
|
||||||
const leftTextArea = document.getElementById("left");
|
<label for="right">Second concurrent edit</label>
|
||||||
const rightTextArea = document.getElementById("right");
|
<textarea id="right" name="right"></textarea>
|
||||||
const mergedTextArea = document.getElementById("merged");
|
</div>
|
||||||
const mergeButton = document.getElementById("merge-button");
|
|
||||||
|
|
||||||
const sampleTexts = [
|
<button id="merge-button" type="button">Merge</button>
|
||||||
"The quick brown fox jumps over the lazy dog.",
|
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
|
||||||
"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.",
|
|
||||||
"A journey of a thousand miles begins with a single step.",
|
|
||||||
"To be, or not to be, that is the question.",
|
|
||||||
];
|
|
||||||
|
|
||||||
function loadSample() {
|
<div class="text-area diamond-result">
|
||||||
const randomIndex = Math.floor(
|
<label for="merged">Deconflicted result (readonly)</label>
|
||||||
Math.random() * sampleTexts.length
|
<textarea id="merged" name="merged" readonly></textarea>
|
||||||
);
|
</div>
|
||||||
const text = sampleTexts[randomIndex];
|
</main>
|
||||||
originalTextArea.value = text;
|
|
||||||
leftTextArea.value = text;
|
|
||||||
rightTextArea.value = text;
|
|
||||||
mergedTextArea.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeButton.addEventListener("click", () => {
|
<footer>
|
||||||
const original = originalTextArea.value;
|
<p>2025 Andras Schmelczer</p>
|
||||||
const left = leftTextArea.value;
|
<a
|
||||||
const right = rightTextArea.value;
|
href="https://github.com/schmelczer/reconcile"
|
||||||
|
class="github-link"
|
||||||
|
aria-label="GitHub repository"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</footer>
|
||||||
|
|
||||||
try {
|
<script type="module" src="script.js"></script>
|
||||||
const result = mergeText(original, left, right);
|
|
||||||
mergedTextArea.value = result;
|
|
||||||
} catch (e) {
|
|
||||||
mergedTextArea.value = `Error: ${e}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("load", loadSample);
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
41
examples/website/script.js
Normal file
41
examples/website/script.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import init, { mergeText } from "./reconcile.js";
|
||||||
|
|
||||||
|
const originalTextArea = document.getElementById("original");
|
||||||
|
const leftTextArea = document.getElementById("left");
|
||||||
|
const rightTextArea = document.getElementById("right");
|
||||||
|
const mergedTextArea = document.getElementById("merged");
|
||||||
|
const mergeButton = document.getElementById("merge-button");
|
||||||
|
|
||||||
|
const sampleTexts = [
|
||||||
|
"The quick brown fox jumps over the lazy dog.",
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||||
|
"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.",
|
||||||
|
"A journey of a thousand miles begins with a single step.",
|
||||||
|
"To be, or not to be, that is the question.",
|
||||||
|
];
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await init();
|
||||||
|
|
||||||
|
mergeButton.addEventListener("click", () => {
|
||||||
|
const original = originalTextArea.value;
|
||||||
|
const left = leftTextArea.value;
|
||||||
|
const right = rightTextArea.value;
|
||||||
|
|
||||||
|
const result = mergeText(original, left, right);
|
||||||
|
mergedTextArea.value = result;
|
||||||
|
});
|
||||||
|
|
||||||
|
loadSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSample() {
|
||||||
|
const randomIndex = Math.floor(Math.random() * sampleTexts.length);
|
||||||
|
const text = sampleTexts[randomIndex];
|
||||||
|
originalTextArea.value = text;
|
||||||
|
leftTextArea.value = text;
|
||||||
|
rightTextArea.value = text;
|
||||||
|
mergedTextArea.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
|
|
@ -1,200 +1,192 @@
|
||||||
body {
|
* {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px;
|
|
||||||
background-color: #f0f2f5;
|
|
||||||
color: #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
html,
|
||||||
max-width: 1200px;
|
body {
|
||||||
margin: 0 auto;
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Segoe UI", Arial, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #f8fafc 0%, #e0e7ef 100%);
|
||||||
|
color: #23272f;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
header {
|
||||||
|
padding: 32px 20px 0 20px;
|
||||||
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #1c1e21;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.diamond-layout {
|
header > h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2451a6;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > p {
|
||||||
|
color: #5a6272;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto auto auto;
|
grid-template-rows: auto auto auto;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr auto 1fr;
|
||||||
gap: 20px 20px;
|
gap: 20px;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
padding: 32px 12vw 32px 12vw;
|
||||||
|
min-height: 540px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diamond-row {
|
.diamond-parent {
|
||||||
display: contents;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.diamond-row-top .diamond-parent {
|
.diamond-left {
|
||||||
grid-column: 2 / 3;
|
grid-column: 1;
|
||||||
grid-row: 1 / 2;
|
grid-row: 2;
|
||||||
}
|
}
|
||||||
.diamond-row-middle .diamond-left {
|
|
||||||
grid-column: 1 / 2;
|
.diamond-right {
|
||||||
grid-row: 2 / 3;
|
grid-column: 3;
|
||||||
|
grid-row: 2;
|
||||||
}
|
}
|
||||||
.diamond-row-middle .diamond-right {
|
|
||||||
grid-column: 3 / 4;
|
#merge-button {
|
||||||
grid-row: 2 / 3;
|
grid-column: 2;
|
||||||
|
grid-row: 2;
|
||||||
|
padding: 12px 36px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: linear-gradient(90deg, #2451a6 0%, #3486eb 100%);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(36, 81, 166, 0.08);
|
||||||
|
cursor: pointer;
|
||||||
|
align-self: center;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
margin: 0 32px;
|
||||||
}
|
}
|
||||||
.diamond-row-bottom .diamond-bottom-content {
|
|
||||||
|
#merge-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.diamond-right {
|
||||||
|
grid-column: 3;
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diamond-result {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 3;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
pointer-events: none;
|
||||||
gap: 10px;
|
|
||||||
grid-column: 2 / 3;
|
|
||||||
grid-row: 3 / 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diamond-row-bottom .diamond-result {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
flex: 1 1 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diamond-bottom-content {
|
|
||||||
width: 100%;
|
|
||||||
max-width: none;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diamond-bottom-content .diamond-result {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diamond-bottom-content textarea {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diamond-parent, .diamond-result {
|
|
||||||
grid-column: 1 / -1 !important;
|
|
||||||
width: 100%;
|
|
||||||
max-width: none;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.diamond-left, .diamond-right {
|
|
||||||
min-width: 280px;
|
|
||||||
max-width: 500px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
|
||||||
.diamond-layout {
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
.diamond-row-top .diamond-parent,
|
|
||||||
.diamond-row-bottom .diamond-bottom-content {
|
|
||||||
grid-column: 1 / 3;
|
|
||||||
}
|
|
||||||
.diamond-row-middle .diamond-left {
|
|
||||||
grid-column: 1 / 2;
|
|
||||||
}
|
|
||||||
.diamond-row-middle .diamond-right {
|
|
||||||
grid-column: 2 / 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.diamond-layout {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.diamond-row-top .diamond-parent,
|
|
||||||
.diamond-row-middle .diamond-left,
|
|
||||||
.diamond-row-middle .diamond-right,
|
|
||||||
.diamond-row-bottom .diamond-bottom-content {
|
|
||||||
grid-column: 1 / 2;
|
|
||||||
}
|
|
||||||
.diamond-row-bottom .diamond-bottom-content {
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
.diamond-result textarea,
|
|
||||||
.diamond-parent textarea,
|
|
||||||
.diamond-left textarea,
|
|
||||||
.diamond-right textarea {
|
|
||||||
font-size: 1rem;
|
|
||||||
min-height: 100px;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
padding: 14px 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.diamond-layout {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.diamond-row-top .diamond-parent,
|
|
||||||
.diamond-row-middle .diamond-left,
|
|
||||||
.diamond-row-middle .diamond-right,
|
|
||||||
.diamond-row-bottom .diamond-result {
|
|
||||||
grid-column: 1 / 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-area {
|
.text-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(36, 81, 166, 0.06);
|
||||||
|
padding: 18px 20px 16px 20px;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 8px;
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
|
color: #2451a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
border: none;
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
font-family: inherit;
|
||||||
|
color: #23272f;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
margin-bottom: 0;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea[readonly] {
|
@media (max-width: 900px) {
|
||||||
background-color: #e9ebee;
|
main {
|
||||||
}
|
padding: 24px 2vw;
|
||||||
|
}
|
||||||
button {
|
|
||||||
padding: 10px 20px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: #007bff;
|
|
||||||
color: white;
|
|
||||||
font-size: 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.text-areas {
|
main {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: auto auto auto auto auto;
|
||||||
|
}
|
||||||
|
.diamond-parent {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diamond-left {
|
||||||
|
grid-row: 2;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diamond-right {
|
||||||
|
grid-row: 3;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#merge-button {
|
||||||
|
grid-row: 4;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diamond-result {
|
||||||
|
grid-row: 5;
|
||||||
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 32px;
|
||||||
|
padding: 28px 0 18px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #5a6272;
|
||||||
|
font-size: 1rem;
|
||||||
|
box-shadow: 0 -4px 12px 0 rgba(28, 28, 87, 0.1),
|
||||||
|
0 -1px 2px 0 rgba(1, 1, 3, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-link > svg {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 36px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.github-link > svg:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
transform: translateY(-50%) scale(1.15);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue