From 998a3a9f1cf64b3716ec3e06d4ff8e1b5a713244 Mon Sep 17 00:00:00 2001 From: im47cn <67424112+im47cn@users.noreply.github.com> Date: Wed, 29 Jul 2026 14:39:41 +0800 Subject: [PATCH] fix(lint): resolve all 19 ESLint errors across 9 files Fixes: - react-hooks/exhaustive-deps: wrap derived values in useMemo - react-hooks/set-state-in-effect: derive during render or suppress with justification for legitimate sync patterns - @next/next/no-img-element: suppress for office-render thumbnails - prefer-const / no-useless-assignment: clean up mutable bindings - preserve-caught-error: attach cause when rethrowing - no-useless-escape: remove unnecessary backslashes in regex - no-undef: use globalThis.URL for Node.js ESM globals - @typescript-eslint/no-unused-vars: prefix unused declarations Co-authored-by: Crush (GLM 5.2) --- .../src/presentation/cursor-canvas-runtime.tsx | 15 ++++++++++++--- .../src/presentation/presentation-preview.tsx | 15 ++++++--------- .../src/shared/office-color-utils.ts | 14 +++++--------- packages/office/src/cursor-canvas.ts | 1 + scripts/docs/feature-tree-generator.ts | 6 +++--- scripts/docs/framework-feature-tree-generator.ts | 2 +- scripts/harness/analyze-search-tool-usage.ts | 4 ++-- scripts/mcp-http-proxy.mjs | 4 ++-- tools/hook-runtime/src/specialist-review.ts | 2 +- 9 files changed, 33 insertions(+), 30 deletions(-) diff --git a/packages/office-render/src/presentation/cursor-canvas-runtime.tsx b/packages/office-render/src/presentation/cursor-canvas-runtime.tsx index f65b6bdb4..ecf49826b 100644 --- a/packages/office-render/src/presentation/cursor-canvas-runtime.tsx +++ b/packages/office-render/src/presentation/cursor-canvas-runtime.tsx @@ -64,7 +64,10 @@ export function PresentationCursorCanvas({ 0, slides.findIndex((slide) => asNumber(slide.index, 1) === selectedIndex), ); - const selectedSlide = slides[selectedPosition] ?? slides[0] ?? {}; + const selectedSlide = useMemo( + () => slides[selectedPosition] ?? slides[0] ?? {}, + [slides, selectedPosition], + ); const selectedSlideIndex = asNumber(selectedSlide.index, selectedPosition + 1); const title = asString(artifact.title) || "Presentation"; @@ -77,7 +80,11 @@ export function PresentationCursorCanvas({ if (slides.some((slide) => asNumber(slide.index, 1) === selectedIndex)) { return; } - setSelectedIndex(asNumber(slides[0]?.index, 1)); + const firstIndex = asNumber(slides[0]?.index, 1); + if (firstIndex !== selectedIndex) { + // eslint-disable-next-line react-hooks/set-state-in-effect -- sync when slide set changes and current index is stale + setSelectedIndex(firstIndex); + } }, [selectedIndex, slides]); const goPrevious = useCallback(() => { @@ -151,6 +158,7 @@ export function PresentationCursorCanvas({ > {slideIndex} {thumbnail ? ( + // eslint-disable-next-line @next/next/no-img-element -- office thumbnail, not a Next.js route image (); const entries = Object.entries(media); if (entries.length === 0) { - setImages(next); + // eslint-disable-next-line react-hooks/set-state-in-effect -- sync when media set is empty + if (!cancelled) setImages(next); return; } diff --git a/packages/office-render/src/presentation/presentation-preview.tsx b/packages/office-render/src/presentation/presentation-preview.tsx index 6bbe8304d..82d631fbb 100644 --- a/packages/office-render/src/presentation/presentation-preview.tsx +++ b/packages/office-render/src/presentation/presentation-preview.tsx @@ -385,7 +385,7 @@ function SlideshowOverlay({ const frameSize = useElementSize(frameRef); const didEnterFullscreenRef = useRef(typeof document !== "undefined" && document.fullscreenElement != null); const selectedIndex = Math.min(activeSlideIndex, Math.max(0, slides.length - 1)); - const slide = slides[selectedIndex] ?? {}; + const slide = useMemo(() => slides[selectedIndex] ?? {}, [slides, selectedIndex]); const frame = getSlideFrameSize(slide, layouts); const fit = computePresentationFit(frameSize, frame, { padding: 0 }); const canvasWidth = Math.max(1, fit.width); @@ -396,11 +396,8 @@ function SlideshowOverlay({ const showSpeakerNotesLabel = labels.showSpeakerNotes ?? speakerNotesLabel; const hideSpeakerNotesLabel = labels.hideSpeakerNotes ?? speakerNotesLabel; - useEffect(() => { - if (!hasSpeakerNotes) { - setShowSpeakerNotes(false); - } - }, [hasSpeakerNotes]); + // When there are no speaker notes, the panel cannot be shown. + const canShowSpeakerNotes = hasSpeakerNotes && showSpeakerNotes; const goPrevious = useCallback(() => { setActiveSlideIndex(Math.max(0, selectedIndex - 1)); @@ -508,7 +505,7 @@ function SlideshowOverlay({ {hasSpeakerNotes ? ( -
+
- {showSpeakerNotes && hasSpeakerNotes ? ( + {canShowSpeakerNotes ? (