diff --git a/packages/carta-md/src/lib/internal/components/Input.svelte b/packages/carta-md/src/lib/internal/components/Input.svelte index 76bd48fb..c48dc2e4 100644 --- a/packages/carta-md/src/lib/internal/components/Input.svelte +++ b/packages/carta-md/src/lib/internal/components/Input.svelte @@ -112,21 +112,13 @@ }, 300); const onValueChange = (value: string) => { - const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); - - if (isMobile) { - // On mobile, highlight immediately as using the debounced version - // causes issues with the keyboard and focus. - highlight(value); - } else { - if (highlightElem) { - speculativeHighlightUpdate(highlightElem, prevValue, value); - requestAnimationFrame(resize); - } - - debouncedHighlight(value); + if (highlightElem) { + speculativeHighlightUpdate(highlightElem, prevValue, value); + requestAnimationFrame(resize); } + debouncedHighlight(value); + highlightNestedLanguages(value); }; diff --git a/packages/carta-md/src/lib/internal/speculative.ts b/packages/carta-md/src/lib/internal/speculative.ts index 251303d4..67f3097d 100644 --- a/packages/carta-md/src/lib/internal/speculative.ts +++ b/packages/carta-md/src/lib/internal/speculative.ts @@ -44,7 +44,8 @@ export function speculativeHighlightUpdate(container: HTMLDivElement, from: stri advance(change.value.length); }; const addedCallback = (change: Change) => { - const node = textNodes[currentNodeIdx] ?? createTemporaryNode(container); + const node = textNodes.at(currentNodeIdx) ?? createTemporaryNode(container); + if (!node) return; // Could not create a temporary node const text = node.textContent ?? ''; const pre = text.slice(0, currentNodeCharIdx); @@ -54,40 +55,26 @@ export function speculativeHighlightUpdate(container: HTMLDivElement, from: stri advance(change.value.length); }; const removedCallback = (change: Change) => { - // Use the Selection object to delete removed text - const startNodeIdx = currentNodeIdx; - const startNodeCharIdx = currentNodeCharIdx; - advance(change.value.length); - const endNodeIdx = currentNodeIdx; - let endNodeCharIdx = currentNodeCharIdx; - - const startNode = textNodes[startNodeIdx]; - let endNode = textNodes[endNodeIdx]; - - if (!endNode) { - // Remove everything - endNode = textNodes.at(-1)!; - endNodeCharIdx = endNode.textContent?.length ?? 0; - } - - const range = new Range(); - range.setStart(startNode, startNodeCharIdx); - range.setEnd(endNode, endNodeCharIdx); + let charsToRemove = change.value.length; - const selection = window.getSelection(); - if (!selection) return; + while (charsToRemove > 0) { + const node = textNodes.at(currentNodeIdx); + if (!node) return; // Could not find a node - const initialRanges = new Array(selection.rangeCount).map((_, i) => selection.getRangeAt(i)); - selection.removeAllRanges(); + const text = node.textContent ?? ''; + const availableNodeChars = text.length - currentNodeCharIdx; - selection.addRange(range); - selection.deleteFromDocument(); + const stepsTaken = Math.min(availableNodeChars, charsToRemove); - initialRanges.forEach(selection.addRange); + node.textContent = + text.slice(0, currentNodeCharIdx) + text.slice(currentNodeCharIdx + stepsTaken); - // Go back to start - currentNodeIdx = startNodeIdx; - currentNodeCharIdx = startNodeCharIdx; + charsToRemove -= stepsTaken; + if (stepsTaken == availableNodeChars) { + currentNodeIdx++; + currentNodeCharIdx = 0; + } + } }; for (const change of diff) {