This commit is contained in:
Andras Schmelczer 2026-10-29 08:13:37 +00:00
commit a050ec252b
2 changed files with 58 additions and 65 deletions

View file

@ -99,65 +99,72 @@ function updateMergedText(): void {
selectedTokenizer
);
let selectionStart: number = Number.NEGATIVE_INFINITY;
let selectionEnd: number = Number.NEGATIVE_INFINITY;
if ((results.cursors?.length ?? 0) > 0) {
selectionStart = results.cursors![0].position;
selectionEnd = results.cursors![1].position;
}
const isSelection = selectionStart !== selectionEnd;
const selection =
results.cursors?.length === 2
? {
start: results.cursors[0].position,
end: results.cursors[1].position,
side: leftCursors ? 'left' : 'right',
}
: undefined;
const selectionSide = leftCursors ? 'left' : 'right';
const fragment = document.createDocumentFragment();
function appendSpan(text: string, className: string): void {
if (text.length === 0) {
return;
}
const span = document.createElement('span');
span.className = className;
span.textContent = text;
fragment.appendChild(span);
}
function appendCaret(): void {
if (!selection) {
return;
}
fragment.appendChild(
createSelectionOverlay(
selection.side === 'left',
selection.start !== selection.end
)
);
}
let currentPosition = 0;
if (selectionEnd === 0) {
fragment.appendChild(createSelectionOverlay(selectionSide === 'left', isSelection));
if (selection?.end === 0) {
appendCaret();
}
for (const { text, history } of results.history) {
const isDelete = history === 'RemovedFromLeft' || history === 'RemovedFromRight';
let spanChars: string[] = [];
let currentClass = '';
const flushSpan = () => {
if (spanChars.length > 0) {
const span = document.createElement('span');
span.className = currentClass;
span.textContent = spanChars.join('');
fragment.appendChild(span);
spanChars = [];
}
};
for (const character of text) {
let className = history;
if (
!isDelete &&
selectionStart <= currentPosition &&
currentPosition < selectionEnd
) {
className += ` selection-${selectionSide}`;
}
if (className !== currentClass) {
flushSpan();
currentClass = className;
}
spanChars.push(character);
if (!isDelete) {
if (currentPosition === selectionEnd - 1) {
flushSpan();
fragment.appendChild(
createSelectionOverlay(selectionSide === 'left', isSelection)
);
}
currentPosition++;
}
if (isDelete || !selection) {
appendSpan(text, history);
continue;
}
flushSpan();
// Reconcile positions count Unicode scalars, rather than UTF-16 code units.
const chars = [...text];
const start = Math.max(0, Math.min(chars.length, selection.start - currentPosition));
const end = Math.max(0, Math.min(chars.length, selection.end - currentPosition));
appendSpan(chars.slice(0, start).join(''), history);
appendSpan(
chars.slice(start, end).join(''),
`${history} selection-${selection.side}`
);
if (
currentPosition < selection.end &&
selection.end <= currentPosition + chars.length
) {
appendCaret();
}
appendSpan(chars.slice(end).join(''), history);
currentPosition += chars.length;
}
mergedTextArea.innerHTML = '';

View file

@ -25,7 +25,6 @@ $gradient-end: #e0e7ef;
* {
box-sizing: border-box;
margin: 0;
user-select: none;
}
html,
@ -76,13 +75,6 @@ header > h1 {
text-align: center;
}
h1,
code,
p,
p * {
user-select: text;
}
code {
background: $code-bg;
color: $code-text;
@ -121,6 +113,7 @@ main {
}
.radio-group {
user-select: none;
display: flex;
gap: 16px;
justify-content: center;
@ -265,6 +258,7 @@ main {
}
label {
user-select: none;
display: inline-block;
margin-bottom: 8px;
font-weight: 600;
@ -309,7 +303,6 @@ textarea {
box-sizing: border-box;
resize: none;
cursor: text;
user-select: text;
caret-color: $primary-blue;
outline: 2px solid transparent;
outline-offset: 2px;
@ -334,7 +327,6 @@ textarea:focus {
#merged {
width: 100%;
user-select: text;
> * {
position: relative;
@ -349,14 +341,9 @@ textarea:focus {
}
}
.Unchanged {
user-select: text;
}
.Left,
.AddedFromLeft,
.RemovedFromLeft {
user-select: text;
background: $green;
}
@ -381,7 +368,6 @@ textarea:focus {
.Right,
.AddedFromRight,
.RemovedFromRight {
user-select: text;
background: $light-blue;
}
@ -396,6 +382,7 @@ $CARET_WIDTH: 2;
$DOT_RADIUS: 4;
.selection-caret {
user-select: none;
position: relative;
z-index: 1000;
@ -453,7 +440,6 @@ $DOT_RADIUS: 4;
top: -1.3em;
left: calc(-#{$CARET_WIDTH} / 2 * 1px);
font-size: 0.9em;
user-select: none;
color: white;
padding: 0 2px;
transition: opacity 0.3s ease-in-out;