Refactor line number handling in App.tsx

This commit is contained in:
2026-02-01 03:49:19 +01:00
parent 10c6043e4e
commit ddba57d28b

View File

@@ -322,95 +322,17 @@ export default function App() {
const handleTextareaScroll = useCallback(() => {}, []); const handleTextareaScroll = useCallback(() => {}, []);
useEffect(() => { useEffect(() => {
if (!showLineNumbersActive) return;
const textarea = textareaRef.current; const textarea = textareaRef.current;
if (!textarea || typeof ResizeObserver === "undefined") return; if (!textarea) return;
const observer = new ResizeObserver(() => { if (showLineNumbersActive && !markdownPreview) {
setMeasureTick((tick) => tick + 1); appendLineNumbers(textarea);
}); } else {
observer.observe(textarea); removeLineNumbers(textarea);
return () => observer.disconnect(); }
}, [showLineNumbersActive]);
useEffect(() => {
if (!showLineNumbersActive) return;
let raf = 0;
const handleResize = () => {
if (raf) cancelAnimationFrame(raf);
raf = requestAnimationFrame(() => {
setMeasureTick((tick) => tick + 1);
});
};
window.addEventListener("resize", handleResize);
return () => { return () => {
window.removeEventListener("resize", handleResize); removeLineNumbers(textarea);
if (raf) cancelAnimationFrame(raf);
}; };
}, [showLineNumbersActive]); }, [markdownPreview, showLineNumbersActive]);
useLayoutEffect(() => {
if (!showLineNumbersActive) return;
const textarea = textareaRef.current;
if (!textarea) return;
const computed = window.getComputedStyle(textarea);
const lineHeight = parseFloat(computed.lineHeight);
if (Number.isFinite(lineHeight) && lineHeight > 0 && lineHeight !== defaultLineHeight) {
setDefaultLineHeight(lineHeight);
}
}, [defaultLineHeight, measureTick, showLineNumbersActive, sidebarCollapsed, historyOpen, textSize]);
const computeLineMetrics = useCallback(() => {
if (!showLineNumbersActive || !defaultLineHeight) return;
const textarea = textareaRef.current;
if (!textarea) return;
const styles = window.getComputedStyle(textarea);
const paddingLeft = parseFloat(styles.paddingLeft) || 0;
const paddingRight = parseFloat(styles.paddingRight) || 0;
const contentWidth = Math.max(1, textarea.clientWidth - paddingLeft - paddingRight);
let canvas = measureCanvasRef.current;
if (!canvas) {
canvas = document.createElement("canvas");
measureCanvasRef.current = canvas;
}
const context = canvas.getContext("2d");
if (!context) return;
context.font = styles.font;
const charWidth = context.measureText("M").width || 1;
const columns = Math.max(1, Math.floor(contentWidth / charWidth));
const heights = new Array(lineCount);
const tops = new Array(lineCount + 1);
tops[0] = 0;
for (let i = 0; i < lineCount; i += 1) {
const length = lines[i]?.length ?? 0;
const wraps = Math.max(1, Math.ceil(length / columns));
const height = wraps * defaultLineHeight;
heights[i] = height;
tops[i + 1] = tops[i] + height;
}
lineHeightsRef.current = heights;
lineTopsRef.current = tops;
setLineNumbersVersion((version) => version + 1);
updateVisibleRange();
if (lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = textarea.scrollTop;
}
}, [
defaultLineHeight,
lineCount,
lines,
showLineNumbersActive,
updateVisibleRange
]);
useEffect(() => {
computeLineMetrics();
}, [computeLineMetrics, measureTick, sidebarCollapsed, historyOpen, textSize]);
useEffect(() => {
if (showLineNumbersActive && textareaRef.current && lineNumbersRef.current) {
lineNumbersRef.current.scrollTop = textareaRef.current.scrollTop;
}
}, [showLineNumbersActive, body]);
const refreshTexts = useCallback(async () => { const refreshTexts = useCallback(async () => {