chore: upgrade Node.js runtime to v24 across codebase #419

Merged
jason.woltje merged 438 commits from fix/auth-frontend-remediation into main 2026-02-17 01:04:47 +00:00
Showing only changes of commit 952eeb7323 - Show all commits

View File

@@ -53,6 +53,8 @@ export function LinkAutocomplete({
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null); const searchTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const abortControllerRef = useRef<AbortController | null>(null); const abortControllerRef = useRef<AbortController | null>(null);
const mirrorRef = useRef<HTMLDivElement | null>(null);
const cursorSpanRef = useRef<HTMLSpanElement | null>(null);
/** /**
* Search for knowledge entries matching the query. * Search for knowledge entries matching the query.
@@ -132,15 +134,37 @@ export function LinkAutocomplete({
); );
/** /**
* Calculate dropdown position relative to cursor * Calculate dropdown position relative to cursor.
* Uses a persistent off-screen mirror element (via refs) to avoid
* creating and removing DOM nodes on every keystroke, which causes
* layout thrashing.
*/ */
const calculateDropdownPosition = useCallback( const calculateDropdownPosition = useCallback(
(textarea: HTMLTextAreaElement, cursorIndex: number): { top: number; left: number } => { (textarea: HTMLTextAreaElement, cursorIndex: number): { top: number; left: number } => {
// Create a mirror div to measure text position // Lazily create the mirror element once, then reuse it
const mirror = document.createElement("div"); if (!mirrorRef.current) {
const styles = window.getComputedStyle(textarea); const mirror = document.createElement("div");
mirror.style.position = "absolute";
mirror.style.visibility = "hidden";
mirror.style.height = "auto";
mirror.style.whiteSpace = "pre-wrap";
mirror.style.pointerEvents = "none";
document.body.appendChild(mirror);
mirrorRef.current = mirror;
// Copy relevant styles const span = document.createElement("span");
span.textContent = "|";
cursorSpanRef.current = span;
}
const mirror = mirrorRef.current;
const cursorSpan = cursorSpanRef.current;
if (!cursorSpan) {
return { top: 0, left: 0 };
}
// Sync styles from the textarea so measurement is accurate
const styles = window.getComputedStyle(textarea);
const stylesToCopy = [ const stylesToCopy = [
"fontFamily", "fontFamily",
"fontSize", "fontSize",
@@ -161,31 +185,19 @@ export function LinkAutocomplete({
} }
}); });
mirror.style.position = "absolute";
mirror.style.visibility = "hidden";
mirror.style.width = `${String(textarea.clientWidth)}px`; mirror.style.width = `${String(textarea.clientWidth)}px`;
mirror.style.height = "auto";
mirror.style.whiteSpace = "pre-wrap";
// Get text up to cursor // Update content: text before cursor + cursor marker span
const textBeforeCursor = textarea.value.substring(0, cursorIndex); const textBeforeCursor = textarea.value.substring(0, cursorIndex);
mirror.textContent = textBeforeCursor; mirror.textContent = textBeforeCursor;
// Create a span for the cursor position
const cursorSpan = document.createElement("span");
cursorSpan.textContent = "|";
mirror.appendChild(cursorSpan); mirror.appendChild(cursorSpan);
document.body.appendChild(mirror);
const textareaRect = textarea.getBoundingClientRect(); const textareaRect = textarea.getBoundingClientRect();
const cursorSpanRect = cursorSpan.getBoundingClientRect(); const cursorSpanRect = cursorSpan.getBoundingClientRect();
const top = cursorSpanRect.top - textareaRect.top + textarea.scrollTop + 20; const top = cursorSpanRect.top - textareaRect.top + textarea.scrollTop + 20;
const left = cursorSpanRect.left - textareaRect.left + textarea.scrollLeft; const left = cursorSpanRect.left - textareaRect.left + textarea.scrollLeft;
document.body.removeChild(mirror);
return { top, left }; return { top, left };
}, },
[] []
@@ -346,7 +358,8 @@ export function LinkAutocomplete({
}, [textareaRef, handleInput, handleKeyDown]); }, [textareaRef, handleInput, handleKeyDown]);
/** /**
* Cleanup timeout and abort in-flight requests on unmount * Cleanup timeout, abort in-flight requests, and remove the
* persistent mirror element on unmount
*/ */
useEffect(() => { useEffect(() => {
return (): void => { return (): void => {
@@ -356,6 +369,11 @@ export function LinkAutocomplete({
if (abortControllerRef.current) { if (abortControllerRef.current) {
abortControllerRef.current.abort(); abortControllerRef.current.abort();
} }
if (mirrorRef.current) {
document.body.removeChild(mirrorRef.current);
mirrorRef.current = null;
cursorSpanRef.current = null;
}
}; };
}, []); }, []);