Add slide number to presentation mode route

- 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 <noreply@anthropic.com>
This commit is contained in:
Michael Mainguy 2025-08-21 09:29:24 -05:00
parent 3864de28e7
commit 7c7c8235f3
4 changed files with 37 additions and 15 deletions

View File

@ -26,7 +26,7 @@ function App() {
<Route path="/presentations/new" element={<NewPresentationPage />} />
<Route path="/presentations/:presentationId/edit/slides/:slideNumber" element={<PresentationEditor />} />
<Route path="/presentations/:presentationId/view/slides/:slideNumber" element={<PresentationViewer />} />
<Route path="/presentations/:presentationId/present" element={<PresentationMode />} />
<Route path="/presentations/:presentationId/present/:slideNumber" element={<PresentationMode />} />
<Route path="/presentations/:presentationId/slide/:slideId/edit" element={<SlideEditor />} />
<Route path="/themes" element={<ThemeBrowser />} />
<Route path="/themes/:themeId" element={<ThemeDetailPage />} />

View File

@ -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<Presentation | null>(null);
const [theme, setTheme] = useState<Theme | null>(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<string | null>(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(() => {

View File

@ -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) {

View File

@ -213,7 +213,7 @@ export const PresentationsList: React.FC = () => {
<button
type="button"
className="card-action secondary"
onClick={() => navigate(`/presentations/${presentation.metadata.id}/present`)}
onClick={() => navigate(`/presentations/${presentation.metadata.id}/present/1`)}
>
Present
</button>