feat: preserve slide overview position via url hash#2476
Conversation
❌ Deploy Preview for slidev failed.
|
@slidev/client
create-slidev
create-slidev-theme
@slidev/parser
@slidev/cli
@slidev/types
commit: |
There was a problem hiding this comment.
Pull request overview
This PR adds URL hash-based navigation to the Slidev slide overview page. When clicking a slide number in the left sidebar, the URL updates with a hash (e.g., #slide-24), enabling page refresh persistence and direct linking to specific slides. The template loop variable was also renamed from route to slide to avoid shadowing the vue-router useRoute() variable.
Changes:
- Added hash-based scroll tracking with
getSlideHashId,getSlideHash,getSlideIndexFromHash, andscrollToSlideAndUpdateHashfunctions that update the URL hash when clicking sidebar slide numbers - Added a vue-router hash watcher and
onMountedhook to restore scroll position from the URL hash on page load and browser navigation - Renamed the
v-forloop variable fromroutetoslidethroughout the template to avoid shadowing the vue-router route object, and updated documentation
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
packages/client/pages/overview.vue |
Core implementation: hash generation/parsing functions, scroll-to-hash on mount, hash update on sidebar click, watcher for hash changes, and template loop variable rename |
docs/guide/ui.md |
Documents the new URL hash behavior in the Slide Overview section |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const blocks: Map<number, HTMLElement> = reactive(new Map()) | ||
| const activeBlocks = ref<number[]>([]) | ||
| const edittingNote = ref<number | null>(null) | ||
| const skipHashScroll = ref<string>() |
There was a problem hiding this comment.
There is a potential race condition with rapid clicks on the sidebar buttons. skipHashScroll stores a single hash value, but the watcher contains await nextTick() which yields control. If the user clicks slide 1 then quickly clicks slide 2 before the watcher finishes processing the first hash change, the second click overwrites skipHashScroll to "#slide-2", and then the watcher for #slide-1 sees a mismatch and triggers an unwanted smooth scroll back to slide 1.
While unlikely in practice, consider using a Set<string> instead of a single ref to track all hashes that should skip scrolling, removing each entry as it's consumed by the watcher.
| } | ||
|
|
||
| function scrollToSlide(idx: number) { | ||
| function getSlideHashId(no: number) { |
There was a problem hiding this comment.
How about inining getSlideHashId?
| <div | ||
| v-for="(route, idx) of slides" | ||
| :key="route.no" | ||
| v-for="(slide, idx) of slides" |
There was a problem hiding this comment.
How about reverting the renaming of route, which is common in the codebase?
Hey!
This PR includes some UX improvements for the overview page. When clicking on a slide link, a hash is now added to the URL (e.g.
http://localhost:3030/overview#slide-24), making it easy to refresh the page or share a direct link to a specific slide section in long decks.