From 7c5218dba268fbc3e66900c5d847371f23e70636 Mon Sep 17 00:00:00 2001 From: CaffeineFueled Date: Thu, 12 Mar 2026 20:43:02 +0100 Subject: [PATCH] ux: FIX cursor updates to prevent cursor from jumping around while working together #21 --- app.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index a52638f..0d84bb4 100644 --- a/app.py +++ b/app.py @@ -399,9 +399,10 @@ function connect(){ } } else if (msg.type === "update" && msg.ver > ver && msg.clientId !== clientId) { const {selectionStart:s, selectionEnd:e} = ta; + const oldText = ta.value; ta.value = msg.text; ver = msg.ver; updateGutter(); - ta.selectionStart = Math.min(s, ta.value.length); - ta.selectionEnd = Math.min(e, ta.value.length); + ta.selectionStart = adjustCursor(oldText, msg.text, s); + ta.selectionEnd = adjustCursor(oldText, msg.text, e); } else if (msg.type === "peers_changed") { const el = $("#peers"); el.textContent = msg.count; @@ -439,6 +440,25 @@ async function copyToClipboard() { } } +// Adjust cursor position after a remote text update. +// Finds the single changed region (common prefix + suffix), +// then shifts the cursor accordingly: +// - change is after cursor → no movement +// - change is before cursor → shift by length delta +// - cursor was inside the changed region → place at end of new content +function adjustCursor(oldText, newText, pos) { + let start = 0; + const minLen = Math.min(oldText.length, newText.length); + while (start < minLen && oldText[start] === newText[start]) start++; + if (pos <= start) return pos; // change is entirely after cursor + let oldEnd = oldText.length, newEnd = newText.length; + while (oldEnd > start && newEnd > start && oldText[oldEnd - 1] === newText[newEnd - 1]) { + oldEnd--; newEnd--; + } + if (pos >= oldEnd) return pos + (newEnd - oldEnd); // change is before cursor + return newEnd; // cursor was inside changed region +} + connect(); // Handle Tab key to insert 4 spaces instead of navigation