@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.
- package/builtin/SlidevVideo.vue +1 -1
- package/builtin/Toc.vue +1 -1
- package/builtin/TocList.vue +1 -1
- package/composables/useClicks.ts +16 -15
- 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 +2 -3
- package/context.ts +1 -1
- package/internals/Goto.vue +2 -2
- package/internals/NavControls.vue +7 -5
- package/internals/NoteStatic.vue +1 -1
- package/internals/PrintContainer.vue +4 -3
- package/internals/PrintSlide.vue +6 -13
- package/internals/PrintSlideClick.vue +9 -11
- package/internals/QuickOverview.vue +10 -10
- package/internals/SideEditor.vue +3 -3
- package/internals/SlideLoading.vue +19 -0
- package/internals/SlideWrapper.ts +12 -12
- package/internals/SlidesShow.vue +13 -10
- package/layouts/error.vue +5 -0
- package/logic/drawings.ts +7 -7
- package/logic/nav-state.ts +20 -0
- package/logic/nav.ts +49 -259
- package/logic/note.ts +2 -2
- 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/modules/context.ts +3 -10
- package/package.json +5 -4
- package/pages/notes.vue +2 -3
- package/pages/overview.vue +19 -21
- package/pages/play.vue +2 -1
- package/pages/presenter/print.vue +2 -2
- package/pages/presenter.vue +15 -14
- package/routes.ts +6 -14
- package/setup/root.ts +6 -7
- package/setup/shortcuts.ts +2 -1
- package/shim-vue.d.ts +3 -0
- package/utils.ts +15 -2
package/logic/nav.ts
CHANGED
|
@@ -1,44 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
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'
|
|
10
4
|
import { CLICKS_MAX } from '../constants'
|
|
5
|
+
import { useNavBase } from '../composables/useNav'
|
|
11
6
|
import { useRouteQuery } from './route'
|
|
12
|
-
import {
|
|
7
|
+
import { currentRoute, currentSlideRoute, hasPrimarySlide } from './nav-state'
|
|
8
|
+
import { getSlide } from './slides'
|
|
13
9
|
|
|
14
|
-
export
|
|
10
|
+
export * from './slides'
|
|
11
|
+
export * from './nav-state'
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
export const routeForceRefresh = ref(0)
|
|
18
|
-
nextTick(() => {
|
|
19
|
-
router.afterEach(async () => {
|
|
20
|
-
await nextTick()
|
|
21
|
-
routeForceRefresh.value += 1
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
export const navDirection = ref(0)
|
|
26
|
-
export const clicksDirection = ref(0)
|
|
27
|
-
|
|
28
|
-
export const route = computed(() => router.currentRoute.value)
|
|
29
|
-
|
|
30
|
-
export const isPrintMode = computed(() => route.value.query.print !== undefined)
|
|
31
|
-
export const isPrintWithClicks = computed(() => route.value.query.print === 'clicks')
|
|
32
|
-
export const isEmbedded = computed(() => route.value.query.embedded !== undefined)
|
|
33
|
-
export const isPresenter = computed(() => route.value.path.startsWith('/presenter'))
|
|
34
|
-
export const isNotesViewer = computed(() => route.value.path.startsWith('/notes'))
|
|
35
|
-
export const presenterPassword = computed(() => route.value.query.password)
|
|
36
|
-
export const showPresenter = computed(() => !isPresenter.value && (!configs.remote || presenterPassword.value === configs.remote))
|
|
13
|
+
export const clicksContext = computed(() => usePrimaryClicks(currentSlideRoute.value))
|
|
37
14
|
|
|
38
15
|
const queryClicksRaw = useRouteQuery('clicks', '0')
|
|
39
16
|
export const queryClicks = computed({
|
|
40
17
|
get() {
|
|
41
|
-
// eslint-disable-next-line ts/no-use-before-define
|
|
42
18
|
if (clicksContext.value.disabled)
|
|
43
19
|
return CLICKS_MAX
|
|
44
20
|
let v = +(queryClicksRaw.value || 0)
|
|
@@ -51,231 +27,45 @@ export const queryClicks = computed({
|
|
|
51
27
|
},
|
|
52
28
|
})
|
|
53
29
|
|
|
54
|
-
export const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
else
|
|
93
|
-
queryClicks.value += 1
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function prev() {
|
|
97
|
-
clicksDirection.value = -1
|
|
98
|
-
if (queryClicks.value <= 0)
|
|
99
|
-
await prevSlide()
|
|
100
|
-
else
|
|
101
|
-
queryClicks.value -= 1
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function getPath(no: number | string) {
|
|
105
|
-
return isPresenter.value ? `/presenter/${no}` : `/${no}`
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export async function nextSlide() {
|
|
109
|
-
clicksDirection.value = 1
|
|
110
|
-
if (currentPage.value < rawRoutes.length)
|
|
111
|
-
await go(currentPage.value + 1)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export async function prevSlide(lastClicks = true) {
|
|
115
|
-
clicksDirection.value = -1
|
|
116
|
-
const next = Math.max(1, currentPage.value - 1)
|
|
117
|
-
await go(next)
|
|
118
|
-
if (lastClicks && clicksTotal.value)
|
|
119
|
-
router.replace({ query: { ...route.value.query, clicks: clicksTotal.value } })
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function goFirst() {
|
|
123
|
-
return go(1)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function goLast() {
|
|
127
|
-
return go(total.value)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function go(page: number | string, clicks?: number) {
|
|
131
|
-
skipTransition.value = false
|
|
132
|
-
return router.push({ path: getPath(page), query: { ...route.value.query, clicks } })
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function useSwipeControls(root: Ref<HTMLElement | undefined>) {
|
|
136
|
-
const swipeBegin = ref(0)
|
|
137
|
-
const { direction, distanceX, distanceY } = usePointerSwipe(root, {
|
|
138
|
-
pointerTypes: ['touch'],
|
|
139
|
-
onSwipeStart() {
|
|
140
|
-
if (isDrawing.value)
|
|
141
|
-
return
|
|
142
|
-
swipeBegin.value = timestamp()
|
|
143
|
-
},
|
|
144
|
-
onSwipeEnd() {
|
|
145
|
-
if (!swipeBegin.value)
|
|
146
|
-
return
|
|
147
|
-
if (isDrawing.value)
|
|
148
|
-
return
|
|
149
|
-
|
|
150
|
-
const x = Math.abs(distanceX.value)
|
|
151
|
-
const y = Math.abs(distanceY.value)
|
|
152
|
-
if (x / window.innerWidth > 0.3 || x > 75) {
|
|
153
|
-
if (direction.value === 'left')
|
|
154
|
-
next()
|
|
155
|
-
else
|
|
156
|
-
prev()
|
|
157
|
-
}
|
|
158
|
-
else if (y / window.innerHeight > 0.4 || y > 200) {
|
|
159
|
-
if (direction.value === 'down')
|
|
160
|
-
prevSlide()
|
|
161
|
-
else
|
|
162
|
-
nextSlide()
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export async function downloadPDF() {
|
|
169
|
-
const { saveAs } = await import('file-saver')
|
|
170
|
-
saveAs(
|
|
171
|
-
typeof configs.download === 'string'
|
|
172
|
-
? configs.download
|
|
173
|
-
: configs.exportFilename
|
|
174
|
-
? `${configs.exportFilename}.pdf`
|
|
175
|
-
: `${import.meta.env.BASE_URL}slidev-exported.pdf`,
|
|
176
|
-
`${configs.title}.pdf`,
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export async function openInEditor(url?: string) {
|
|
181
|
-
if (url == null) {
|
|
182
|
-
const slide = currentRoute.value?.meta?.slide
|
|
183
|
-
if (!slide)
|
|
184
|
-
return false
|
|
185
|
-
url = `${slide.filepath}:${slide.start}`
|
|
186
|
-
}
|
|
187
|
-
await fetch(`/__open-in-editor?file=${encodeURIComponent(url)}`)
|
|
188
|
-
return true
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function addToTree(tree: TocItem[], route: RouteRecordRaw, level = 1) {
|
|
192
|
-
const titleLevel = route.meta?.slide?.level
|
|
193
|
-
if (titleLevel && titleLevel > level && tree.length > 0) {
|
|
194
|
-
addToTree(tree[tree.length - 1].children, route, level + 1)
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
tree.push({
|
|
198
|
-
children: [],
|
|
199
|
-
level,
|
|
200
|
-
path: route.path,
|
|
201
|
-
hideInToc: Boolean(route.meta?.slide?.frontmatter?.hideInToc),
|
|
202
|
-
title: route.meta?.slide?.title,
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function getTreeWithActiveStatuses(
|
|
208
|
-
tree: TocItem[],
|
|
209
|
-
currentRoute?: RouteRecordRaw,
|
|
210
|
-
hasActiveParent = false,
|
|
211
|
-
parent?: TocItem,
|
|
212
|
-
): TocItem[] {
|
|
213
|
-
return tree.map((item: TocItem) => {
|
|
214
|
-
const clone = {
|
|
215
|
-
...item,
|
|
216
|
-
active: item.path === currentRoute?.path,
|
|
217
|
-
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()
|
|
218
68
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
parent.activeParent = true
|
|
223
|
-
return clone
|
|
224
|
-
})
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function filterTree(tree: TocItem[], level = 1): TocItem[] {
|
|
228
|
-
return tree
|
|
229
|
-
.filter((item: TocItem) => !item.hideInToc)
|
|
230
|
-
.map((item: TocItem) => ({
|
|
231
|
-
...item,
|
|
232
|
-
children: filterTree(item.children, level + 1),
|
|
233
|
-
}))
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const transitionResolveMap: Record<string, string | undefined> = {
|
|
237
|
-
'slide-left': 'slide-left | slide-right',
|
|
238
|
-
'slide-right': 'slide-right | slide-left',
|
|
239
|
-
'slide-up': 'slide-up | slide-down',
|
|
240
|
-
'slide-down': 'slide-down | slide-up',
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function resolveTransition(transition?: string | TransitionGroupProps, isBackward = false): TransitionGroupProps | undefined {
|
|
244
|
-
if (!transition)
|
|
245
|
-
return undefined
|
|
246
|
-
if (typeof transition === 'string') {
|
|
247
|
-
transition = {
|
|
248
|
-
name: transition,
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (!transition.name)
|
|
253
|
-
return undefined
|
|
254
|
-
|
|
255
|
-
let name = transition.name.includes('|')
|
|
256
|
-
? transition.name
|
|
257
|
-
: (transitionResolveMap[transition.name] || transition.name)
|
|
258
|
-
|
|
259
|
-
if (name.includes('|')) {
|
|
260
|
-
const [forward, backward] = name.split('|').map(i => i.trim())
|
|
261
|
-
name = isBackward ? backward : forward
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (!name)
|
|
265
|
-
return undefined
|
|
266
|
-
|
|
267
|
-
return {
|
|
268
|
-
...transition,
|
|
269
|
-
name,
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
export function getCurrentTransition(direction: number, currentRoute?: RouteRecordRaw, prevRoute?: RouteRecordRaw) {
|
|
274
|
-
let transition = direction > 0
|
|
275
|
-
? prevRoute?.meta?.transition
|
|
276
|
-
: currentRoute?.meta?.transition
|
|
277
|
-
if (!transition)
|
|
278
|
-
transition = configs.transition
|
|
279
|
-
|
|
280
|
-
return resolveTransition(transition, direction < 0)
|
|
281
|
-
}
|
|
69
|
+
},
|
|
70
|
+
{ flush: 'post', immediate: true },
|
|
71
|
+
)
|
package/logic/note.ts
CHANGED
|
@@ -36,11 +36,11 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (__DEV__) {
|
|
39
|
-
import.meta.hot?.on('slidev-
|
|
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
|
|
43
|
+
import.meta.hot?.on('slidev:update-note', (payload) => {
|
|
44
44
|
if (payload.id === id && info.value.note?.trim() !== payload.note?.trim())
|
|
45
45
|
info.value = { ...info.value, ...payload }
|
|
46
46
|
})
|
package/logic/overview.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { computed, ref } from 'vue'
|
|
2
|
-
import {
|
|
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(() =>
|
|
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
|
+
})
|
package/logic/slides.ts
ADDED
|
@@ -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/modules/context.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import type { App } from 'vue'
|
|
2
2
|
import { computed, reactive, ref } from 'vue'
|
|
3
|
-
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
|
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
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
|
|
@@ -24,10 +17,10 @@ export interface SlidevContext {
|
|
|
24
17
|
export function createSlidevContext() {
|
|
25
18
|
return {
|
|
26
19
|
install(app: App) {
|
|
27
|
-
const context = reactive(useContext(
|
|
20
|
+
const context = reactive(useContext())
|
|
28
21
|
app.provide(injectionRenderContext, ref('none'))
|
|
29
22
|
app.provide(injectionSlidevContext, context)
|
|
30
|
-
app.provide(injectionCurrentPage, computed(() => context.nav.
|
|
23
|
+
app.provide(injectionCurrentPage, computed(() => context.nav.currentSlideNo))
|
|
31
24
|
|
|
32
25
|
// allows controls from postMessages
|
|
33
26
|
if (__DEV__) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.48.0-beta.
|
|
4
|
+
"version": "0.48.0-beta.21",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@antfu/utils": "^0.7.7",
|
|
30
30
|
"@iconify-json/carbon": "^1.1.30",
|
|
31
31
|
"@iconify-json/ph": "^1.1.11",
|
|
32
|
+
"@iconify-json/svg-spinners": "^1.1.2",
|
|
32
33
|
"@shikijs/monaco": "^1.1.7",
|
|
33
34
|
"@shikijs/vitepress-twoslash": "^1.1.7",
|
|
34
35
|
"@slidev/rough-notation": "^0.1.0",
|
|
@@ -54,10 +55,10 @@
|
|
|
54
55
|
"shiki-magic-move": "^0.1.0",
|
|
55
56
|
"typescript": "^5.3.3",
|
|
56
57
|
"unocss": "^0.58.5",
|
|
57
|
-
"vue": "^3.4.
|
|
58
|
+
"vue": "^3.4.21",
|
|
58
59
|
"vue-router": "^4.3.0",
|
|
59
|
-
"@slidev/types": "0.48.0-beta.
|
|
60
|
-
"@slidev/parser": "0.48.0-beta.
|
|
60
|
+
"@slidev/types": "0.48.0-beta.21",
|
|
61
|
+
"@slidev/parser": "0.48.0-beta.21"
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"vite": "^5.1.4"
|
package/pages/notes.vue
CHANGED
|
@@ -5,8 +5,7 @@ import { useLocalStorage } from '@vueuse/core'
|
|
|
5
5
|
import { configs } from '../env'
|
|
6
6
|
import { sharedState } from '../state/shared'
|
|
7
7
|
import { fullscreen } from '../state'
|
|
8
|
-
import { total } from '../logic/nav'
|
|
9
|
-
import { rawRoutes } from '../routes'
|
|
8
|
+
import { slides, total } from '../logic/nav'
|
|
10
9
|
import NoteDisplay from '../internals/NoteDisplay.vue'
|
|
11
10
|
import IconButton from '../internals/IconButton.vue'
|
|
12
11
|
|
|
@@ -20,7 +19,7 @@ const { isFullscreen, toggle: toggleFullscreen } = fullscreen
|
|
|
20
19
|
const scroller = ref<HTMLDivElement>()
|
|
21
20
|
const fontSize = useLocalStorage('slidev-notes-font-size', 18)
|
|
22
21
|
const pageNo = computed(() => sharedState.lastUpdate?.type === 'viewer' ? sharedState.viewerPage : sharedState.page)
|
|
23
|
-
const currentRoute = computed(() =>
|
|
22
|
+
const currentRoute = computed(() => slides.value.find(i => i.no === pageNo.value))
|
|
24
23
|
|
|
25
24
|
watch(pageNo, () => {
|
|
26
25
|
scroller.value?.scrollTo({ left: 0, top: 0, behavior: 'smooth' })
|
package/pages/overview.vue
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
|
3
3
|
import { useHead } from '@unhead/vue'
|
|
4
|
-
import type {
|
|
5
|
-
import type { ClicksContext } from 'packages/types'
|
|
4
|
+
import type { ClicksContext, SlideRoute } from '@slidev/types'
|
|
6
5
|
import { configs } from '../env'
|
|
7
|
-
import { openInEditor,
|
|
6
|
+
import { getSlidePath, openInEditor, slides } from '../logic/nav'
|
|
8
7
|
import { useFixedClicks } from '../composables/useClicks'
|
|
9
8
|
import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
|
|
10
9
|
import { getSlideClass } from '../utils'
|
|
@@ -26,19 +25,19 @@ useHead({
|
|
|
26
25
|
const blocks: Map<number, HTMLElement> = reactive(new Map())
|
|
27
26
|
const activeBlocks = ref<number[]>([])
|
|
28
27
|
const edittingNote = ref<number | null>(null)
|
|
29
|
-
const wordCounts = computed(() =>
|
|
28
|
+
const wordCounts = computed(() => slides.value.map(route => wordCount(route.meta?.slide?.note || '')))
|
|
30
29
|
const totalWords = computed(() => wordCounts.value.reduce((a, b) => a + b, 0))
|
|
31
|
-
const totalClicks = computed(() =>
|
|
30
|
+
const totalClicks = computed(() => slides.value.map(route => getSlideClicks(route)).reduce((a, b) => a + b, 0))
|
|
32
31
|
|
|
33
|
-
const clicksContextMap = new WeakMap<
|
|
34
|
-
function getClicksContext(route:
|
|
32
|
+
const clicksContextMap = new WeakMap<SlideRoute, ClicksContext>()
|
|
33
|
+
function getClicksContext(route: SlideRoute) {
|
|
35
34
|
// We create a local clicks context to calculate the total clicks of the slide
|
|
36
35
|
if (!clicksContextMap.has(route))
|
|
37
36
|
clicksContextMap.set(route, useFixedClicks(route, CLICKS_MAX))
|
|
38
37
|
return clicksContextMap.get(route)!
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
function getSlideClicks(route:
|
|
40
|
+
function getSlideClicks(route: SlideRoute) {
|
|
42
41
|
return route.meta?.clicks || getClicksContext(route)?.total
|
|
43
42
|
}
|
|
44
43
|
|
|
@@ -80,7 +79,7 @@ function scrollToSlide(idx: number) {
|
|
|
80
79
|
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
function onMarkerClick(e: MouseEvent, clicks: number, route:
|
|
82
|
+
function onMarkerClick(e: MouseEvent, clicks: number, route: SlideRoute) {
|
|
84
83
|
const ctx = getClicksContext(route)
|
|
85
84
|
if (ctx.current === clicks)
|
|
86
85
|
ctx.current = CLICKS_MAX
|
|
@@ -101,8 +100,8 @@ onMounted(() => {
|
|
|
101
100
|
<nav class="h-full flex flex-col border-r border-main p2 select-none">
|
|
102
101
|
<div class="flex flex-col flex-auto items-center justify-center group gap-1">
|
|
103
102
|
<div
|
|
104
|
-
v-for="(route, idx) of
|
|
105
|
-
:key="route.
|
|
103
|
+
v-for="(route, idx) of slides"
|
|
104
|
+
:key="route.no"
|
|
106
105
|
class="relative"
|
|
107
106
|
>
|
|
108
107
|
<button
|
|
@@ -136,8 +135,8 @@ onMounted(() => {
|
|
|
136
135
|
@scroll="checkActiveBlocks"
|
|
137
136
|
>
|
|
138
137
|
<div
|
|
139
|
-
v-for="(route, idx) of
|
|
140
|
-
:key="route.
|
|
138
|
+
v-for="(route, idx) of slides"
|
|
139
|
+
:key="route.no"
|
|
141
140
|
:ref="el => blocks.set(idx, el as any)"
|
|
142
141
|
class="relative border-t border-main of-hidden flex gap-4 min-h-50 group"
|
|
143
142
|
>
|
|
@@ -148,7 +147,7 @@ onMounted(() => {
|
|
|
148
147
|
<IconButton
|
|
149
148
|
class="mr--3 op0 group-hover:op80"
|
|
150
149
|
title="Play in new tab"
|
|
151
|
-
@click="openSlideInNewTab(route
|
|
150
|
+
@click="openSlideInNewTab(getSlidePath(route, false))"
|
|
152
151
|
>
|
|
153
152
|
<carbon:presentation-file />
|
|
154
153
|
</IconButton>
|
|
@@ -164,23 +163,22 @@ onMounted(() => {
|
|
|
164
163
|
<div class="flex flex-col gap-2 my5">
|
|
165
164
|
<div
|
|
166
165
|
class="border rounded border-main overflow-hidden bg-main select-none h-max"
|
|
167
|
-
@dblclick="openSlideInNewTab(route
|
|
166
|
+
@dblclick="openSlideInNewTab(getSlidePath(route, false))"
|
|
168
167
|
>
|
|
169
168
|
<SlideContainer
|
|
170
|
-
:key="route.
|
|
169
|
+
:key="route.no"
|
|
171
170
|
:width="cardWidth"
|
|
172
171
|
:clicks-disabled="true"
|
|
173
172
|
class="pointer-events-none important:[&_*]:select-none"
|
|
174
173
|
>
|
|
175
174
|
<SlideWrapper
|
|
176
|
-
:is="route.component"
|
|
177
|
-
v-if="route?.component"
|
|
175
|
+
:is="route.component!"
|
|
178
176
|
:clicks-context="getClicksContext(route)"
|
|
179
177
|
:class="getSlideClass(route)"
|
|
180
178
|
:route="route"
|
|
181
179
|
render-context="overview"
|
|
182
180
|
/>
|
|
183
|
-
<DrawingPreview :page="
|
|
181
|
+
<DrawingPreview :page="route.no" />
|
|
184
182
|
</SlideContainer>
|
|
185
183
|
</div>
|
|
186
184
|
<ClicksSlider
|
|
@@ -220,8 +218,8 @@ onMounted(() => {
|
|
|
220
218
|
</main>
|
|
221
219
|
<div class="absolute top-0 right-0 px3 py1.5 border-b border-l rounded-lb bg-main border-main select-none">
|
|
222
220
|
<div class="text-xs op50">
|
|
223
|
-
{{
|
|
224
|
-
{{ totalClicks +
|
|
221
|
+
{{ slides.length }} slides ·
|
|
222
|
+
{{ totalClicks + slides.length - 1 }} clicks ·
|
|
225
223
|
{{ totalWords }} words
|
|
226
224
|
</div>
|
|
227
225
|
</div>
|
package/pages/play.vue
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, shallowRef } from 'vue'
|
|
3
3
|
import { isEditorVertical, isScreenVertical, showEditor, slideScale, windowSize } from '../state'
|
|
4
|
-
import { isEmbedded, isPrintMode, next, prev
|
|
4
|
+
import { isEmbedded, isPrintMode, next, prev } from '../logic/nav'
|
|
5
|
+
import { useSwipeControls } from '../composables/useSwipeControls'
|
|
5
6
|
import { isDrawing } from '../logic/drawings'
|
|
6
7
|
import { registerShortcuts } from '../logic/shortcuts'
|
|
7
8
|
import { configs } from '../env'
|
|
@@ -3,7 +3,7 @@ import { computed } from 'vue'
|
|
|
3
3
|
import { useStyleTag } from '@vueuse/core'
|
|
4
4
|
import { useHead } from '@unhead/vue'
|
|
5
5
|
import { configs } from '../../env'
|
|
6
|
-
import {
|
|
6
|
+
import { slides, total } from '../../logic/nav'
|
|
7
7
|
import NoteDisplay from '../../internals/NoteDisplay.vue'
|
|
8
8
|
|
|
9
9
|
useStyleTag(`
|
|
@@ -28,7 +28,7 @@ useHead({
|
|
|
28
28
|
title: `Notes - ${configs.title}`,
|
|
29
29
|
})
|
|
30
30
|
|
|
31
|
-
const slidesWithNote = computed(() =>
|
|
31
|
+
const slidesWithNote = computed(() => slides.value
|
|
32
32
|
.map(route => route.meta?.slide)
|
|
33
33
|
.filter(slide => slide !== undefined && slide.noteHTML !== ''))
|
|
34
34
|
</script>
|