Skip to content

Commit 297837f

Browse files
committed
fix(typing): prevent ghost caret by syncing decorations and hiding Monaco cursor
1 parent 81e2834 commit 297837f

File tree

2 files changed

+17
-4
lines changed

2 files changed

+17
-4
lines changed

src/pages/Typing.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,9 @@
163163
margin-bottom: 6px;
164164
opacity: 0.95;
165165
}
166+
167+
/* Monaco native cursor: we draw our own cursor indicator via decorations. */
168+
.monaco-editor .cursors-layer .cursor,
169+
.monaco-editor .cursors-layer .secondary-cursor {
170+
display: none !important;
171+
}

src/pages/Typing.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,11 +460,9 @@ export function Typing({ file, segments, settings, segmentIndex, onBack, onUpdat
460460

461461
const engine = engineRef.current
462462
setUi(snapshotFromEngine(engine, startAtRef.current))
463-
464-
applyDecorations()
465463
maybeComplete()
466464
})
467-
}, [applyDecorations, maybeComplete])
465+
}, [maybeComplete])
468466

469467
const resetEngine = useCallback(() => {
470468
completedRef.current = false
@@ -591,9 +589,11 @@ export function Typing({ file, segments, settings, segmentIndex, onBack, onUpdat
591589
for (let i = 0; i < text.length; i += 1) {
592590
handleKey(engineRef.current, text[i])
593591
}
592+
// Apply Monaco decorations synchronously to avoid a 1-frame stale cursor/afterContent "ghost".
593+
applyDecorations()
594594
scheduleCommit()
595595
perfRef.current.lastKeyHandlingMs = performance.now() - keyStart
596-
}, [scheduleCommit, segment])
596+
}, [applyDecorations, scheduleCommit, segment])
597597

598598
const handleInput = useCallback((event: React.FormEvent<HTMLTextAreaElement>) => {
599599
if (isComposingRef.current) return
@@ -670,6 +670,7 @@ export function Typing({ file, segments, settings, segmentIndex, onBack, onUpdat
670670
perfRef.current.pendingRenderLatency = true
671671
ensureStarted()
672672
handleBackspace(engineRef.current)
673+
applyDecorations()
673674
scheduleCommit()
674675
perfRef.current.lastKeyHandlingMs = performance.now() - keyStart
675676
return
@@ -682,6 +683,7 @@ export function Typing({ file, segments, settings, segmentIndex, onBack, onUpdat
682683
perfRef.current.pendingRenderLatency = true
683684
ensureStarted()
684685
handleKey(engineRef.current, '\n')
686+
applyDecorations()
685687
scheduleCommit()
686688
perfRef.current.lastKeyHandlingMs = performance.now() - keyStart
687689
return
@@ -695,6 +697,7 @@ export function Typing({ file, segments, settings, segmentIndex, onBack, onUpdat
695697
perfRef.current.pendingRenderLatency = true
696698
ensureStarted()
697699
for (let i = 0; i < settings.tabWidth; i += 1) handleKey(engineRef.current, ' ')
700+
applyDecorations()
698701
scheduleCommit()
699702
perfRef.current.lastKeyHandlingMs = performance.now() - keyStart
700703
return
@@ -716,6 +719,10 @@ export function Typing({ file, segments, settings, segmentIndex, onBack, onUpdat
716719
const editorOptions = useMemo<Monaco.editor.IStandaloneEditorConstructionOptions>(() => ({
717720
readOnly: true,
718721
domReadOnly: true,
722+
// We render our own cursor/position indicator via decorations/content widgets.
723+
// Hide Monaco's native cursor to avoid occasional focus-related "ghost" caret rendering.
724+
cursorWidth: 0,
725+
hideCursorInOverviewRuler: true,
719726
minimap: { enabled: false },
720727
scrollBeyondLastLine: false,
721728
wordWrap: 'off',

0 commit comments

Comments
 (0)