@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
@@ -96,7 +96,7 @@ export default defineComponent({
96
96
  if (depth < +this.depth && Array.isArray(i.children))
97
97
  vNode = h(i, {}, mapSubList(i.children, depth))
98
98
  else
99
- vNode = i
99
+ vNode = h(i)
100
100
 
101
101
  const delta = thisShowIdx - execIdx
102
102
  execIdx = thisShowIdx
@@ -1,12 +1,13 @@
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
- import { ref, shallowReactive } from 'vue'
5
- import type { RouteRecordRaw } from 'vue-router'
6
- import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
4
+ import { computed, ref, shallowReactive } from 'vue'
5
+ import { currentSlideNo, isPrintMode, isPrintWithClicks } from '../logic/nav'
7
6
  import { normalizeAtProp } from '../logic/utils'
7
+ import { CLICKS_MAX } from '../constants'
8
+ import { routeForceRefresh, useRouteQuery } from '../logic/route'
8
9
 
9
- function useClicksContextBase(route: RouteRecordRaw | undefined, getCurrent: () => number): ClicksContext {
10
+ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): ClicksContext {
10
11
  const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
11
12
  const map: ClicksContext['map'] = shallowReactive(new Map())
12
13
 
@@ -15,7 +16,10 @@ function useClicksContextBase(route: RouteRecordRaw | undefined, getCurrent: ()
15
16
  return isPrintMode.value && !isPrintWithClicks.value
16
17
  },
17
18
  get current() {
18
- return getCurrent()
19
+ return current.value
20
+ },
21
+ set current(value) {
22
+ current.value = value
19
23
  },
20
24
  relativeOffsets,
21
25
  map,
@@ -53,31 +57,46 @@ function useClicksContextBase(route: RouteRecordRaw | undefined, getCurrent: ()
53
57
  get total() {
54
58
  // eslint-disable-next-line no-unused-expressions
55
59
  routeForceRefresh.value
56
- return route?.meta?.clicks
60
+ return clicksOverrides
57
61
  ?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
58
62
  },
59
63
  }
60
64
  }
61
65
 
62
- export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksContext {
66
+ const queryClicksRaw = useRouteQuery('clicks', '0')
67
+
68
+ export function usePrimaryClicks(route: SlideRoute): ClicksContext {
63
69
  if (route?.meta?.__clicksContext)
64
70
  return route.meta.__clicksContext
65
- const thisPath = +(route?.path ?? 99999)
66
- const context = useClicksContextBase(route, () => {
67
- const currentPath = +(currentRoute.value?.path ?? 99999)
68
- if (currentPath === thisPath)
69
- return queryClicks.value
70
- else if (currentPath > thisPath)
71
- return 99999
72
- else
73
- return 0
71
+ const thisNo = route.no
72
+ const current = computed({
73
+ get() {
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)
80
+ return CLICKS_MAX
81
+ else
82
+ return 0
83
+ },
84
+ set(v) {
85
+ if (currentSlideNo.value === thisNo) {
86
+ // eslint-disable-next-line ts/no-use-before-define
87
+ queryClicksRaw.value = Math.min(v, context.total).toString()
88
+ }
89
+ },
74
90
  })
91
+ const context = useClicksContextBase(
92
+ current,
93
+ route?.meta?.clicks,
94
+ )
75
95
  if (route?.meta)
76
96
  route.meta.__clicksContext = context
77
97
  return context
78
98
  }
79
99
 
80
- export function useFixedClicks(route: RouteRecordRaw | undefined, currentInit = 0): [Ref<number>, ClicksContext] {
81
- const current = ref(currentInit)
82
- return [current, useClicksContextBase(route, () => current.value)]
100
+ export function useFixedClicks(route?: SlideRoute | undefined, currentInit = 0): ClicksContext {
101
+ return useClicksContextBase(ref(currentInit), route?.meta?.clicks)
83
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,16 +1,17 @@
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
- export const injectionClicksContext: InjectionKey<Ref<ClicksContext>> = Symbol('slidev-clicks-context')
7
- export const injectionCurrentPage: InjectionKey<Ref<number>> = Symbol('slidev-page')
8
- export const injectionSlideScale: InjectionKey<ComputedRef<number>> = Symbol('slidev-slide-scale')
9
- export const injectionSlidevContext: InjectionKey<UnwrapNestedRefs<SlidevContext>> = Symbol('slidev-slidev-context')
10
- export const injectionRoute: InjectionKey<RouteRecordRaw> = Symbol('slidev-route')
11
- export const injectionRenderContext: InjectionKey<Ref<RenderContext>> = Symbol('slidev-render-context')
12
- export const injectionActive: InjectionKey<Ref<boolean>> = Symbol('slidev-active')
13
- export const injectionFrontmatter: InjectionKey<Record<string, any>> = Symbol('slidev-fontmatter')
5
+ // Here we use string literal instead of symbols to make HMR more stable
6
+ // The value of the injections keys are implementation details, you should always use them with the reference to the constant instead of the value
7
+ export const injectionClicksContext = '$$slidev-clicks-context' as unknown as InjectionKey<Ref<ClicksContext>>
8
+ export const injectionCurrentPage = '$$slidev-page' as unknown as InjectionKey<Ref<number>>
9
+ export const injectionSlideScale = '$$slidev-slide-scale' as unknown as InjectionKey<ComputedRef<number>>
10
+ export const injectionSlidevContext = '$$slidev-context' as unknown as InjectionKey<UnwrapNestedRefs<SlidevContext>>
11
+ export const injectionRoute = '$$slidev-route' as unknown as InjectionKey<SlideRoute>
12
+ export const injectionRenderContext = '$$slidev-render-context' as unknown as InjectionKey<Ref<RenderContext>>
13
+ export const injectionActive = '$$slidev-active' as unknown as InjectionKey<Ref<boolean>>
14
+ export const injectionFrontmatter = '$$slidev-fontmatter' as unknown as InjectionKey<Record<string, any>>
14
15
 
15
16
  export const CLASS_VCLICK_TARGET = 'slidev-vclick-target'
16
17
  export const CLASS_VCLICK_HIDDEN = 'slidev-vclick-hidden'
@@ -20,7 +21,55 @@ export const CLASS_VCLICK_HIDDEN_EXP = 'slidev-vclick-hidden-explicitly'
20
21
  export const CLASS_VCLICK_CURRENT = 'slidev-vclick-current'
21
22
  export const CLASS_VCLICK_PRIOR = 'slidev-vclick-prior'
22
23
 
24
+ export const CLICKS_MAX = 999999
25
+
23
26
  export const TRUST_ORIGINS = [
24
27
  'localhost',
25
28
  '127.0.0.1',
26
29
  ]
30
+
31
+ export const FRONTMATTER_FIELDS = [
32
+ 'clicks',
33
+ 'disabled',
34
+ 'hide',
35
+ 'hideInToc',
36
+ 'layout',
37
+ 'level',
38
+ 'preload',
39
+ 'routeAlias',
40
+ 'src',
41
+ 'title',
42
+ 'transition',
43
+ 'zoom',
44
+ ]
45
+
46
+ export const HEADMATTER_FIELDS = [
47
+ ...FRONTMATTER_FIELDS,
48
+ 'theme',
49
+ 'titleTemplate',
50
+ 'info',
51
+ 'author',
52
+ 'keywords',
53
+ 'presenter',
54
+ 'download',
55
+ 'exportFilename',
56
+ 'export',
57
+ 'highlighter',
58
+ 'lineNumbers',
59
+ 'monaco',
60
+ 'remoteAssets',
61
+ 'selectable',
62
+ 'record',
63
+ 'colorSchema',
64
+ 'routerMode',
65
+ 'aspectRatio',
66
+ 'canvasWidth',
67
+ 'themeConfig',
68
+ 'favicon',
69
+ 'plantUmlServer',
70
+ 'fonts',
71
+ 'defaults',
72
+ 'drawings',
73
+ 'htmlAttrs',
74
+ 'mdc',
75
+ ]
package/context.ts ADDED
@@ -0,0 +1,73 @@
1
+ import { ref, shallowRef, toRef } from 'vue'
2
+ import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
3
+ import { useFixedClicks } from './composables/useClicks'
4
+ import {
5
+ FRONTMATTER_FIELDS,
6
+ HEADMATTER_FIELDS,
7
+ injectionClicksContext,
8
+ injectionCurrentPage,
9
+ injectionFrontmatter,
10
+ injectionRenderContext,
11
+ injectionRoute,
12
+ injectionSlideScale,
13
+ injectionSlidevContext,
14
+ } from './constants'
15
+
16
+ const clicksContextFallback = shallowRef(useFixedClicks())
17
+
18
+ /**
19
+ * Get the current slide context, should be called inside the setup function of a component inside slide
20
+ */
21
+ export function useSlideContext() {
22
+ const $slidev = injectLocal(injectionSlidevContext)!
23
+ const $nav = toRef($slidev, 'nav')
24
+ const $clicksContext = injectLocal(injectionClicksContext, clicksContextFallback)!.value
25
+ const $clicks = toRef($clicksContext, 'current')
26
+ const $page = injectLocal(injectionCurrentPage)!
27
+ const $renderContext = injectLocal(injectionRenderContext)!
28
+ const $frontmatter = injectLocal(injectionFrontmatter, {})
29
+ const $route = injectLocal(injectionRoute, undefined)
30
+ const $scale = injectLocal(injectionSlideScale, ref(1))!
31
+
32
+ return {
33
+ $slidev,
34
+ $nav,
35
+ $clicksContext,
36
+ $clicks,
37
+ $page,
38
+ $route,
39
+ $renderContext,
40
+ $frontmatter,
41
+ $scale,
42
+ }
43
+ }
44
+
45
+ export function provideFrontmatter(frontmatter: Record<string, any>) {
46
+ provideLocal(injectionFrontmatter, frontmatter)
47
+
48
+ const {
49
+ $slidev,
50
+ $page,
51
+ } = useSlideContext()
52
+
53
+ // update frontmatter in router to make HMR work better
54
+ const route = $slidev.nav.slides.find(i => i.no === $page.value)
55
+ if (route?.meta?.slide?.frontmatter) {
56
+ for (const key of Object.keys(route.meta.slide.frontmatter)) {
57
+ if (!(key in frontmatter))
58
+ delete route.meta.slide.frontmatter[key]
59
+ }
60
+ Object.assign(route.meta.slide.frontmatter, frontmatter)
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Convert frontmatter options to props for v-bind
66
+ * It removes known options fields, and expose an extra `frontmatter` field that contains full frontmatter
67
+ */
68
+ export function frontmatterToProps(frontmatter: Record<string, any>, pageNo: number) {
69
+ return {
70
+ ...objectOmit(frontmatter, pageNo === 0 ? HEADMATTER_FIELDS : FRONTMATTER_FIELDS),
71
+ frontmatter,
72
+ }
73
+ }
package/env.ts CHANGED
@@ -1,15 +1,12 @@
1
- import type { SlidevConfig } from '@slidev/types'
2
- import type { UnwrapNestedRefs } from 'vue'
3
1
  import { computed } from 'vue'
4
2
  import { objectMap } from '@antfu/utils'
3
+ import configs from '#slidev/configs'
5
4
 
6
- // @ts-expect-error missing types
7
- import _configs from '/@slidev/configs'
8
- import type { SlidevContext } from './modules/context'
5
+ export { configs }
9
6
 
10
- export const configs = _configs as SlidevConfig
11
7
  export const slideAspect = configs.aspectRatio ?? (16 / 9)
12
8
  export const slideWidth = configs.canvasWidth ?? 980
9
+
13
10
  // To honor the aspect ratio more as possible, we need to approximate the height to the next integer.
14
11
  // Doing this, we will prevent on print, to create an additional empty white page after each page.
15
12
  export const slideHeight = Math.ceil(slideWidth / slideAspect)
@@ -17,9 +14,3 @@ export const slideHeight = Math.ceil(slideWidth / slideAspect)
17
14
  export const themeVars = computed(() => {
18
15
  return objectMap(configs.themeConfig || {}, (k, v) => [`--slidev-theme-${k}`, v])
19
16
  })
20
-
21
- declare module 'vue' {
22
- interface ComponentCustomProperties {
23
- $slidev: UnwrapNestedRefs<SlidevContext>
24
- }
25
- }