@slidev/client 0.48.0-beta.20 → 0.48.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,7 +22,7 @@ const ended = ref(false)
22
22
  const matchRoute = computed(() => {
23
23
  if (!video.value || currentContext?.value !== 'slide')
24
24
  return false
25
- return route === $slidev?.nav.currentRoute
25
+ return route && route.no === $slidev?.nav.currentSlideNo
26
26
  })
27
27
 
28
28
  const matchClick = computed(() => {
package/builtin/Toc.vue CHANGED
@@ -74,7 +74,7 @@ function filterOnlySiblings(tree: TocItem[]): TocItem[] {
74
74
  }
75
75
 
76
76
  const toc = computed(() => {
77
- const tree = $slidev?.nav.tree
77
+ const tree = $slidev?.nav.tocTree
78
78
  if (!tree)
79
79
  return []
80
80
  let tocTree = filterTreeDepth(tree)
@@ -48,7 +48,7 @@ const styles = computed(() => {
48
48
  :class="[{ 'slidev-toc-item-active': item.active }, { 'slidev-toc-item-parent-active': item.activeParent }]"
49
49
  >
50
50
  <Link :to="item.path">
51
- <Titles :no="item.path" />
51
+ <Titles :no="item.no" />
52
52
  </Link>
53
53
  <TocList
54
54
  v-if="item.children.length > 0"
@@ -1,11 +1,11 @@
1
1
  import { sum } from '@antfu/utils'
2
- import type { ClicksContext } from '@slidev/types'
2
+ import type { ClicksContext, SlideRoute } from '@slidev/types'
3
3
  import type { Ref } from 'vue'
4
4
  import { computed, ref, shallowReactive } from 'vue'
5
- import type { RouteRecordRaw } from 'vue-router'
6
- import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
5
+ import { currentSlideNo, isPrintMode, isPrintWithClicks } from '../logic/nav'
7
6
  import { normalizeAtProp } from '../logic/utils'
8
7
  import { CLICKS_MAX } from '../constants'
8
+ import { routeForceRefresh, useRouteQuery } from '../logic/route'
9
9
 
10
10
  function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): ClicksContext {
11
11
  const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
@@ -63,27 +63,28 @@ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): C
63
63
  }
64
64
  }
65
65
 
66
- export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksContext {
66
+ const queryClicksRaw = useRouteQuery('clicks', '0')
67
+
68
+ export function usePrimaryClicks(route: SlideRoute): ClicksContext {
67
69
  if (route?.meta?.__clicksContext)
68
70
  return route.meta.__clicksContext
69
- const thisPath = +(route?.path ?? Number.NaN)
71
+ const thisNo = route.no
70
72
  const current = computed({
71
73
  get() {
72
- const currentPath = +(currentRoute.value?.path ?? Number.NaN)
73
- if (!currentPath || Number.isNaN(currentPath))
74
- return 0
75
- if (currentPath === thisPath)
76
- return queryClicks.value
77
- else if (currentPath > thisPath)
74
+ // eslint-disable-next-line ts/no-use-before-define
75
+ if (context.disabled)
76
+ return CLICKS_MAX
77
+ if (currentSlideNo.value === thisNo)
78
+ return +(queryClicksRaw.value || 0) || 0
79
+ else if (currentSlideNo.value > thisNo)
78
80
  return CLICKS_MAX
79
81
  else
80
82
  return 0
81
83
  },
82
84
  set(v) {
83
- const currentPath = +(currentRoute.value?.path ?? Number.NaN)
84
- if (currentPath === thisPath) {
85
+ if (currentSlideNo.value === thisNo) {
85
86
  // eslint-disable-next-line ts/no-use-before-define
86
- queryClicks.value = Math.min(v, context.total)
87
+ queryClicksRaw.value = Math.min(v, context.total).toString()
87
88
  }
88
89
  },
89
90
  })
@@ -96,6 +97,6 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
96
97
  return context
97
98
  }
98
99
 
99
- export function useFixedClicks(route?: RouteRecordRaw | undefined, currentInit = 0): ClicksContext {
100
+ export function useFixedClicks(route?: SlideRoute | undefined, currentInit = 0): ClicksContext {
100
101
  return useClicksContextBase(ref(currentInit), route?.meta?.clicks)
101
102
  }
@@ -1,16 +1,11 @@
1
- import type { ComputedRef } from 'vue'
2
1
  import { computed } from 'vue'
3
- import type { RouteLocationNormalizedLoaded } from 'vue-router'
4
- import type { SlidevContext } from '../modules/context'
5
2
  import { configs } from '../env'
6
- import { useNav } from './useNav'
3
+ import type { SlidevContext } from '../modules/context'
4
+ import * as nav from '../logic/nav'
7
5
 
8
- export function useContext(
9
- route: ComputedRef<RouteLocationNormalizedLoaded>,
10
- ): SlidevContext {
11
- const nav = useNav(route)
6
+ export function useContext(): SlidevContext {
12
7
  return {
13
- nav,
8
+ nav: { ...nav }, // Convert the module to a plain object
14
9
  configs,
15
10
  themeConfigs: computed(() => configs.themeConfig),
16
11
  }
@@ -1,55 +1,193 @@
1
- import type { ComputedRef } from 'vue'
2
- import { computed } from 'vue'
3
- import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
4
- import type { TocItem } from '@slidev/types'
5
- import type { SlidevContextNav } from '../modules/context'
6
- import { addToTree, clicks, clicksContext, clicksTotal, downloadPDF, filterTree, getPath, getTreeWithActiveStatuses, go, next, nextSlide, openInEditor, prev, prevSlide } from '../logic/nav'
7
- import { rawRoutes } from '../routes'
8
-
9
- export function useNav(route: ComputedRef<RouteRecordRaw | RouteLocationNormalizedLoaded>): SlidevContextNav {
10
- const path = computed(() => route.value.path)
11
- const total = computed(() => rawRoutes.length)
12
-
13
- const currentPage = computed(() => Number.parseInt(path.value.split(/\//g).slice(-1)[0]) || 1)
14
- const currentPath = computed(() => getPath(currentPage.value))
15
- const currentRoute = computed(() => rawRoutes.find(i => i.path === `${currentPage.value}`) ?? rawRoutes.at(-1) ?? rawRoutes[0])
16
- const currentSlideId = computed(() => currentRoute.value?.meta?.slide?.id)
17
- const currentLayout = computed(() => currentRoute.value?.meta?.layout || (currentPage.value === 1 ? 'cover' : 'default'))
18
-
19
- const nextRoute = computed(() => rawRoutes.find(i => i.path === `${Math.min(rawRoutes.length, currentPage.value + 1)}`))
20
-
21
- const rawTree = computed(() => rawRoutes
22
- .filter((route: RouteRecordRaw) => route.meta?.slide?.title)
23
- .reduce((acc: TocItem[], route: RouteRecordRaw) => {
24
- addToTree(acc, route)
25
- return acc
26
- }, []))
27
- const treeWithActiveStatuses = computed(() => getTreeWithActiveStatuses(rawTree.value, currentRoute.value))
28
- const tree = computed(() => filterTree(treeWithActiveStatuses.value))
1
+ import type { ClicksContext, SlideRoute, TocItem } from '@slidev/types'
2
+ import type { ComputedRef, Ref, TransitionGroupProps } from 'vue'
3
+ import { computed, ref, watch } from 'vue'
4
+ import type { Router } from 'vue-router'
5
+ import { getCurrentTransition } from '../logic/transition'
6
+ import { getSlidePath } from '../logic/slides'
7
+ import { useTocTree } from './useTocTree'
8
+ import { skipTransition } from './hmr'
9
+ import { slides } from '#slidev/slides'
10
+
11
+ export interface SlidevContextNav {
12
+ slides: Ref<SlideRoute[]>
13
+ total: ComputedRef<number>
14
+
15
+ currentPath: ComputedRef<string>
16
+ currentPage: ComputedRef<number>
17
+ currentSlideNo: ComputedRef<number>
18
+ currentSlideRoute: ComputedRef<SlideRoute>
19
+ currentTransition: ComputedRef<TransitionGroupProps | undefined>
20
+ currentLayout: ComputedRef<string>
21
+
22
+ nextRoute: ComputedRef<SlideRoute>
23
+ prevRoute: ComputedRef<SlideRoute>
24
+ hasNext: ComputedRef<boolean>
25
+ hasPrev: ComputedRef<boolean>
26
+
27
+ clicksContext: ComputedRef<ClicksContext>
28
+ clicks: ComputedRef<number>
29
+ clicksTotal: ComputedRef<number>
30
+
31
+ /** The table of content tree */
32
+ tocTree: ComputedRef<TocItem[]>
33
+ /** The direction of the navigation, 1 for forward, -1 for backward */
34
+ navDirection: Ref<number>
35
+ /** The direction of the clicks, 1 for forward, -1 for backward */
36
+ clicksDirection: Ref<number>
37
+ /** Utility function for open file in editor, only avaible in dev mode */
38
+ openInEditor: (url?: string) => Promise<boolean>
39
+
40
+ /** Go to next click */
41
+ next: () => Promise<void>
42
+ /** Go to previous click */
43
+ prev: () => Promise<void>
44
+ /** Go to next slide */
45
+ nextSlide: () => Promise<void>
46
+ /** Go to previous slide */
47
+ prevSlide: (lastClicks?: boolean) => Promise<void>
48
+ /** Go to slide */
49
+ go: (page: number | string, clicks?: number) => Promise<void>
50
+ /** Go to the first slide */
51
+ goFirst: () => Promise<void>
52
+ /** Go to the last slide */
53
+ goLast: () => Promise<void>
54
+ }
55
+
56
+ export function useNavBase(
57
+ currentSlideRoute: ComputedRef<SlideRoute>,
58
+ clicksContext: ComputedRef<ClicksContext>,
59
+ queryClicks: Ref<number> = ref(0),
60
+ router?: Router,
61
+ ): SlidevContextNav {
62
+ const total = computed(() => slides.value.length)
63
+
64
+ const navDirection = ref(0)
65
+ const clicksDirection = ref(0)
66
+
67
+ const currentPath = computed(() => getSlidePath(currentSlideRoute.value))
68
+ const currentSlideNo = computed(() => currentSlideRoute.value.no)
69
+ const currentLayout = computed(() => currentSlideRoute.value.meta?.layout || (currentSlideNo.value === 1 ? 'cover' : 'default'))
70
+
71
+ const clicks = computed(() => clicksContext.value.current)
72
+ const clicksTotal = computed(() => clicksContext.value.total)
73
+ const nextRoute = computed(() => slides.value[Math.min(slides.value.length, currentSlideNo.value + 1) - 1])
74
+ const prevRoute = computed(() => slides.value[Math.max(1, currentSlideNo.value - 1) - 1])
75
+ const hasNext = computed(() => currentSlideNo.value < slides.value.length || clicks.value < clicksTotal.value)
76
+ const hasPrev = computed(() => currentSlideNo.value > 1 || clicks.value > 0)
77
+
78
+ const currentTransition = computed(() => getCurrentTransition(navDirection.value, currentSlideRoute.value, prevRoute.value))
79
+
80
+ watch(currentSlideRoute, (next, prev) => {
81
+ navDirection.value = next.no - prev.no
82
+ })
83
+
84
+ async function openInEditor(url?: string) {
85
+ if (!__DEV__)
86
+ return false
87
+ if (url == null) {
88
+ const slide = currentSlideRoute.value?.meta?.slide
89
+ if (!slide)
90
+ return false
91
+ url = `${slide.filepath}:${slide.start}`
92
+ }
93
+ await fetch(`/__open-in-editor?file=${encodeURIComponent(url)}`)
94
+ return true
95
+ }
96
+
97
+ const tocTree = useTocTree(slides)
98
+
99
+ async function next() {
100
+ clicksDirection.value = 1
101
+ if (clicksTotal.value <= queryClicks.value)
102
+ await nextSlide()
103
+ else
104
+ queryClicks.value += 1
105
+ }
106
+
107
+ async function prev() {
108
+ clicksDirection.value = -1
109
+ if (queryClicks.value <= 0)
110
+ await prevSlide()
111
+ else
112
+ queryClicks.value -= 1
113
+ }
114
+
115
+ async function nextSlide() {
116
+ clicksDirection.value = 1
117
+ if (currentSlideNo.value < slides.value.length)
118
+ await go(currentSlideNo.value + 1)
119
+ }
120
+
121
+ async function prevSlide(lastClicks = true) {
122
+ clicksDirection.value = -1
123
+ const next = Math.max(1, currentSlideNo.value - 1)
124
+ await go(next)
125
+ if (lastClicks && clicksTotal.value) {
126
+ router?.replace({
127
+ query: { ...router.currentRoute.value.query, clicks: clicksTotal.value },
128
+ })
129
+ }
130
+ }
131
+
132
+ function goFirst() {
133
+ return go(1)
134
+ }
135
+
136
+ function goLast() {
137
+ return go(total.value)
138
+ }
139
+
140
+ async function go(page: number | string, clicks?: number) {
141
+ skipTransition.value = false
142
+ await router?.push({
143
+ path: getSlidePath(page),
144
+ query: { ...router.currentRoute.value.query, clicks },
145
+ })
146
+ }
29
147
 
30
148
  return {
31
- rawRoutes,
32
- route,
33
- path,
149
+ slides,
34
150
  total,
35
- clicksContext,
36
- clicks,
37
- clicksTotal,
38
- currentPage,
39
151
  currentPath,
40
- currentRoute,
41
- currentSlideId,
152
+ currentSlideNo,
153
+ currentPage: currentSlideNo,
154
+ currentSlideRoute,
42
155
  currentLayout,
156
+ currentTransition,
157
+ clicksDirection,
43
158
  nextRoute,
44
- rawTree,
45
- treeWithActiveStatuses,
46
- tree,
47
- go,
48
- downloadPDF,
49
- next,
50
- nextSlide,
159
+ prevRoute,
160
+ clicksContext,
161
+ clicks,
162
+ clicksTotal,
163
+ hasNext,
164
+ hasPrev,
165
+ tocTree,
166
+ navDirection,
51
167
  openInEditor,
168
+ next,
52
169
  prev,
170
+ go,
171
+ goLast,
172
+ goFirst,
173
+ nextSlide,
53
174
  prevSlide,
54
175
  }
55
176
  }
177
+
178
+ export function useFixedNav(
179
+ currentSlideRoute: SlideRoute,
180
+ clicksContext: ClicksContext,
181
+ ): SlidevContextNav {
182
+ const noop = async () => { }
183
+ return {
184
+ ...useNavBase(computed(() => currentSlideRoute), computed(() => clicksContext)),
185
+ next: noop,
186
+ prev: noop,
187
+ nextSlide: noop,
188
+ prevSlide: noop,
189
+ goFirst: noop,
190
+ goLast: noop,
191
+ go: noop,
192
+ }
193
+ }
@@ -0,0 +1,40 @@
1
+ import type { Ref } from 'vue'
2
+ import { ref } from 'vue'
3
+ import { timestamp, usePointerSwipe } from '@vueuse/core'
4
+ import { isDrawing } from '../logic/drawings'
5
+ import { next, nextSlide, prev, prevSlide } from '../logic/nav'
6
+
7
+ export function useSwipeControls(root: Ref<HTMLElement | undefined>) {
8
+ const swipeBegin = ref(0)
9
+ const { direction, distanceX, distanceY } = usePointerSwipe(root, {
10
+ pointerTypes: ['touch'],
11
+ onSwipeStart() {
12
+ if (isDrawing.value)
13
+ return
14
+ swipeBegin.value = timestamp()
15
+ },
16
+ onSwipeEnd() {
17
+ if (!swipeBegin.value)
18
+ return
19
+ if (isDrawing.value)
20
+ return
21
+
22
+ const x = Math.abs(distanceX.value)
23
+ const y = Math.abs(distanceY.value)
24
+ if (x / window.innerWidth > 0.3 || x > 75) {
25
+ if (direction.value === 'left')
26
+ next()
27
+
28
+ else
29
+ prev()
30
+ }
31
+ else if (y / window.innerHeight > 0.4 || y > 200) {
32
+ if (direction.value === 'down')
33
+ prevSlide()
34
+
35
+ else
36
+ nextSlide()
37
+ }
38
+ },
39
+ })
40
+ }
@@ -0,0 +1,63 @@
1
+ import type { SlideRoute, TocItem } from '@slidev/types'
2
+ import type { ComputedRef, Ref } from 'vue'
3
+ import { computed } from 'vue'
4
+ import { currentSlideNo, currentSlideRoute, getSlidePath } from '../logic/nav'
5
+
6
+ function addToTree(tree: TocItem[], route: SlideRoute, level = 1) {
7
+ const titleLevel = route.meta?.slide?.level
8
+ if (titleLevel && titleLevel > level && tree.length > 0) {
9
+ addToTree(tree[tree.length - 1].children, route, level + 1)
10
+ }
11
+ else {
12
+ tree.push({
13
+ no: route.no,
14
+ children: [],
15
+ level,
16
+ path: getSlidePath(route.meta.slide?.frontmatter?.routeAlias ?? route.no),
17
+ hideInToc: Boolean(route.meta?.slide?.frontmatter?.hideInToc),
18
+ title: route.meta?.slide?.title,
19
+ })
20
+ }
21
+ }
22
+
23
+ function getTreeWithActiveStatuses(
24
+ tree: TocItem[],
25
+ currentRoute?: SlideRoute,
26
+ hasActiveParent = false,
27
+ parent?: TocItem,
28
+ ): TocItem[] {
29
+ return tree.map((item: TocItem) => {
30
+ const clone = {
31
+ ...item,
32
+ active: item.no === currentSlideNo.value,
33
+ hasActiveParent,
34
+ }
35
+ if (clone.children.length > 0)
36
+ clone.children = getTreeWithActiveStatuses(clone.children, currentRoute, clone.active || clone.hasActiveParent, clone)
37
+ if (parent && (clone.active || clone.activeParent))
38
+ parent.activeParent = true
39
+ return clone
40
+ })
41
+ }
42
+
43
+ function filterTree(tree: TocItem[], level = 1): TocItem[] {
44
+ return tree
45
+ .filter((item: TocItem) => !item.hideInToc)
46
+ .map((item: TocItem) => ({
47
+ ...item,
48
+ children: filterTree(item.children, level + 1),
49
+ }))
50
+ }
51
+
52
+ export function useTocTree(slides: Ref<SlideRoute[]>): ComputedRef<TocItem[]> {
53
+ const rawTree = computed(() => slides.value
54
+ .filter((route: SlideRoute) => route.meta?.slide?.title)
55
+ .reduce((acc: TocItem[], route: SlideRoute) => {
56
+ addToTree(acc, route)
57
+ return acc
58
+ }, []))
59
+
60
+ const treeWithActiveStatuses = computed(() => getTreeWithActiveStatuses(rawTree.value, currentSlideRoute.value))
61
+
62
+ return computed(() => filterTree(treeWithActiveStatuses.value))
63
+ }
package/constants.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type { ComputedRef, InjectionKey, Ref, UnwrapNestedRefs } from 'vue'
2
- import type { RouteRecordRaw } from 'vue-router'
3
- import type { ClicksContext, RenderContext } from '@slidev/types'
2
+ import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
4
3
  import type { SlidevContext } from './modules/context'
5
4
 
6
5
  // Here we use string literal instead of symbols to make HMR more stable
@@ -9,7 +8,7 @@ export const injectionClicksContext = '$$slidev-clicks-context' as unknown as In
9
8
  export const injectionCurrentPage = '$$slidev-page' as unknown as InjectionKey<Ref<number>>
10
9
  export const injectionSlideScale = '$$slidev-slide-scale' as unknown as InjectionKey<ComputedRef<number>>
11
10
  export const injectionSlidevContext = '$$slidev-context' as unknown as InjectionKey<UnwrapNestedRefs<SlidevContext>>
12
- export const injectionRoute = '$$slidev-route' as unknown as InjectionKey<RouteRecordRaw>
11
+ export const injectionRoute = '$$slidev-route' as unknown as InjectionKey<SlideRoute>
13
12
  export const injectionRenderContext = '$$slidev-render-context' as unknown as InjectionKey<Ref<RenderContext>>
14
13
  export const injectionActive = '$$slidev-active' as unknown as InjectionKey<Ref<boolean>>
15
14
  export const injectionFrontmatter = '$$slidev-fontmatter' as unknown as InjectionKey<Record<string, any>>
package/context.ts CHANGED
@@ -51,7 +51,7 @@ export function provideFrontmatter(frontmatter: Record<string, any>) {
51
51
  } = useSlideContext()
52
52
 
53
53
  // update frontmatter in router to make HMR work better
54
- const route = $slidev.nav.rawRoutes.find(i => i.path === String($page.value))
54
+ const route = $slidev.nav.slides.find(i => i.no === $page.value)
55
55
  if (route?.meta?.slide?.frontmatter) {
56
56
  for (const key of Object.keys(route.meta.slide.frontmatter)) {
57
57
  if (!(key in frontmatter))
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, watch } from 'vue'
3
3
  import Fuse from 'fuse.js'
4
- import { go, rawRoutes } from '../logic/nav'
4
+ import { go, slides } from '../logic/nav'
5
5
  import { activeElement, showGotoDialog } from '../state'
6
6
  import Titles from '#slidev/titles.md'
7
7
 
@@ -16,7 +16,7 @@ function notNull<T>(value: T | null | undefined): value is T {
16
16
  return value !== null && value !== undefined
17
17
  }
18
18
 
19
- const fuse = computed(() => new Fuse(rawRoutes.map(i => i.meta?.slide).filter(notNull), {
19
+ const fuse = computed(() => new Fuse(slides.value.map(i => i.meta?.slide).filter(notNull), {
20
20
  keys: ['no', 'title'],
21
21
  threshold: 0.3,
22
22
  shouldSort: true,
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, shallowRef } from 'vue'
3
3
  import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
4
- import { currentPage, downloadPDF, hasNext, hasPrev, isEmbedded, isPresenter, next, presenterPassword, prev, showPresenter, total } from '../logic/nav'
4
+ import { downloadPDF } from '../utils'
5
+ import { currentRoute, currentSlideNo, getSlidePath, hasNext, hasPrev, isEmbedded, isPresenter, isPresenterAvailable, next, prev, total } from '../logic/nav'
5
6
  import { activeElement, breakpoints, fullscreen, presenterLayout, showEditor, showInfoDialog, showPresenterCursor, toggleOverview, togglePresenterLayout } from '../state'
6
7
  import { brush, drawingEnabled } from '../logic/drawings'
7
8
  import { configs } from '../env'
@@ -21,9 +22,10 @@ const props = defineProps({
21
22
  const md = breakpoints.smaller('md')
22
23
  const { isFullscreen, toggle: toggleFullscreen } = fullscreen
23
24
 
25
+ const presenterPassword = computed(() => currentRoute.value.query.password)
24
26
  const query = computed(() => presenterPassword.value ? `?password=${presenterPassword.value}` : '')
25
- const presenterLink = computed(() => `/presenter/${currentPage.value}${query.value}`)
26
- const nonPresenterLink = computed(() => `/${currentPage.value}${query.value}`)
27
+ const presenterLink = computed(() => `${getSlidePath(currentSlideNo.value, true)}${query.value}`)
28
+ const nonPresenterLink = computed(() => `${getSlidePath(currentSlideNo.value, false)}${query.value}`)
27
29
 
28
30
  const root = ref<HTMLDivElement>()
29
31
  function onMouseLeave() {
@@ -107,7 +109,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
107
109
  <RouterLink v-if="isPresenter" :to="nonPresenterLink" class="slidev-icon-btn" title="Play Mode">
108
110
  <carbon:presentation-file />
109
111
  </RouterLink>
110
- <RouterLink v-if="__SLIDEV_FEATURE_PRESENTER__ && showPresenter" :to="presenterLink" class="slidev-icon-btn" title="Presenter Mode">
112
+ <RouterLink v-if="__SLIDEV_FEATURE_PRESENTER__ && isPresenterAvailable" :to="presenterLink" class="slidev-icon-btn" title="Presenter Mode">
111
113
  <carbon:user-speaker />
112
114
  </RouterLink>
113
115
 
@@ -156,7 +158,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
156
158
 
157
159
  <div class="h-40px flex" p="l-1 t-0.5 r-2" text="sm leading-2">
158
160
  <div class="my-auto">
159
- {{ currentPage }}
161
+ {{ currentSlideNo }}
160
162
  <span class="opacity-50">/ {{ total }}</span>
161
163
  </div>
162
164
  </div>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import type { ClicksContext } from 'packages/types'
2
+ import type { ClicksContext } from '@slidev/types'
3
3
  import { useSlideInfo } from '../logic/note'
4
4
  import NoteDisplay from './NoteDisplay.vue'
5
5
 
@@ -4,7 +4,7 @@ import { computed } from 'vue'
4
4
  import { provideLocal } from '@vueuse/core'
5
5
  import { configs, slideAspect, slideWidth } from '../env'
6
6
  import { injectionSlideScale } from '../constants'
7
- import { route as currentRoute, rawRoutes } from '../logic/nav'
7
+ import { currentRoute, slides } from '../logic/nav'
8
8
  import PrintSlide from './PrintSlide.vue'
9
9
 
10
10
  const props = defineProps<{
@@ -22,7 +22,8 @@ const scale = computed(() => {
22
22
  return (height.value * slideAspect) / slideWidth
23
23
  })
24
24
 
25
- let routes = rawRoutes
25
+ // In print mode, the routes will never change. So we don't need reactivity here.
26
+ let routes = slides.value
26
27
  if (currentRoute.value.query.range) {
27
28
  const r = parseRangeString(routes.length, currentRoute.value.query.range as string)
28
29
  routes = r.map(i => routes[i - 1])
@@ -38,7 +39,7 @@ provideLocal(injectionSlideScale, scale)
38
39
  <template>
39
40
  <div id="print-container" :class="className">
40
41
  <div id="print-content">
41
- <PrintSlide v-for="route of routes" :key="route.path" :route="route" />
42
+ <PrintSlide v-for="route of routes" :key="route.no" :route="route" />
42
43
  </div>
43
44
  <slot name="controls" />
44
45
  </div>
@@ -1,30 +1,23 @@
1
1
  <script setup lang="ts">
2
- import type { RouteRecordRaw } from 'vue-router'
3
- import { computed } from 'vue'
4
- import { useNav } from '../composables/useNav'
2
+ import type { SlideRoute } from '@slidev/types'
3
+ import { useFixedNav } from '../composables/useNav'
5
4
  import { useFixedClicks } from '../composables/useClicks'
6
5
  import PrintSlideClick from './PrintSlideClick.vue'
7
6
 
8
- const props = defineProps<{ route: RouteRecordRaw }>()
9
-
10
- const route = computed(() => props.route)
11
- const nav = useNav(route)
12
- const clicks0 = useFixedClicks(route.value, 0)
7
+ const { route } = defineProps<{ route: SlideRoute }>()
8
+ const clicks0 = useFixedClicks(route, 0)
13
9
  </script>
14
10
 
15
11
  <template>
16
12
  <PrintSlideClick
17
13
  :clicks-context="clicks0"
18
- :nav="nav"
19
- :route="route"
14
+ :nav="useFixedNav(route, clicks0)"
20
15
  />
21
16
  <template v-if="!clicks0.disabled">
22
17
  <PrintSlideClick
23
18
  v-for="i of clicks0.total"
24
19
  :key="i"
25
- :clicks-context="useFixedClicks(route, i)"
26
- :nav="nav"
27
- :route="route"
20
+ :nav="useFixedNav(route, useFixedClicks(route, i))"
28
21
  />
29
22
  </template>
30
23
  </template>