diff --git a/examples/website/src/index.html b/examples/website/src/index.html index 5136fc7..ce656d2 100644 --- a/examples/website/src/index.html +++ b/examples/website/src/index.html @@ -28,7 +28,7 @@
-

Reconcile-text: conflict-free 3-way text merging

+

reconcile-text: conflict-free 3-way text merging

Think ; -const sampleText = `The \`reconcile\` Rust library is embedded on this page as a WASM module and powers these text boxes. Experiment with changing the "Original", "First concurrent edit", and "Second concurrent edit" text boxes to see competing changes get merged in real-time within the "Deconflicted result" box. Here, you will see color-coded tokens marking the origin of each token, including ones that got deleted. The result highly depends on the tokenization strategy, for example, deciding how casing or whitespace is taken into account.`; +const sampleText = `The \`reconcile\` Rust library is embedded on this page as a WASM module and powers these text boxes. Experiment with changing the "Original", "First concurrent edit", and "Second concurrent edit" text boxes to see competing changes get merged in real-time within the "Deconflicted result" box. Here, you will see color-coded tokens marking the origin of each token, including ones that got deleted. The result highly depends on the tokenisation strategy, for example, deciding how casing or whitespace is taken into account.`; async function main(): Promise { originalTextArea.addEventListener('input', updateMergedText); @@ -76,11 +76,11 @@ function updateMergedText(): void { } const selectionSide = leftCursors ? 'left' : 'right'; - mergedTextArea.innerHTML = ''; + const fragment = document.createDocumentFragment(); let currentPosition = 0; if (selectionEnd === 0) { - mergedTextArea.appendChild(createCaret(selectionSide === 'left')); + fragment.appendChild(createCaret(selectionSide === 'left')); } for (const { text, history } of results.history) { @@ -93,10 +93,10 @@ function updateMergedText(): void { span.className += ` selection-${selectionSide}`; } - mergedTextArea.appendChild(span); + fragment.appendChild(span); - if (currentPosition == selectionEnd - 1) { - mergedTextArea.appendChild(createCaret(selectionSide === 'left')); + if (currentPosition === selectionEnd - 1) { + fragment.appendChild(createCaret(selectionSide === 'left')); } if (history !== 'RemovedFromLeft' && history !== 'RemovedFromRight') { @@ -105,6 +105,9 @@ function updateMergedText(): void { } } } + + mergedTextArea.innerHTML = ''; + mergedTextArea.appendChild(fragment); } function getCursorsFromActiveTextArea() { @@ -167,8 +170,8 @@ function autoResize(textarea: HTMLTextAreaElement): void { function focusTextArea(textarea: HTMLTextAreaElement): void { textarea.focus(); - textarea.selectionStart = textarea.value.length; - textarea.selectionEnd = textarea.value.length; + textarea.selectionStart = 0; + textarea.selectionEnd = 0; } main(); diff --git a/examples/website/src/style.scss b/examples/website/src/style.scss index a60438f..e39429f 100644 --- a/examples/website/src/style.scss +++ b/examples/website/src/style.scss @@ -1,3 +1,5 @@ +@use 'sass:color'; + // Colour palette $primary-blue: #2451a6; $light-blue: #85bff7; @@ -16,6 +18,10 @@ $gradient-end: #e0e7ef; @return rgba($colour, $opacity); } +@function caret-colour($colour, $amount: 20%) { + @return color.adjust($colour, $lightness: -$amount); +} + * { box-sizing: border-box; margin: 0; @@ -36,6 +42,7 @@ body { height: 100vh; height: 100dvh; overflow-y: auto; + overflow-x: hidden; } .background { @@ -274,9 +281,13 @@ textarea { #merged { width: 100%; user-select: text; + display: flex; + flex-wrap: wrap; > * { position: relative; + display: block; + white-space: pre-wrap; } } @@ -328,13 +339,14 @@ $DOT_RADIUS: 4; .selection-caret { position: relative; + z-index: 1000; &.selection-caret-left { - background: $green; + background: caret-colour($green); } &.selection-caret-right { - background: $light-blue; + background: caret-colour($light-blue); } > * { @@ -359,13 +371,24 @@ $DOT_RADIUS: 4; height: #{$DOT_RADIUS * 2}px; top: -#{$DOT_RADIUS}px; left: -#{$DOT_RADIUS}px; - transition: transform 0.3s ease-in-out; + transition: opacity 0.3s ease-in-out; transform-origin: bottom center; box-sizing: border-box; + + &::before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 30px; + height: 30px; + border-radius: 50%; + } } &:hover > .dot { - transform: scale(0); + opacity: 0; } > .info { diff --git a/examples/website/tsconfig.json b/examples/website/tsconfig.json index 6cf10b0..fb72539 100644 --- a/examples/website/tsconfig.json +++ b/examples/website/tsconfig.json @@ -11,7 +11,5 @@ "skipLibCheck": true, "inlineSourceMap": true }, - "exclude": [ - "./dist" - ] -} \ No newline at end of file + "exclude": ["./dist"] +} diff --git a/reconcile-js/package.json b/reconcile-js/package.json index 0236597..dfa1f7c 100644 --- a/reconcile-js/package.json +++ b/reconcile-js/package.json @@ -48,4 +48,4 @@ "webpack-cli": "^6.0.1", "webpack-merge": "^6.0.1" } -} \ No newline at end of file +}