@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.
- package/App.vue +7 -0
- package/builtin/Arrow.vue +2 -4
- package/builtin/CodeBlockWrapper.vue +14 -6
- package/builtin/KaTexBlockWrapper.vue +5 -4
- package/builtin/Mermaid.vue +4 -3
- package/builtin/Monaco.vue +109 -92
- package/builtin/RenderWhen.vue +3 -3
- package/builtin/ShikiMagicMove.vue +50 -0
- package/builtin/SlideCurrentNo.vue +2 -3
- package/builtin/SlidesTotal.vue +3 -4
- package/builtin/SlidevVideo.vue +9 -7
- package/builtin/Toc.vue +4 -4
- package/builtin/TocList.vue +4 -3
- package/builtin/Tweet.vue +3 -22
- package/builtin/VClick.ts +2 -1
- package/builtin/VClickGap.vue +3 -5
- package/builtin/VClicks.ts +1 -1
- package/composables/useClicks.ts +39 -20
- package/composables/useContext.ts +4 -9
- package/composables/useNav.ts +182 -44
- package/composables/useSwipeControls.ts +40 -0
- package/composables/useTocTree.ts +63 -0
- package/constants.ts +59 -10
- package/context.ts +73 -0
- package/env.ts +3 -12
- package/internals/ClicksSlider.vue +93 -0
- package/internals/Controls.vue +2 -2
- package/internals/DrawingControls.vue +39 -9
- package/internals/DrawingLayer.vue +3 -3
- package/internals/Goto.vue +7 -6
- package/internals/IconButton.vue +7 -3
- package/internals/InfoDialog.vue +1 -1
- package/internals/Modal.vue +1 -1
- package/internals/NavControls.vue +11 -10
- package/internals/NoteDisplay.vue +131 -8
- package/internals/NoteEditable.vue +128 -0
- package/internals/NoteStatic.vue +8 -6
- package/internals/PrintContainer.vue +8 -6
- package/internals/PrintSlide.vue +10 -11
- package/internals/PrintSlideClick.vue +14 -18
- package/internals/{SlidesOverview.vue → QuickOverview.vue} +31 -20
- package/internals/RecordingControls.vue +1 -1
- package/internals/RecordingDialog.vue +5 -6
- package/internals/{Editor.vue → SideEditor.vue} +9 -5
- package/internals/SlideContainer.vue +12 -9
- package/internals/SlideLoading.vue +19 -0
- package/internals/SlideWrapper.ts +32 -16
- package/internals/SlidesShow.vue +20 -18
- package/layouts/error.vue +5 -0
- package/layouts/two-cols-header.vue +9 -3
- package/logic/drawings.ts +13 -10
- package/logic/nav-state.ts +20 -0
- package/logic/nav.ts +51 -258
- package/logic/note.ts +9 -9
- package/logic/overview.ts +2 -2
- package/logic/route.ts +10 -1
- package/logic/slides.ts +19 -0
- package/logic/transition.ts +50 -0
- package/main.ts +8 -4
- package/modules/context.ts +7 -13
- package/modules/mermaid.ts +6 -7
- package/modules/{directives.ts → v-click.ts} +15 -15
- package/modules/v-mark.ts +159 -0
- package/package.json +27 -16
- package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
- package/{internals/NotesView.vue → pages/notes.vue} +7 -6
- package/pages/overview.vue +227 -0
- package/{internals/Play.vue → pages/play.vue} +17 -13
- package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +13 -8
- package/{internals/Presenter.vue → pages/presenter.vue} +114 -105
- package/{internals/Print.vue → pages/print.vue} +3 -4
- package/routes.ts +28 -60
- package/setup/codemirror.ts +8 -3
- package/setup/monaco.ts +108 -44
- package/setup/root.ts +8 -9
- package/setup/shortcuts.ts +2 -1
- package/shim-vue.d.ts +38 -0
- package/shim.d.ts +1 -13
- package/state/index.ts +10 -10
- package/styles/code.css +7 -3
- package/styles/index.css +68 -7
- package/styles/katex.css +1 -1
- package/styles/layouts-base.css +17 -12
- package/styles/monaco.css +27 -0
- package/styles/vars.css +1 -0
- package/uno.config.ts +14 -2
- package/utils.ts +15 -2
- package/iframes/monaco/index.css +0 -28
- package/iframes/monaco/index.html +0 -7
- package/iframes/monaco/index.ts +0 -260
- package/internals/NoteEditor.vue +0 -88
package/builtin/VClicks.ts
CHANGED
package/composables/useClicks.ts
CHANGED
|
@@ -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
|
|
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(
|
|
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
|
|
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
|
|
60
|
+
return clicksOverrides
|
|
57
61
|
?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
|
|
58
62
|
},
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
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
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
|
81
|
-
|
|
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 {
|
|
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
|
}
|
package/composables/useNav.ts
CHANGED
|
@@ -1,55 +1,193 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
32
|
-
route,
|
|
33
|
-
path,
|
|
149
|
+
slides,
|
|
34
150
|
total,
|
|
35
|
-
clicksContext,
|
|
36
|
-
clicks,
|
|
37
|
-
clicksTotal,
|
|
38
|
-
currentPage,
|
|
39
151
|
currentPath,
|
|
40
|
-
|
|
41
|
-
|
|
152
|
+
currentSlideNo,
|
|
153
|
+
currentPage: currentSlideNo,
|
|
154
|
+
currentSlideRoute,
|
|
42
155
|
currentLayout,
|
|
156
|
+
currentTransition,
|
|
157
|
+
clicksDirection,
|
|
43
158
|
nextRoute,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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 {
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
12
|
-
export const
|
|
13
|
-
export const
|
|
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
|
-
|
|
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
|
-
}
|