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 searchTimeoutRef = useRef<NodeJS.Timeout | 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.
@@ -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(
(textarea: HTMLTextAreaElement, cursorIndex: number): { top: number; left: number } => {
// Create a mirror div to measure text position
const mirror = document.createElement("div");
const styles = window.getComputedStyle(textarea);
// Lazily create the mirror element once, then reuse it
if (!mirrorRef.current) {
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 = [
"fontFamily",
"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.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);
mirror.textContent = textBeforeCursor;
// Create a span for the cursor position
const cursorSpan = document.createElement("span");
cursorSpan.textContent = "|";
mirror.appendChild(cursorSpan);
document.body.appendChild(mirror);
const textareaRect = textarea.getBoundingClientRect();
const cursorSpanRect = cursorSpan.getBoundingClientRect();
const top = cursorSpanRect.top - textareaRect.top + textarea.scrollTop + 20;
const left = cursorSpanRect.left - textareaRect.left + textarea.scrollLeft;
document.body.removeChild(mirror);
return { top, left };
},
[]
@@ -346,7 +358,8 @@ export function LinkAutocomplete({
}, [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(() => {
return (): void => {
@@ -356,6 +369,11 @@ export function LinkAutocomplete({
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
if (mirrorRef.current) {
document.body.removeChild(mirrorRef.current);
mirrorRef.current = null;
cursorSpanRef.current = null;
}
};
}, []);