@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
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { RouteRecordRaw } from 'vue-router'
|
|
3
2
|
import { computed, reactive, shallowRef } from 'vue'
|
|
4
|
-
import type { ClicksContext } from '@slidev/types'
|
|
5
3
|
import { provideLocal } from '@vueuse/core'
|
|
6
4
|
import { injectionSlidevContext } from '../constants'
|
|
7
5
|
import { configs, slideHeight, slideWidth } from '../env'
|
|
8
6
|
import { getSlideClass } from '../utils'
|
|
9
|
-
import type { SlidevContextNav } from '../
|
|
7
|
+
import type { SlidevContextNav } from '../composables/useNav'
|
|
10
8
|
import SlideWrapper from './SlideWrapper'
|
|
11
9
|
|
|
12
10
|
import GlobalTop from '#slidev/global-components/top'
|
|
13
11
|
import GlobalBottom from '#slidev/global-components/bottom'
|
|
14
12
|
|
|
15
|
-
const
|
|
16
|
-
clicksContext: ClicksContext
|
|
13
|
+
const { nav } = defineProps<{
|
|
17
14
|
nav: SlidevContextNav
|
|
18
|
-
route: RouteRecordRaw
|
|
19
15
|
}>()
|
|
20
16
|
|
|
17
|
+
const route = computed(() => nav.currentSlideRoute.value)
|
|
18
|
+
|
|
21
19
|
const style = computed(() => ({
|
|
22
20
|
height: `${slideHeight}px`,
|
|
23
21
|
width: `${slideWidth}px`,
|
|
@@ -28,11 +26,11 @@ if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
|
|
|
28
26
|
import('./DrawingPreview.vue').then(v => (DrawingPreview.value = v.default))
|
|
29
27
|
|
|
30
28
|
const id = computed(() =>
|
|
31
|
-
`${
|
|
29
|
+
`${route.value.no.toString().padStart(3, '0')}-${(nav.clicks.value + 1).toString().padStart(2, '0')}`,
|
|
32
30
|
)
|
|
33
31
|
|
|
34
32
|
provideLocal(injectionSlidevContext, reactive({
|
|
35
|
-
nav
|
|
33
|
+
nav,
|
|
36
34
|
configs,
|
|
37
35
|
themeConfigs: computed(() => configs.themeConfig),
|
|
38
36
|
}))
|
|
@@ -43,8 +41,8 @@ provideLocal(injectionSlidevContext, reactive({
|
|
|
43
41
|
<GlobalBottom />
|
|
44
42
|
|
|
45
43
|
<SlideWrapper
|
|
46
|
-
:is="route
|
|
47
|
-
:clicks-context="clicksContext"
|
|
44
|
+
:is="route.component!"
|
|
45
|
+
:clicks-context="nav.clicksContext.value"
|
|
48
46
|
:class="getSlideClass(route)"
|
|
49
47
|
:route="route"
|
|
50
48
|
/>
|
|
@@ -55,7 +53,7 @@ provideLocal(injectionSlidevContext, reactive({
|
|
|
55
53
|
&& DrawingPreview
|
|
56
54
|
"
|
|
57
55
|
>
|
|
58
|
-
<DrawingPreview :page="
|
|
56
|
+
<DrawingPreview :page="route.no" />
|
|
59
57
|
</template>
|
|
60
58
|
|
|
61
59
|
<GlobalTop />
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useEventListener, useVModel } from '@vueuse/core'
|
|
3
3
|
import { computed, ref, watchEffect } from 'vue'
|
|
4
4
|
import { breakpoints, showOverview, windowSize } from '../state'
|
|
5
|
-
import {
|
|
5
|
+
import { currentSlideNo, go as goSlide, slides } from '../logic/nav'
|
|
6
6
|
import { currentOverviewPage, overviewRowCount } from '../logic/overview'
|
|
7
7
|
import { useFixedClicks } from '../composables/useClicks'
|
|
8
8
|
import { getSlideClass } from '../utils'
|
|
@@ -78,17 +78,17 @@ useEventListener('keypress', (e) => {
|
|
|
78
78
|
keyboardBuffer.value += String(num)
|
|
79
79
|
|
|
80
80
|
// beyond the number of slides, reset
|
|
81
|
-
if (+keyboardBuffer.value >=
|
|
81
|
+
if (+keyboardBuffer.value >= slides.value.length) {
|
|
82
82
|
keyboardBuffer.value = ''
|
|
83
83
|
return
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const extactMatch =
|
|
86
|
+
const extactMatch = slides.value.findIndex(i => `/${i.no}` === keyboardBuffer.value)
|
|
87
87
|
if (extactMatch !== -1)
|
|
88
88
|
currentOverviewPage.value = extactMatch + 1
|
|
89
89
|
|
|
90
90
|
// When the input number is the largest at the number of digits, we go to that page directly.
|
|
91
|
-
if (+keyboardBuffer.value * 10 >
|
|
91
|
+
if (+keyboardBuffer.value * 10 > slides.value.length) {
|
|
92
92
|
go(+keyboardBuffer.value)
|
|
93
93
|
keyboardBuffer.value = ''
|
|
94
94
|
}
|
|
@@ -97,7 +97,7 @@ useEventListener('keypress', (e) => {
|
|
|
97
97
|
watchEffect(() => {
|
|
98
98
|
// Watch currentPage, make sure every time we open overview,
|
|
99
99
|
// we focus on the right page.
|
|
100
|
-
currentOverviewPage.value =
|
|
100
|
+
currentOverviewPage.value = currentSlideNo.value
|
|
101
101
|
// Watch rowCount, make sure up and down shortcut work correctly.
|
|
102
102
|
overviewRowCount.value = rowCount.value
|
|
103
103
|
})
|
|
@@ -120,17 +120,17 @@ watchEffect(() => {
|
|
|
120
120
|
:style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
|
|
121
121
|
>
|
|
122
122
|
<div
|
|
123
|
-
v-for="(route, idx) of
|
|
124
|
-
:key="route.
|
|
123
|
+
v-for="(route, idx) of slides"
|
|
124
|
+
:key="route.no"
|
|
125
125
|
class="relative"
|
|
126
126
|
>
|
|
127
127
|
<div
|
|
128
128
|
class="inline-block border rounded overflow-hidden bg-main hover:border-primary transition"
|
|
129
129
|
:class="(focus(idx + 1) || currentOverviewPage === idx + 1) ? 'border-primary' : 'border-main'"
|
|
130
|
-
@click="go(
|
|
130
|
+
@click="go(route.no)"
|
|
131
131
|
>
|
|
132
132
|
<SlideContainer
|
|
133
|
-
:key="route.
|
|
133
|
+
:key="route.no"
|
|
134
134
|
:width="cardWidth"
|
|
135
135
|
:clicks-disabled="true"
|
|
136
136
|
class="pointer-events-none"
|
|
@@ -143,7 +143,7 @@ watchEffect(() => {
|
|
|
143
143
|
:route="route"
|
|
144
144
|
render-context="overview"
|
|
145
145
|
/>
|
|
146
|
-
<DrawingPreview :page="
|
|
146
|
+
<DrawingPreview :page="route.no" />
|
|
147
147
|
</SlideContainer>
|
|
148
148
|
</div>
|
|
149
149
|
<div
|
package/internals/SideEditor.vue
CHANGED
|
@@ -3,7 +3,7 @@ import { throttledWatch, useEventListener } from '@vueuse/core'
|
|
|
3
3
|
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
|
4
4
|
import { activeElement, editorHeight, editorWidth, isInputting, showEditor, isEditorVertical as vertical } from '../state'
|
|
5
5
|
import { useCodeMirror } from '../setup/codemirror'
|
|
6
|
-
import {
|
|
6
|
+
import { currentSlideNo, openInEditor } from '../logic/nav'
|
|
7
7
|
import { useDynamicSlideInfo } from '../logic/note'
|
|
8
8
|
import IconButton from './IconButton.vue'
|
|
9
9
|
|
|
@@ -19,7 +19,7 @@ const frontmatter = ref<any>({})
|
|
|
19
19
|
const contentInput = ref<HTMLTextAreaElement>()
|
|
20
20
|
const noteInput = ref<HTMLTextAreaElement>()
|
|
21
21
|
|
|
22
|
-
const { info, update } = useDynamicSlideInfo(
|
|
22
|
+
const { info, update } = useDynamicSlideInfo(currentSlideNo)
|
|
23
23
|
|
|
24
24
|
watch(
|
|
25
25
|
info,
|
|
@@ -103,7 +103,7 @@ onMounted(async () => {
|
|
|
103
103
|
})
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
-
watch(
|
|
106
|
+
watch(currentSlideNo, () => {
|
|
107
107
|
contentEditor.clearHistory()
|
|
108
108
|
noteEditor.clearHistory()
|
|
109
109
|
}, { flush: 'post' })
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
const timeout = ref(false)
|
|
5
|
+
onMounted(() => {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
timeout.value = true
|
|
8
|
+
}, 200)
|
|
9
|
+
})
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div class="h-full w-full flex items-center justify-center gap-2">
|
|
14
|
+
<template v-if="timeout">
|
|
15
|
+
<div class="i-svg-spinners-90-ring-with-bg text-xl" />
|
|
16
|
+
<div>Loading slide...</div>
|
|
17
|
+
</template>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { computed, defineComponent, h, ref, toRef } from 'vue'
|
|
1
|
+
import { computed, defineAsyncComponent, defineComponent, h, ref, toRef } from 'vue'
|
|
2
2
|
import type { PropType } from 'vue'
|
|
3
3
|
import { provideLocal } from '@vueuse/core'
|
|
4
|
-
import type { ClicksContext, RenderContext } from '@slidev/types'
|
|
5
|
-
import type { RouteRecordRaw } from 'vue-router'
|
|
4
|
+
import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
|
|
6
5
|
import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
|
|
6
|
+
import SlideLoading from './SlideLoading.vue'
|
|
7
7
|
|
|
8
8
|
export default defineComponent({
|
|
9
9
|
name: 'SlideWrapper',
|
|
@@ -21,17 +21,16 @@ export default defineComponent({
|
|
|
21
21
|
default: false,
|
|
22
22
|
},
|
|
23
23
|
is: {
|
|
24
|
-
type: Object,
|
|
25
24
|
required: true,
|
|
26
25
|
},
|
|
27
26
|
route: {
|
|
28
|
-
type: Object as PropType<
|
|
27
|
+
type: Object as PropType<SlideRoute>,
|
|
29
28
|
required: true,
|
|
30
29
|
},
|
|
31
30
|
},
|
|
32
31
|
setup(props) {
|
|
33
32
|
provideLocal(injectionRoute, props.route)
|
|
34
|
-
provideLocal(injectionCurrentPage, ref(
|
|
33
|
+
provideLocal(injectionCurrentPage, ref(props.route.no))
|
|
35
34
|
provideLocal(injectionRenderContext, ref(props.renderContext as RenderContext))
|
|
36
35
|
provideLocal(injectionActive, toRef(props, 'active'))
|
|
37
36
|
provideLocal(injectionClicksContext, toRef(props, 'clicksContext'))
|
|
@@ -48,11 +47,12 @@ export default defineComponent({
|
|
|
48
47
|
}
|
|
49
48
|
})
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
const SlideComponent = defineAsyncComponent({
|
|
51
|
+
loader: (props.is as any),
|
|
52
|
+
delay: 300,
|
|
53
|
+
loadingComponent: SlideLoading,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return () => h(SlideComponent, { style: style.value })
|
|
57
57
|
},
|
|
58
58
|
})
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { TransitionGroup, computed, shallowRef, watch } from 'vue'
|
|
3
|
-
import {
|
|
3
|
+
import { currentSlideRoute, currentTransition, isPresenter, nextRoute, slides } from '../logic/nav'
|
|
4
4
|
import { getSlideClass } from '../utils'
|
|
5
5
|
import { useViewTransition } from '../composables/useViewTransition'
|
|
6
6
|
import { skipTransition } from '../composables/hmr'
|
|
@@ -16,9 +16,9 @@ defineProps<{
|
|
|
16
16
|
}>()
|
|
17
17
|
|
|
18
18
|
// preload next route
|
|
19
|
-
watch(
|
|
20
|
-
if (
|
|
21
|
-
|
|
19
|
+
watch(currentSlideRoute, () => {
|
|
20
|
+
if (currentSlideRoute.value?.meta && currentSlideRoute.value.meta.preload !== false)
|
|
21
|
+
currentSlideRoute.value.meta.__preloaded = true
|
|
22
22
|
if (nextRoute.value?.meta && nextRoute.value.meta.preload !== false)
|
|
23
23
|
nextRoute.value.meta.__preloaded = true
|
|
24
24
|
}, { immediate: true })
|
|
@@ -29,7 +29,7 @@ const DrawingLayer = shallowRef<any>()
|
|
|
29
29
|
if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
|
|
30
30
|
import('./DrawingLayer.vue').then(v => DrawingLayer.value = v.default)
|
|
31
31
|
|
|
32
|
-
const loadedRoutes = computed(() =>
|
|
32
|
+
const loadedRoutes = computed(() => slides.value.filter(r => r.meta?.__preloaded || r === currentSlideRoute.value))
|
|
33
33
|
|
|
34
34
|
function onAfterLeave() {
|
|
35
35
|
// After transition, we disable it so HMR won't trigger it again
|
|
@@ -45,22 +45,25 @@ function onAfterLeave() {
|
|
|
45
45
|
<!-- Slides -->
|
|
46
46
|
<component
|
|
47
47
|
:is="hasViewTransition ? 'div' : TransitionGroup"
|
|
48
|
-
v-bind="skipTransition ? {} :
|
|
48
|
+
v-bind="skipTransition ? {} : currentTransition"
|
|
49
49
|
id="slideshow"
|
|
50
50
|
tag="div"
|
|
51
51
|
@after-leave="onAfterLeave"
|
|
52
52
|
>
|
|
53
|
-
<
|
|
53
|
+
<div
|
|
54
|
+
v-for="route of loadedRoutes"
|
|
55
|
+
v-show="route === currentSlideRoute"
|
|
56
|
+
:key="route.no"
|
|
57
|
+
>
|
|
54
58
|
<SlideWrapper
|
|
55
|
-
:is="route
|
|
56
|
-
v-show="route === currentRoute"
|
|
59
|
+
:is="route.component!"
|
|
57
60
|
:clicks-context="usePrimaryClicks(route)"
|
|
58
61
|
:class="getSlideClass(route)"
|
|
59
62
|
:route="route"
|
|
60
63
|
:render-context="renderContext"
|
|
61
64
|
class="overflow-hidden"
|
|
62
65
|
/>
|
|
63
|
-
</
|
|
66
|
+
</div>
|
|
64
67
|
</component>
|
|
65
68
|
|
|
66
69
|
<!-- Global Top -->
|
package/logic/drawings.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { toReactive, useLocalStorage } from '@vueuse/core'
|
|
|
5
5
|
import { drawingState, onPatch, patch } from '../state/drawings'
|
|
6
6
|
import { configs } from '../env'
|
|
7
7
|
import { isInputting } from '../state'
|
|
8
|
-
import {
|
|
8
|
+
import { currentSlideNo, isPresenter } from './nav'
|
|
9
9
|
|
|
10
10
|
export const brushColors = [
|
|
11
11
|
'#ff595e',
|
|
@@ -63,7 +63,7 @@ export const drauu = markRaw(createDrauu(drauuOptions))
|
|
|
63
63
|
export function clearDrauu() {
|
|
64
64
|
drauu.clear()
|
|
65
65
|
if (syncUp.value)
|
|
66
|
-
patch(
|
|
66
|
+
patch(currentSlideNo.value, '')
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export function updateState() {
|
|
@@ -74,7 +74,7 @@ export function updateState() {
|
|
|
74
74
|
|
|
75
75
|
export function loadCanvas(page?: number) {
|
|
76
76
|
disableDump = true
|
|
77
|
-
const data = drawingState[page ||
|
|
77
|
+
const data = drawingState[page || currentSlideNo.value]
|
|
78
78
|
if (data != null)
|
|
79
79
|
drauu.load(data)
|
|
80
80
|
else
|
|
@@ -87,7 +87,7 @@ drauu.on('changed', () => {
|
|
|
87
87
|
updateState()
|
|
88
88
|
if (!disableDump) {
|
|
89
89
|
const dump = drauu.dump()
|
|
90
|
-
const key =
|
|
90
|
+
const key = currentSlideNo.value
|
|
91
91
|
if ((drawingState[key] || '') !== dump && syncUp.value)
|
|
92
92
|
patch(key, drauu.dump())
|
|
93
93
|
}
|
|
@@ -95,14 +95,14 @@ drauu.on('changed', () => {
|
|
|
95
95
|
|
|
96
96
|
onPatch((state) => {
|
|
97
97
|
disableDump = true
|
|
98
|
-
if (state[
|
|
99
|
-
drauu.load(state[
|
|
98
|
+
if (state[currentSlideNo.value] != null)
|
|
99
|
+
drauu.load(state[currentSlideNo.value] || '')
|
|
100
100
|
disableDump = false
|
|
101
101
|
updateState()
|
|
102
102
|
})
|
|
103
103
|
|
|
104
104
|
nextTick(() => {
|
|
105
|
-
watch(
|
|
105
|
+
watch(currentSlideNo, () => {
|
|
106
106
|
if (!drauu.mounted)
|
|
107
107
|
return
|
|
108
108
|
loadCanvas()
|
|
@@ -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])
|