From c0f8d486a4f5b090c74a7336647f87264f071e5a Mon Sep 17 00:00:00 2001 From: Etherll Date: Thu, 28 May 2026 10:36:20 +0300 Subject: [PATCH] Studio: fix RAG PDF main page rendering as a thin white strip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The thumbnail-rail refactor hoisted to wrap both the rail and the main page so the PDF loads once. That moved the width-measuring scroll container INSIDE , which only renders its children after the PDF finishes loading. The old `useEffect(..., [])` ran on component mount — when the container was still absent — so the ResizeObserver never attached, `width` stayed null, and the main collapsed to width 0. Replace the mount-effect measurement with a callback ref: the ResizeObserver now attaches the instant the container node mounts, regardless of when that happens relative to PDF load. Disconnects cleanly on unmount / re-attach. Verified: tsc clean, vite build succeeds, preview-pdf-smoke (incl. the resize/debounce case) passes. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../rag/components/preview-pdf-view.tsx | 69 ++++++++++++------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/studio/frontend/src/features/rag/components/preview-pdf-view.tsx b/studio/frontend/src/features/rag/components/preview-pdf-view.tsx index 7786792d26..fa2d1b5d39 100644 --- a/studio/frontend/src/features/rag/components/preview-pdf-view.tsx +++ b/studio/frontend/src/features/rag/components/preview-pdf-view.tsx @@ -252,6 +252,8 @@ export const PreviewPdfView: FC = ({ target, file }) => { const [searchTerm, setSearchTerm] = useState(""); const [copied, setCopied] = useState(false); const containerRef = useRef(null); + const observerRef = useRef(null); + const resizeTimeoutRef = useRef(null); const lastMeasuredWidthRef = useRef(null); const lastResetKeyRef = useRef(null); const [width, setWidth] = useState(null); @@ -280,35 +282,54 @@ export const PreviewPdfView: FC = ({ target, file }) => { setCopied(false); }, [resetKey, target.targetPage]); - useEffect(() => { + const measureWidth = useCallback(() => { const el = containerRef.current; if (!el) { return; } - let timeoutId: number | null = null; - const updateWidth = () => { - const next = Math.max(MIN_PDF_WIDTH, el.clientWidth - PDF_BODY_GUTTER_PX); - if (lastMeasuredWidthRef.current === next) { + const next = Math.max(MIN_PDF_WIDTH, el.clientWidth - PDF_BODY_GUTTER_PX); + if (lastMeasuredWidthRef.current === next) { + return; + } + lastMeasuredWidthRef.current = next; + setWidth(next); + }, []); + + // Callback ref instead of useRef + mount effect: the scroll container + // lives INSIDE , so it only enters the DOM after the PDF + // loads. Attaching the ResizeObserver the instant the node mounts + // (rather than on the component's mount effect, when the node is still + // absent) is what keeps the main page from rendering at width 0 — the + // thin white strip regression. + const attachContainer = useCallback( + (node: HTMLDivElement | null) => { + if (observerRef.current) { + observerRef.current.disconnect(); + observerRef.current = null; + } + if (resizeTimeoutRef.current !== null) { + window.clearTimeout(resizeTimeoutRef.current); + resizeTimeoutRef.current = null; + } + containerRef.current = node; + if (!node) { return; } - lastMeasuredWidthRef.current = next; - setWidth(next); - }; - updateWidth(); - const observer = new ResizeObserver(() => { - if (timeoutId !== null) { - window.clearTimeout(timeoutId); - } - timeoutId = window.setTimeout(updateWidth, RESIZE_DEBOUNCE_MS); - }); - observer.observe(el); - return () => { - observer.disconnect(); - if (timeoutId !== null) { - window.clearTimeout(timeoutId); - } - }; - }, []); + measureWidth(); + const observer = new ResizeObserver(() => { + if (resizeTimeoutRef.current !== null) { + window.clearTimeout(resizeTimeoutRef.current); + } + resizeTimeoutRef.current = window.setTimeout( + measureWidth, + RESIZE_DEBOUNCE_MS, + ); + }); + observer.observe(node); + observerRef.current = observer; + }, + [measureWidth], + ); useEffect(() => { if (!copied) { @@ -531,7 +552,7 @@ export const PreviewPdfView: FC = ({ target, file }) => { ))}