From 7c7c8235f361d57aa63a4b49d520cdbc203f6bfb Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 21 Aug 2025 09:29:24 -0500 Subject: [PATCH] Add slide number to presentation mode route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update route to /presentations/:presentationId/present/:slideNumber - Add URL-synced navigation with goToSlide function - Update keyboard navigation to maintain URL state - Enable deep linking and browser navigation for slides - Start presentation from slide 1 in presentations list - Present button in viewer uses current slide number 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/App.tsx | 2 +- .../presentations/PresentationMode.tsx | 46 ++++++++++++++----- .../presentations/PresentationViewer.tsx | 2 +- .../presentations/PresentationsList.tsx | 2 +- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 893164a..4d9b705 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,7 +26,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/src/components/presentations/PresentationMode.tsx b/src/components/presentations/PresentationMode.tsx index df425f2..0fcaf2b 100644 --- a/src/components/presentations/PresentationMode.tsx +++ b/src/components/presentations/PresentationMode.tsx @@ -10,15 +10,29 @@ import { loggers } from '../../utils/logger.ts'; import './PresentationMode.css'; export const PresentationMode: React.FC = () => { - const { presentationId } = useParams<{ presentationId: string }>(); + const { presentationId, slideNumber } = useParams<{ + presentationId: string; + slideNumber: string; + }>(); const navigate = useNavigate(); const [presentation, setPresentation] = useState(null); const [theme, setTheme] = useState(null); - const [currentSlideIndex, setCurrentSlideIndex] = useState(0); + const [currentSlideIndex, setCurrentSlideIndex] = useState( + slideNumber ? parseInt(slideNumber, 10) - 1 : 0 + ); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Navigate to specific slide and update URL + const goToSlide = (slideIndex: number) => { + if (!presentation) return; + + const clampedIndex = Math.max(0, Math.min(slideIndex, presentation.slides.length - 1)); + setCurrentSlideIndex(clampedIndex); + navigate(`/presentations/${presentationId}/present/${clampedIndex + 1}`, { replace: true }); + }; + // Keyboard navigation handler const handleKeyPress = useCallback((event: KeyboardEvent) => { if (!presentation || presentation.slides.length === 0) return; @@ -28,24 +42,22 @@ export const PresentationMode: React.FC = () => { case 'Space': case 'Enter': event.preventDefault(); - setCurrentSlideIndex(prev => - Math.min(prev + 1, presentation.slides.length - 1) - ); + goToSlide(currentSlideIndex + 1); break; case 'ArrowLeft': event.preventDefault(); - setCurrentSlideIndex(prev => Math.max(prev - 1, 0)); + goToSlide(currentSlideIndex - 1); break; case 'Home': event.preventDefault(); - setCurrentSlideIndex(0); + goToSlide(0); break; case 'End': event.preventDefault(); - setCurrentSlideIndex(presentation.slides.length - 1); + goToSlide(presentation.slides.length - 1); break; case 'Escape': @@ -55,14 +67,24 @@ export const PresentationMode: React.FC = () => { default: // Handle number keys for direct slide navigation - const slideNumber = parseInt(event.key); - if (slideNumber >= 1 && slideNumber <= presentation.slides.length) { + const slideNum = parseInt(event.key); + if (slideNum >= 1 && slideNum <= presentation.slides.length) { event.preventDefault(); - setCurrentSlideIndex(slideNumber - 1); + goToSlide(slideNum - 1); } break; } - }, [presentation, navigate, presentationId]); + }, [presentation, currentSlideIndex, goToSlide]); + + // Sync current slide index with URL parameter + useEffect(() => { + if (slideNumber) { + const newIndex = parseInt(slideNumber, 10) - 1; + if (newIndex >= 0 && newIndex !== currentSlideIndex) { + setCurrentSlideIndex(newIndex); + } + } + }, [slideNumber]); // Set up keyboard listeners useEffect(() => { diff --git a/src/components/presentations/PresentationViewer.tsx b/src/components/presentations/PresentationViewer.tsx index 25b9c27..49d335d 100644 --- a/src/components/presentations/PresentationViewer.tsx +++ b/src/components/presentations/PresentationViewer.tsx @@ -86,7 +86,7 @@ export const PresentationViewer: React.FC = () => { const enterPresentationMode = () => { if (!presentation) return; - navigate(`/presentations/${presentationId}/present`); + navigate(`/presentations/${presentationId}/present/${currentSlideIndex + 1}`); }; if (loading) { diff --git a/src/components/presentations/PresentationsList.tsx b/src/components/presentations/PresentationsList.tsx index 5e47cb6..ac8651b 100644 --- a/src/components/presentations/PresentationsList.tsx +++ b/src/components/presentations/PresentationsList.tsx @@ -213,7 +213,7 @@ export const PresentationsList: React.FC = () => {