Update example website

This commit is contained in:
Andras Schmelczer 2025-06-15 14:46:51 +01:00
parent 3e87a68dfd
commit 4851e85986
No known key found for this signature in database
GPG key ID: FC8F2C3D3D1A718C
3 changed files with 245 additions and 225 deletions

View file

@ -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>

View 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();

View file

@ -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);
}