diff --git a/frontend/src/components/Editor.jsx b/frontend/src/components/Editor.jsx index 27b8042..97e1cfb 100644 --- a/frontend/src/components/Editor.jsx +++ b/frontend/src/components/Editor.jsx @@ -110,7 +110,9 @@ const Editor = forwardRef(function Editor( const [lintStatus, setLintStatus] = useState('idle') // idle | checking | done | stale const [lintCount, setLintCount] = useState(0) const [lintPopover, setLintPopover] = useState(null) // { top, left, message, replacements, from, to } - const lintStatusRef = useRef('idle') + const lintStatusRef = useRef('idle') + const lintDebounce = useRef(null) + const runLintRef = useRef(null) // always holds latest runLint, safe to call from onUpdate closure const editor = useEditor({ extensions: [ @@ -126,11 +128,9 @@ const Editor = forwardRef(function Editor( content: '', onUpdate({ editor }) { onChange(stripLintMarks(editor.getJSON())) - // Mark results as stale when the user edits so they know to re-check - if (lintStatusRef.current === 'done') { - lintStatusRef.current = 'stale' - setLintStatus('stale') - } + // Debounced auto-check: fire 1.5 s after the last keystroke + clearTimeout(lintDebounce.current) + lintDebounce.current = setTimeout(() => runLintRef.current?.(), 1500) }, }) @@ -153,6 +153,11 @@ const Editor = forwardRef(function Editor( }, [editor, content]) // ── Lint check ─────────────────────────────────────────────────────────── + // Keep ref in sync so the onUpdate closure (which never re-captures) always + // calls the latest version of runLint. + useEffect(() => { runLintRef.current = runLint }, [runLint]) + useEffect(() => () => clearTimeout(lintDebounce.current), []) + const runLint = useCallback(async () => { if (!editor || lintStatusRef.current === 'checking') return lintStatusRef.current = 'checking' @@ -265,7 +270,7 @@ const Editor = forwardRef(function Editor( lintCount={lintCount} />
- +
{wordCount.toLocaleString()} {wordCount === 1 ? 'word' : 'words'}