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 ? (