@slidev/client 0.48.0-beta.2 → 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.
Files changed (91) hide show
  1. package/App.vue +7 -0
  2. package/builtin/Arrow.vue +2 -4
  3. package/builtin/CodeBlockWrapper.vue +14 -6
  4. package/builtin/KaTexBlockWrapper.vue +5 -4
  5. package/builtin/Mermaid.vue +4 -3
  6. package/builtin/Monaco.vue +109 -92
  7. package/builtin/RenderWhen.vue +3 -3
  8. package/builtin/ShikiMagicMove.vue +50 -0
  9. package/builtin/SlideCurrentNo.vue +2 -3
  10. package/builtin/SlidesTotal.vue +3 -4
  11. package/builtin/SlidevVideo.vue +9 -7
  12. package/builtin/Toc.vue +4 -4
  13. package/builtin/TocList.vue +4 -3
  14. package/builtin/Tweet.vue +3 -22
  15. package/builtin/VClick.ts +2 -1
  16. package/builtin/VClickGap.vue +3 -5
  17. package/builtin/VClicks.ts +1 -1
  18. package/composables/useClicks.ts +39 -20
  19. package/composables/useContext.ts +4 -9
  20. package/composables/useNav.ts +182 -44
  21. package/composables/useSwipeControls.ts +40 -0
  22. package/composables/useTocTree.ts +63 -0
  23. package/constants.ts +59 -10
  24. package/context.ts +73 -0
  25. package/env.ts +3 -12
  26. package/internals/ClicksSlider.vue +93 -0
  27. package/internals/Controls.vue +2 -2
  28. package/internals/DrawingControls.vue +39 -9
  29. package/internals/DrawingLayer.vue +3 -3
  30. package/internals/Goto.vue +7 -6
  31. package/internals/IconButton.vue +7 -3
  32. package/internals/InfoDialog.vue +1 -1
  33. package/internals/Modal.vue +1 -1
  34. package/internals/NavControls.vue +11 -10
  35. package/internals/NoteDisplay.vue +131 -8
  36. package/internals/NoteEditable.vue +128 -0
  37. package/internals/NoteStatic.vue +8 -6
  38. package/internals/PrintContainer.vue +8 -6
  39. package/internals/PrintSlide.vue +10 -11
  40. package/internals/PrintSlideClick.vue +14 -18
  41. package/internals/{SlidesOverview.vue → QuickOverview.vue} +31 -20
  42. package/internals/RecordingControls.vue +1 -1
  43. package/internals/RecordingDialog.vue +5 -6
  44. package/internals/{Editor.vue → SideEditor.vue} +9 -5
  45. package/internals/SlideContainer.vue +12 -9
  46. package/internals/SlideLoading.vue +19 -0
  47. package/internals/SlideWrapper.ts +32 -16
  48. package/internals/SlidesShow.vue +20 -18
  49. package/layouts/error.vue +5 -0
  50. package/layouts/two-cols-header.vue +9 -3
  51. package/logic/drawings.ts +13 -10
  52. package/logic/nav-state.ts +20 -0
  53. package/logic/nav.ts +51 -258
  54. package/logic/note.ts +9 -9
  55. package/logic/overview.ts +2 -2
  56. package/logic/route.ts +10 -1
  57. package/logic/slides.ts +19 -0
  58. package/logic/transition.ts +50 -0
  59. package/main.ts +8 -4
  60. package/modules/context.ts +7 -13
  61. package/modules/mermaid.ts +6 -7
  62. package/modules/{directives.ts → v-click.ts} +15 -15
  63. package/modules/v-mark.ts +159 -0
  64. package/package.json +27 -16
  65. package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
  66. package/{internals/NotesView.vue → pages/notes.vue} +7 -6
  67. package/pages/overview.vue +227 -0
  68. package/{internals/Play.vue → pages/play.vue} +17 -13
  69. package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +13 -8
  70. package/{internals/Presenter.vue → pages/presenter.vue} +114 -105
  71. package/{internals/Print.vue → pages/print.vue} +3 -4
  72. package/routes.ts +28 -60
  73. package/setup/codemirror.ts +8 -3
  74. package/setup/monaco.ts +108 -44
  75. package/setup/root.ts +8 -9
  76. package/setup/shortcuts.ts +2 -1
  77. package/shim-vue.d.ts +38 -0
  78. package/shim.d.ts +1 -13
  79. package/state/index.ts +10 -10
  80. package/styles/code.css +7 -3
  81. package/styles/index.css +68 -7
  82. package/styles/katex.css +1 -1
  83. package/styles/layouts-base.css +17 -12
  84. package/styles/monaco.css +27 -0
  85. package/styles/vars.css +1 -0
  86. package/uno.config.ts +14 -2
  87. package/utils.ts +15 -2
  88. package/iframes/monaco/index.css +0 -28
  89. package/iframes/monaco/index.html +0 -7
  90. package/iframes/monaco/index.ts +0 -260
  91. package/internals/NoteEditor.vue +0 -88
package/logic/drawings.ts CHANGED
@@ -4,7 +4,8 @@ import { createDrauu } from 'drauu'
4
4
  import { toReactive, useLocalStorage } from '@vueuse/core'
5
5
  import { drawingState, onPatch, patch } from '../state/drawings'
6
6
  import { configs } from '../env'
7
- import { currentPage, isPresenter } from './nav'
7
+ import { isInputting } from '../state'
8
+ import { currentSlideNo, isPresenter } from './nav'
8
9
 
9
10
  export const brushColors = [
10
11
  '#ff595e',
@@ -40,11 +41,13 @@ export const drawingMode = computed({
40
41
  set(v: DrawingMode | 'arrow') {
41
42
  _mode.value = v
42
43
  if (v === 'arrow') {
43
- brush.mode = 'line'
44
+ // eslint-disable-next-line ts/no-use-before-define
45
+ drauu.mode = 'line'
44
46
  brush.arrowEnd = true
45
47
  }
46
48
  else {
47
- brush.mode = v
49
+ // eslint-disable-next-line ts/no-use-before-define
50
+ drauu.mode = v
48
51
  brush.arrowEnd = false
49
52
  }
50
53
  },
@@ -60,7 +63,7 @@ export const drauu = markRaw(createDrauu(drauuOptions))
60
63
  export function clearDrauu() {
61
64
  drauu.clear()
62
65
  if (syncUp.value)
63
- patch(currentPage.value, '')
66
+ patch(currentSlideNo.value, '')
64
67
  }
65
68
 
66
69
  export function updateState() {
@@ -71,7 +74,7 @@ export function updateState() {
71
74
 
72
75
  export function loadCanvas(page?: number) {
73
76
  disableDump = true
74
- const data = drawingState[page || currentPage.value]
77
+ const data = drawingState[page || currentSlideNo.value]
75
78
  if (data != null)
76
79
  drauu.load(data)
77
80
  else
@@ -84,7 +87,7 @@ drauu.on('changed', () => {
84
87
  updateState()
85
88
  if (!disableDump) {
86
89
  const dump = drauu.dump()
87
- const key = currentPage.value
90
+ const key = currentSlideNo.value
88
91
  if ((drawingState[key] || '') !== dump && syncUp.value)
89
92
  patch(key, drauu.dump())
90
93
  }
@@ -92,14 +95,14 @@ drauu.on('changed', () => {
92
95
 
93
96
  onPatch((state) => {
94
97
  disableDump = true
95
- if (state[currentPage.value] != null)
96
- drauu.load(state[currentPage.value] || '')
98
+ if (state[currentSlideNo.value] != null)
99
+ drauu.load(state[currentSlideNo.value] || '')
97
100
  disableDump = false
98
101
  updateState()
99
102
  })
100
103
 
101
104
  nextTick(() => {
102
- watch(currentPage, () => {
105
+ watch(currentSlideNo, () => {
103
106
  if (!drauu.mounted)
104
107
  return
105
108
  loadCanvas()
@@ -110,7 +113,7 @@ drauu.on('start', () => isDrawing.value = true)
110
113
  drauu.on('end', () => isDrawing.value = false)
111
114
 
112
115
  window.addEventListener('keydown', (e) => {
113
- if (!drawingEnabled.value)
116
+ if (!drawingEnabled.value || isInputting.value)
114
117
  return
115
118
 
116
119
  const noModifier = !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey
@@ -0,0 +1,20 @@
1
+ import { computed } from 'vue'
2
+ import { logicOr } from '@vueuse/math'
3
+ import { configs } from '../env'
4
+ import { router } from '../routes'
5
+ import { getSlide, slides } from './slides'
6
+
7
+ export const currentRoute = computed(() => router.currentRoute.value)
8
+
9
+ export const isPrintMode = computed(() => currentRoute.value.query.print !== undefined)
10
+ export const isPrintWithClicks = computed(() => currentRoute.value.query.print === 'clicks')
11
+ export const isEmbedded = computed(() => currentRoute.value.query.embedded !== undefined)
12
+ export const isPlaying = computed(() => currentRoute.value.name === 'play')
13
+ export const isPresenter = computed(() => currentRoute.value.name === 'presenter')
14
+ export const isNotesViewer = computed(() => currentRoute.value.name === 'notes')
15
+ export const isPresenterAvailable = computed(() => !isPresenter.value && (!configs.remote || currentRoute.value.query.password === configs.remote))
16
+
17
+ export const hasPrimarySlide = logicOr(isPlaying, isPresenter)
18
+
19
+ export const currentSlideNo = computed(() => hasPrimarySlide.value ? getSlide(currentRoute.value.params.no as string)?.no ?? 1 : 1)
20
+ export const currentSlideRoute = computed(() => slides.value[currentSlideNo.value - 1])
package/logic/nav.ts CHANGED
@@ -1,44 +1,22 @@
1
- import type { Ref, TransitionGroupProps } from 'vue'
2
- import type { RouteRecordRaw } from 'vue-router'
3
- import { computed, nextTick, ref, watch } from 'vue'
4
- import type { TocItem } from '@slidev/types'
5
- import { timestamp, usePointerSwipe } from '@vueuse/core'
6
- import { rawRoutes, router } from '../routes'
7
- import { configs } from '../env'
8
- import { skipTransition } from '../composables/hmr'
1
+ import { computed, watch } from 'vue'
2
+ import { router } from '../routes'
9
3
  import { usePrimaryClicks } from '../composables/useClicks'
4
+ import { CLICKS_MAX } from '../constants'
5
+ import { useNavBase } from '../composables/useNav'
10
6
  import { useRouteQuery } from './route'
11
- import { isDrawing } from './drawings'
7
+ import { currentRoute, currentSlideRoute, hasPrimarySlide } from './nav-state'
8
+ import { getSlide } from './slides'
12
9
 
13
- export { rawRoutes, router }
10
+ export * from './slides'
11
+ export * from './nav-state'
14
12
 
15
- // force update collected elements when the route is fully resolved
16
- export const routeForceRefresh = ref(0)
17
- nextTick(() => {
18
- router.afterEach(async () => {
19
- await nextTick()
20
- routeForceRefresh.value += 1
21
- })
22
- })
23
-
24
- export const navDirection = ref(0)
25
-
26
- export const route = computed(() => router.currentRoute.value)
27
-
28
- export const isPrintMode = computed(() => route.value.query.print !== undefined)
29
- export const isPrintWithClicks = computed(() => route.value.query.print === 'clicks')
30
- export const isEmbedded = computed(() => route.value.query.embedded !== undefined)
31
- export const isPresenter = computed(() => route.value.path.startsWith('/presenter'))
32
- export const isNotesViewer = computed(() => route.value.path.startsWith('/notes'))
33
- export const presenterPassword = computed(() => route.value.query.password)
34
- export const showPresenter = computed(() => !isPresenter.value && (!configs.remote || presenterPassword.value === configs.remote))
13
+ export const clicksContext = computed(() => usePrimaryClicks(currentSlideRoute.value))
35
14
 
36
15
  const queryClicksRaw = useRouteQuery('clicks', '0')
37
16
  export const queryClicks = computed({
38
17
  get() {
39
- // eslint-disable-next-line ts/no-use-before-define
40
18
  if (clicksContext.value.disabled)
41
- return 99999
19
+ return CLICKS_MAX
42
20
  let v = +(queryClicksRaw.value || 0)
43
21
  if (Number.isNaN(v))
44
22
  v = 0
@@ -49,230 +27,45 @@ export const queryClicks = computed({
49
27
  },
50
28
  })
51
29
 
52
- export const total = computed(() => rawRoutes.length)
53
- export const path = computed(() => route.value.path)
54
-
55
- export const currentPage = computed(() => Number.parseInt(path.value.split(/\//g).slice(-1)[0]) || 1)
56
- export const currentPath = computed(() => getPath(currentPage.value))
57
- export const currentRoute = computed(() => rawRoutes.find(i => i.path === `${currentPage.value}`))
58
- export const currentSlideId = computed(() => currentRoute.value?.meta?.slide?.id)
59
- export const currentLayout = computed(() => currentRoute.value?.meta?.layout || (currentPage.value === 1 ? 'cover' : 'default'))
60
-
61
- export const nextRoute = computed(() => rawRoutes.find(i => i.path === `${Math.min(rawRoutes.length, currentPage.value + 1)}`))
62
- export const prevRoute = computed(() => rawRoutes.find(i => i.path === `${Math.max(1, currentPage.value - 1)}`))
63
-
64
- export const clicksContext = computed(() => usePrimaryClicks(currentRoute.value))
65
- export const clicks = computed(() => clicksContext.value.current)
66
- export const clicksTotal = computed(() => clicksContext.value.total)
67
-
68
- export const hasNext = computed(() => currentPage.value < rawRoutes.length || clicks.value < clicksTotal.value)
69
- export const hasPrev = computed(() => currentPage.value > 1 || clicks.value > 0)
70
-
71
- export const rawTree = computed(() => rawRoutes
72
- .filter((route: RouteRecordRaw) => route.meta?.slide?.title)
73
- .reduce((acc: TocItem[], route: RouteRecordRaw) => {
74
- addToTree(acc, route)
75
- return acc
76
- }, []))
77
- export const treeWithActiveStatuses = computed(() => getTreeWithActiveStatuses(rawTree.value, currentRoute.value))
78
- export const tree = computed(() => filterTree(treeWithActiveStatuses.value))
79
-
80
- export const transition = computed(() => getCurrentTransition(navDirection.value, currentRoute.value, prevRoute.value))
81
-
82
- watch(currentRoute, (next, prev) => {
83
- navDirection.value = Number(next?.path) - Number(prev?.path)
84
- })
85
-
86
- export async function next() {
87
- if (clicksTotal.value <= queryClicks.value)
88
- await nextSlide()
89
- else
90
- queryClicks.value += 1
91
- }
92
-
93
- export async function prev() {
94
- if (queryClicks.value <= 0)
95
- await prevSlide()
96
- else
97
- queryClicks.value -= 1
98
- }
99
-
100
- export function getPath(no: number | string) {
101
- return isPresenter.value ? `/presenter/${no}` : `/${no}`
102
- }
103
-
104
- export async function nextSlide() {
105
- if (currentPage.value < rawRoutes.length)
106
- await go(currentPage.value + 1)
107
- }
108
-
109
- export async function prevSlide(lastClicks = true) {
110
- const next = Math.max(1, currentPage.value - 1)
111
- await go(next)
112
- if (lastClicks && clicksTotal.value)
113
- router.replace({ query: { ...route.value.query, clicks: clicksTotal.value } })
114
- }
115
-
116
- export function goFirst() {
117
- return go(1)
118
- }
119
-
120
- export function goLast() {
121
- return go(total.value)
122
- }
123
-
124
- export function go(page: number | string, clicks?: number) {
125
- skipTransition.value = false
126
- return router.push({ path: getPath(page), query: { ...route.value.query, clicks } })
127
- }
128
-
129
- export function useSwipeControls(root: Ref<HTMLElement | undefined>) {
130
- const swipeBegin = ref(0)
131
- const { direction, distanceX, distanceY } = usePointerSwipe(root, {
132
- onSwipeStart(e) {
133
- if (e.pointerType !== 'touch')
134
- return
135
- if (isDrawing.value)
136
- return
137
- swipeBegin.value = timestamp()
138
- },
139
- onSwipeEnd(e) {
140
- if (e.pointerType !== 'touch')
141
- return
142
- if (!swipeBegin.value)
143
- return
144
- if (isDrawing.value)
145
- return
146
-
147
- const x = Math.abs(distanceX.value)
148
- const y = Math.abs(distanceY.value)
149
- if (x / window.innerWidth > 0.3 || x > 75) {
150
- if (direction.value === 'left')
151
- next()
152
- else
153
- prev()
154
- }
155
- else if (y / window.innerHeight > 0.4 || y > 200) {
156
- if (direction.value === 'down')
157
- prevSlide()
158
- else
159
- nextSlide()
160
- }
161
- },
162
- })
163
- }
164
-
165
- export async function downloadPDF() {
166
- const { saveAs } = await import('file-saver')
167
- saveAs(
168
- typeof configs.download === 'string'
169
- ? configs.download
170
- : configs.exportFilename
171
- ? `${configs.exportFilename}.pdf`
172
- : `${import.meta.env.BASE_URL}slidev-exported.pdf`,
173
- `${configs.title}.pdf`,
174
- )
175
- }
176
-
177
- export async function openInEditor(url?: string) {
178
- if (url == null) {
179
- const slide = currentRoute.value?.meta?.slide
180
- if (!slide?.filepath)
181
- return false
182
- url = `${slide.filepath}:${slide.start}`
183
- }
184
- await fetch(`/__open-in-editor?file=${encodeURIComponent(url)}`)
185
- return true
186
- }
187
-
188
- export function addToTree(tree: TocItem[], route: RouteRecordRaw, level = 1) {
189
- const titleLevel = route.meta?.slide?.level
190
- if (titleLevel && titleLevel > level && tree.length > 0) {
191
- addToTree(tree[tree.length - 1].children, route, level + 1)
192
- }
193
- else {
194
- tree.push({
195
- children: [],
196
- level,
197
- path: route.path,
198
- hideInToc: Boolean(route.meta?.slide?.frontmatter?.hideInToc),
199
- title: route.meta?.slide?.title,
200
- })
201
- }
202
- }
203
-
204
- export function getTreeWithActiveStatuses(
205
- tree: TocItem[],
206
- currentRoute?: RouteRecordRaw,
207
- hasActiveParent = false,
208
- parent?: TocItem,
209
- ): TocItem[] {
210
- return tree.map((item: TocItem) => {
211
- const clone = {
212
- ...item,
213
- active: item.path === currentRoute?.path,
214
- hasActiveParent,
30
+ export const {
31
+ slides,
32
+ total,
33
+ currentPath,
34
+ currentSlideNo,
35
+ currentPage,
36
+ currentLayout,
37
+ currentTransition,
38
+ clicksDirection,
39
+ nextRoute,
40
+ prevRoute,
41
+ clicks,
42
+ clicksTotal,
43
+ hasNext,
44
+ hasPrev,
45
+ tocTree,
46
+ navDirection,
47
+ openInEditor,
48
+ next,
49
+ prev,
50
+ go,
51
+ goLast,
52
+ goFirst,
53
+ nextSlide,
54
+ prevSlide,
55
+ } = useNavBase(
56
+ currentSlideRoute,
57
+ clicksContext,
58
+ queryClicks,
59
+ router,
60
+ )
61
+
62
+ watch(
63
+ [total, currentRoute],
64
+ async () => {
65
+ if (hasPrimarySlide.value && !getSlide(currentRoute.value.params.no as string)) {
66
+ // The current slide may has been removed. Redirect to the last slide.
67
+ await goLast()
215
68
  }
216
- if (clone.children.length > 0)
217
- clone.children = getTreeWithActiveStatuses(clone.children, currentRoute, clone.active || clone.hasActiveParent, clone)
218
- if (parent && (clone.active || clone.activeParent))
219
- parent.activeParent = true
220
- return clone
221
- })
222
- }
223
-
224
- export function filterTree(tree: TocItem[], level = 1): TocItem[] {
225
- return tree
226
- .filter((item: TocItem) => !item.hideInToc)
227
- .map((item: TocItem) => ({
228
- ...item,
229
- children: filterTree(item.children, level + 1),
230
- }))
231
- }
232
-
233
- const transitionResolveMap: Record<string, string | undefined> = {
234
- 'slide-left': 'slide-left | slide-right',
235
- 'slide-right': 'slide-right | slide-left',
236
- 'slide-up': 'slide-up | slide-down',
237
- 'slide-down': 'slide-down | slide-up',
238
- }
239
-
240
- export function resolveTransition(transition?: string | TransitionGroupProps, isBackward = false): TransitionGroupProps | undefined {
241
- if (!transition)
242
- return undefined
243
- if (typeof transition === 'string') {
244
- transition = {
245
- name: transition,
246
- }
247
- }
248
-
249
- if (!transition.name)
250
- return undefined
251
-
252
- let name = transition.name.includes('|')
253
- ? transition.name
254
- : (transitionResolveMap[transition.name] || transition.name)
255
-
256
- if (name.includes('|')) {
257
- const [forward, backward] = name.split('|').map(i => i.trim())
258
- name = isBackward ? backward : forward
259
- }
260
-
261
- if (!name)
262
- return undefined
263
-
264
- return {
265
- ...transition,
266
- name,
267
- }
268
- }
269
-
270
- export function getCurrentTransition(direction: number, currentRoute?: RouteRecordRaw, prevRoute?: RouteRecordRaw) {
271
- let transition = direction > 0
272
- ? prevRoute?.meta?.transition
273
- : currentRoute?.meta?.transition
274
- if (!transition)
275
- transition = configs.transition
276
-
277
- return resolveTransition(transition, direction < 0)
278
- }
69
+ },
70
+ { flush: 'post', immediate: true },
71
+ )
package/logic/note.ts CHANGED
@@ -2,17 +2,17 @@ import type { MaybeRef } from '@vueuse/core'
2
2
  import { useFetch } from '@vueuse/core'
3
3
  import type { Ref } from 'vue'
4
4
  import { computed, ref, unref } from 'vue'
5
- import type { SlideInfo, SlideInfoExtended } from '@slidev/types'
5
+ import type { SlideInfo, SlidePatch } from '@slidev/types'
6
6
 
7
7
  export interface UseSlideInfo {
8
- info: Ref<SlideInfoExtended | undefined>
9
- update: (data: Partial<SlideInfo>) => Promise<SlideInfoExtended | void>
8
+ info: Ref<SlideInfo | undefined>
9
+ update: (data: SlidePatch) => Promise<SlideInfo | void>
10
10
  }
11
11
 
12
12
  export function useSlideInfo(id: number | undefined): UseSlideInfo {
13
13
  if (id == null) {
14
14
  return {
15
- info: ref() as Ref<SlideInfoExtended | undefined>,
15
+ info: ref() as Ref<SlideInfo | undefined>,
16
16
  update: async () => {},
17
17
  }
18
18
  }
@@ -21,7 +21,7 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
21
21
 
22
22
  execute()
23
23
 
24
- const update = async (data: Partial<SlideInfo>) => {
24
+ const update = async (data: SlidePatch) => {
25
25
  return await fetch(
26
26
  url,
27
27
  {
@@ -36,12 +36,12 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
36
36
  }
37
37
 
38
38
  if (__DEV__) {
39
- import.meta.hot?.on('slidev-update', (payload) => {
39
+ import.meta.hot?.on('slidev:update-slide', (payload) => {
40
40
  if (payload.id === id)
41
41
  info.value = payload.data
42
42
  })
43
- import.meta.hot?.on('slidev-update-note', (payload) => {
44
- if (payload.id === id && info.value.note.trim() !== payload.note.trim())
43
+ import.meta.hot?.on('slidev:update-note', (payload) => {
44
+ if (payload.id === id && info.value.note?.trim() !== payload.note?.trim())
45
45
  info.value = { ...info.value, ...payload }
46
46
  })
47
47
  }
@@ -64,7 +64,7 @@ export function useDynamicSlideInfo(id: MaybeRef<number | undefined>) {
64
64
 
65
65
  return {
66
66
  info: computed(() => get(unref(id)).info.value),
67
- update: async (data: Partial<SlideInfo>, newId?: number) => {
67
+ update: async (data: SlidePatch, newId?: number) => {
68
68
  const info = get(newId ?? unref(id))
69
69
  const newData = await info.update(data)
70
70
  if (newData)
package/logic/overview.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { computed, ref } from 'vue'
2
- import { rawRoutes } from '../routes'
2
+ import { slides } from './nav'
3
3
 
4
4
  // To have same format(.value) as max, wrap it with ref.
5
5
  const min = ref(1)
6
- const max = computed(() => rawRoutes.length)
6
+ const max = computed(() => slides.value.length)
7
7
 
8
8
  export const currentOverviewPage = ref(0)
9
9
  export const overviewRowCount = ref(0)
package/logic/route.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { computed, nextTick, unref } from 'vue'
1
+ import { computed, nextTick, ref, unref } from 'vue'
2
2
  import { router } from '../routes'
3
3
 
4
4
  export function useRouteQuery<T extends string | string[]>(
@@ -24,3 +24,12 @@ export function useRouteQuery<T extends string | string[]>(
24
24
  },
25
25
  })
26
26
  }
27
+
28
+ // force update collected elements when the route is fully resolved
29
+ export const routeForceRefresh = ref(0)
30
+ nextTick(() => {
31
+ router.afterEach(async () => {
32
+ await nextTick()
33
+ routeForceRefresh.value += 1
34
+ })
35
+ })
@@ -0,0 +1,19 @@
1
+ import type { SlideRoute } from '@slidev/types'
2
+ import { router } from '../routes'
3
+ import { isPresenter } from './nav-state'
4
+ import { slides } from '#slidev/slides'
5
+
6
+ export { slides, router }
7
+
8
+ export function getSlide(no: number | string) {
9
+ return slides.value.find(
10
+ s => (s.no === +no || s.meta.slide?.frontmatter.routeAlias === no),
11
+ )
12
+ }
13
+
14
+ export function getSlidePath(route: SlideRoute | number | string, presenter = isPresenter.value) {
15
+ if (typeof route === 'number' || typeof route === 'string')
16
+ route = getSlide(route)!
17
+ const no = route.meta.slide?.frontmatter.routeAlias ?? route.no
18
+ return presenter ? `/presenter/${no}` : `/${no}`
19
+ }
@@ -0,0 +1,50 @@
1
+ import type { SlideRoute } from '@slidev/types'
2
+ import type { TransitionGroupProps } from 'vue'
3
+ import { configs } from '../env'
4
+
5
+ const transitionResolveMap: Record<string, string | undefined> = {
6
+ 'slide-left': 'slide-left | slide-right',
7
+ 'slide-right': 'slide-right | slide-left',
8
+ 'slide-up': 'slide-up | slide-down',
9
+ 'slide-down': 'slide-down | slide-up',
10
+ }
11
+
12
+ export function resolveTransition(transition?: string | TransitionGroupProps, isBackward = false): TransitionGroupProps | undefined {
13
+ if (!transition)
14
+ return undefined
15
+ if (typeof transition === 'string') {
16
+ transition = {
17
+ name: transition,
18
+ }
19
+ }
20
+
21
+ if (!transition.name)
22
+ return undefined
23
+
24
+ let name = transition.name.includes('|')
25
+ ? transition.name
26
+ : (transitionResolveMap[transition.name] || transition.name)
27
+
28
+ if (name.includes('|')) {
29
+ const [forward, backward] = name.split('|').map(i => i.trim())
30
+ name = isBackward ? backward : forward
31
+ }
32
+
33
+ if (!name)
34
+ return undefined
35
+
36
+ return {
37
+ ...transition,
38
+ name,
39
+ }
40
+ }
41
+
42
+ export function getCurrentTransition(direction: number, currentRoute?: SlideRoute, prevRoute?: SlideRoute) {
43
+ let transition = direction > 0
44
+ ? prevRoute?.meta?.transition
45
+ : currentRoute?.meta?.transition
46
+ if (!transition)
47
+ transition = configs.transition
48
+
49
+ return resolveTransition(transition, direction < 0)
50
+ }
package/main.ts CHANGED
@@ -1,17 +1,21 @@
1
+ /// <reference types="@slidev/types/client" />
2
+
1
3
  import { createApp } from 'vue'
2
4
  import { createHead } from '@unhead/vue'
3
5
  import App from './App.vue'
4
6
  import setupMain from './setup/main'
5
7
  import { router } from './routes'
6
- import createDirectives from './modules/directives'
7
- import createSlidevContext from './modules/context'
8
+ import { createVClickDirectives } from './modules/v-click'
9
+ import { createVMarkDirective } from './modules/v-mark'
10
+ import { createSlidevContext } from './modules/context'
8
11
 
9
- import '/@slidev/styles'
12
+ import '#slidev/styles'
10
13
 
11
14
  const app = createApp(App)
12
15
  app.use(router)
13
16
  app.use(createHead())
14
- app.use(createDirectives())
17
+ app.use(createVClickDirectives())
18
+ app.use(createVMarkDirective())
15
19
  app.use(createSlidevContext())
16
20
 
17
21
  setupMain({ app, router })
@@ -1,19 +1,12 @@
1
1
  import type { App } from 'vue'
2
- import { computed, reactive } from 'vue'
3
- import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
2
+ import { computed, reactive, ref } from 'vue'
4
3
  import type { ComputedRef } from '@vue/reactivity'
5
4
  import type { configs } from '../env'
6
5
  import * as nav from '../logic/nav'
7
- import { route } from '../logic/nav'
8
6
  import { isDark } from '../logic/dark'
9
- import { injectionCurrentPage, injectionSlidevContext } from '../constants'
7
+ import { injectionCurrentPage, injectionRenderContext, injectionSlidevContext } from '../constants'
10
8
  import { useContext } from '../composables/useContext'
11
-
12
- export type SlidevContextNavKey = 'path' | 'total' | 'clicksContext' | 'clicks' | 'clicksTotal' | 'currentPage' | 'currentPath' | 'currentRoute' | 'currentSlideId' | 'currentLayout' | 'nextRoute' | 'rawTree' | 'treeWithActiveStatuses' | 'tree' | 'downloadPDF' | 'next' | 'nextSlide' | 'openInEditor' | 'prev' | 'prevSlide' | 'rawRoutes' | 'go'
13
-
14
- export interface SlidevContextNav extends Pick<typeof nav, SlidevContextNavKey> {
15
- route: ComputedRef<RouteRecordRaw | RouteLocationNormalizedLoaded>
16
- }
9
+ import type { SlidevContextNav } from '../composables/useNav'
17
10
 
18
11
  export interface SlidevContext {
19
12
  nav: SlidevContextNav
@@ -21,12 +14,13 @@ export interface SlidevContext {
21
14
  themeConfigs: ComputedRef<typeof configs['themeConfig']>
22
15
  }
23
16
 
24
- export default function createSlidevContext() {
17
+ export function createSlidevContext() {
25
18
  return {
26
19
  install(app: App) {
27
- const context = reactive(useContext(route))
20
+ const context = reactive(useContext())
21
+ app.provide(injectionRenderContext, ref('none'))
28
22
  app.provide(injectionSlidevContext, context)
29
- app.provide(injectionCurrentPage, computed(() => context.nav.currentPage))
23
+ app.provide(injectionCurrentPage, computed(() => context.nav.currentSlideNo))
30
24
 
31
25
  // allows controls from postMessages
32
26
  if (__DEV__) {