@slidev/client 0.48.0-beta.20 → 0.48.0-beta.22
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/CodeBlockWrapper.vue +23 -25
- package/builtin/ShikiMagicMove.vue +63 -13
- 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 +3 -6
- package/env.ts +4 -4
- package/internals/Goto.vue +2 -2
- package/internals/NavControls.vue +7 -5
- package/internals/NoteStatic.vue +1 -1
- package/internals/PrintContainer.vue +8 -7
- package/internals/PrintSlide.vue +6 -13
- package/internals/PrintSlideClick.vue +11 -13
- package/internals/QuickOverview.vue +10 -10
- package/internals/SideEditor.vue +3 -3
- package/internals/SlideContainer.vue +6 -6
- 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/logic/utils.ts +24 -0
- package/modules/context.ts +7 -12
- package/package.json +8 -7
- 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/state/index.ts +1 -1
- package/styles/code.css +2 -2
- package/utils.ts +15 -2
package/context.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ref,
|
|
1
|
+
import { ref, toRef } from 'vue'
|
|
2
2
|
import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
|
|
3
|
-
import { useFixedClicks } from './composables/useClicks'
|
|
4
3
|
import {
|
|
5
4
|
FRONTMATTER_FIELDS,
|
|
6
5
|
HEADMATTER_FIELDS,
|
|
@@ -13,15 +12,13 @@ import {
|
|
|
13
12
|
injectionSlidevContext,
|
|
14
13
|
} from './constants'
|
|
15
14
|
|
|
16
|
-
const clicksContextFallback = shallowRef(useFixedClicks())
|
|
17
|
-
|
|
18
15
|
/**
|
|
19
16
|
* Get the current slide context, should be called inside the setup function of a component inside slide
|
|
20
17
|
*/
|
|
21
18
|
export function useSlideContext() {
|
|
22
19
|
const $slidev = injectLocal(injectionSlidevContext)!
|
|
23
20
|
const $nav = toRef($slidev, 'nav')
|
|
24
|
-
const $clicksContext = injectLocal(injectionClicksContext
|
|
21
|
+
const $clicksContext = injectLocal(injectionClicksContext)!.value
|
|
25
22
|
const $clicks = toRef($clicksContext, 'current')
|
|
26
23
|
const $page = injectLocal(injectionCurrentPage)!
|
|
27
24
|
const $renderContext = injectLocal(injectionRenderContext)!
|
|
@@ -51,7 +48,7 @@ export function provideFrontmatter(frontmatter: Record<string, any>) {
|
|
|
51
48
|
} = useSlideContext()
|
|
52
49
|
|
|
53
50
|
// update frontmatter in router to make HMR work better
|
|
54
|
-
const route = $slidev.nav.
|
|
51
|
+
const route = $slidev.nav.slides.find(i => i.no === $page.value)
|
|
55
52
|
if (route?.meta?.slide?.frontmatter) {
|
|
56
53
|
for (const key of Object.keys(route.meta.slide.frontmatter)) {
|
|
57
54
|
if (!(key in frontmatter))
|
package/env.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { computed } from 'vue'
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
2
|
import { objectMap } from '@antfu/utils'
|
|
3
3
|
import configs from '#slidev/configs'
|
|
4
4
|
|
|
5
5
|
export { configs }
|
|
6
6
|
|
|
7
|
-
export const slideAspect = configs.aspectRatio ?? (16 / 9)
|
|
8
|
-
export const slideWidth = configs.canvasWidth ?? 980
|
|
7
|
+
export const slideAspect = ref(configs.aspectRatio ?? (16 / 9))
|
|
8
|
+
export const slideWidth = ref(configs.canvasWidth ?? 980)
|
|
9
9
|
|
|
10
10
|
// To honor the aspect ratio more as possible, we need to approximate the height to the next integer.
|
|
11
11
|
// Doing this, we will prevent on print, to create an additional empty white page after each page.
|
|
12
|
-
export const slideHeight = Math.ceil(slideWidth / slideAspect)
|
|
12
|
+
export const slideHeight = computed(() => Math.ceil(slideWidth.value / slideAspect.value))
|
|
13
13
|
|
|
14
14
|
export const themeVars = computed(() => {
|
|
15
15
|
return objectMap(configs.themeConfig || {}, (k, v) => [`--slidev-theme-${k}`, v])
|
package/internals/Goto.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, watch } from 'vue'
|
|
3
3
|
import Fuse from 'fuse.js'
|
|
4
|
-
import { go,
|
|
4
|
+
import { go, slides } from '../logic/nav'
|
|
5
5
|
import { activeElement, showGotoDialog } from '../state'
|
|
6
6
|
import Titles from '#slidev/titles.md'
|
|
7
7
|
|
|
@@ -16,7 +16,7 @@ function notNull<T>(value: T | null | undefined): value is T {
|
|
|
16
16
|
return value !== null && value !== undefined
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const fuse = computed(() => new Fuse(
|
|
19
|
+
const fuse = computed(() => new Fuse(slides.value.map(i => i.meta?.slide).filter(notNull), {
|
|
20
20
|
keys: ['no', 'title'],
|
|
21
21
|
threshold: 0.3,
|
|
22
22
|
shouldSort: true,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, shallowRef } from 'vue'
|
|
3
3
|
import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
|
|
4
|
-
import {
|
|
4
|
+
import { downloadPDF } from '../utils'
|
|
5
|
+
import { currentRoute, currentSlideNo, getSlidePath, hasNext, hasPrev, isEmbedded, isPresenter, isPresenterAvailable, next, prev, total } from '../logic/nav'
|
|
5
6
|
import { activeElement, breakpoints, fullscreen, presenterLayout, showEditor, showInfoDialog, showPresenterCursor, toggleOverview, togglePresenterLayout } from '../state'
|
|
6
7
|
import { brush, drawingEnabled } from '../logic/drawings'
|
|
7
8
|
import { configs } from '../env'
|
|
@@ -21,9 +22,10 @@ const props = defineProps({
|
|
|
21
22
|
const md = breakpoints.smaller('md')
|
|
22
23
|
const { isFullscreen, toggle: toggleFullscreen } = fullscreen
|
|
23
24
|
|
|
25
|
+
const presenterPassword = computed(() => currentRoute.value.query.password)
|
|
24
26
|
const query = computed(() => presenterPassword.value ? `?password=${presenterPassword.value}` : '')
|
|
25
|
-
const presenterLink = computed(() =>
|
|
26
|
-
const nonPresenterLink = computed(() =>
|
|
27
|
+
const presenterLink = computed(() => `${getSlidePath(currentSlideNo.value, true)}${query.value}`)
|
|
28
|
+
const nonPresenterLink = computed(() => `${getSlidePath(currentSlideNo.value, false)}${query.value}`)
|
|
27
29
|
|
|
28
30
|
const root = ref<HTMLDivElement>()
|
|
29
31
|
function onMouseLeave() {
|
|
@@ -107,7 +109,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
107
109
|
<RouterLink v-if="isPresenter" :to="nonPresenterLink" class="slidev-icon-btn" title="Play Mode">
|
|
108
110
|
<carbon:presentation-file />
|
|
109
111
|
</RouterLink>
|
|
110
|
-
<RouterLink v-if="__SLIDEV_FEATURE_PRESENTER__ &&
|
|
112
|
+
<RouterLink v-if="__SLIDEV_FEATURE_PRESENTER__ && isPresenterAvailable" :to="presenterLink" class="slidev-icon-btn" title="Presenter Mode">
|
|
111
113
|
<carbon:user-speaker />
|
|
112
114
|
</RouterLink>
|
|
113
115
|
|
|
@@ -156,7 +158,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
156
158
|
|
|
157
159
|
<div class="h-40px flex" p="l-1 t-0.5 r-2" text="sm leading-2">
|
|
158
160
|
<div class="my-auto">
|
|
159
|
-
{{
|
|
161
|
+
{{ currentSlideNo }}
|
|
160
162
|
<span class="opacity-50">/ {{ total }}</span>
|
|
161
163
|
</div>
|
|
162
164
|
</div>
|
package/internals/NoteStatic.vue
CHANGED
|
@@ -4,7 +4,7 @@ import { computed } from 'vue'
|
|
|
4
4
|
import { provideLocal } from '@vueuse/core'
|
|
5
5
|
import { configs, slideAspect, slideWidth } from '../env'
|
|
6
6
|
import { injectionSlideScale } from '../constants'
|
|
7
|
-
import {
|
|
7
|
+
import { currentRoute, slides } from '../logic/nav'
|
|
8
8
|
import PrintSlide from './PrintSlide.vue'
|
|
9
9
|
|
|
10
10
|
const props = defineProps<{
|
|
@@ -12,17 +12,18 @@ const props = defineProps<{
|
|
|
12
12
|
}>()
|
|
13
13
|
|
|
14
14
|
const width = computed(() => props.width)
|
|
15
|
-
const height = computed(() => props.width / slideAspect)
|
|
15
|
+
const height = computed(() => props.width / slideAspect.value)
|
|
16
16
|
|
|
17
17
|
const screenAspect = computed(() => width.value / height.value)
|
|
18
18
|
|
|
19
19
|
const scale = computed(() => {
|
|
20
|
-
if (screenAspect.value < slideAspect)
|
|
21
|
-
return width.value / slideWidth
|
|
22
|
-
return (height.value * slideAspect) / slideWidth
|
|
20
|
+
if (screenAspect.value < slideAspect.value)
|
|
21
|
+
return width.value / slideWidth.value
|
|
22
|
+
return (height.value * slideAspect.value) / slideWidth.value
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// In print mode, the routes will never change. So we don't need reactivity here.
|
|
26
|
+
let routes = slides.value
|
|
26
27
|
if (currentRoute.value.query.range) {
|
|
27
28
|
const r = parseRangeString(routes.length, currentRoute.value.query.range as string)
|
|
28
29
|
routes = r.map(i => routes[i - 1])
|
|
@@ -38,7 +39,7 @@ provideLocal(injectionSlideScale, scale)
|
|
|
38
39
|
<template>
|
|
39
40
|
<div id="print-container" :class="className">
|
|
40
41
|
<div id="print-content">
|
|
41
|
-
<PrintSlide v-for="route of routes" :key="route.
|
|
42
|
+
<PrintSlide v-for="route of routes" :key="route.no" :route="route" />
|
|
42
43
|
</div>
|
|
43
44
|
<slot name="controls" />
|
|
44
45
|
</div>
|
package/internals/PrintSlide.vue
CHANGED
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import { useNav } from '../composables/useNav'
|
|
2
|
+
import type { SlideRoute } from '@slidev/types'
|
|
3
|
+
import { useFixedNav } from '../composables/useNav'
|
|
5
4
|
import { useFixedClicks } from '../composables/useClicks'
|
|
6
5
|
import PrintSlideClick from './PrintSlideClick.vue'
|
|
7
6
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const route = computed(() => props.route)
|
|
11
|
-
const nav = useNav(route)
|
|
12
|
-
const clicks0 = useFixedClicks(route.value, 0)
|
|
7
|
+
const { route } = defineProps<{ route: SlideRoute }>()
|
|
8
|
+
const clicks0 = useFixedClicks(route, 0)
|
|
13
9
|
</script>
|
|
14
10
|
|
|
15
11
|
<template>
|
|
16
12
|
<PrintSlideClick
|
|
17
13
|
:clicks-context="clicks0"
|
|
18
|
-
:nav="
|
|
19
|
-
:route="route"
|
|
14
|
+
:nav="useFixedNav(route, clicks0)"
|
|
20
15
|
/>
|
|
21
16
|
<template v-if="!clicks0.disabled">
|
|
22
17
|
<PrintSlideClick
|
|
23
18
|
v-for="i of clicks0.total"
|
|
24
19
|
:key="i"
|
|
25
|
-
:
|
|
26
|
-
:nav="nav"
|
|
27
|
-
:route="route"
|
|
20
|
+
:nav="useFixedNav(route, useFixedClicks(route, i))"
|
|
28
21
|
/>
|
|
29
22
|
</template>
|
|
30
23
|
</template>
|
|
@@ -1,26 +1,24 @@
|
|
|
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
|
-
height: `${slideHeight}px`,
|
|
23
|
-
width: `${slideWidth}px`,
|
|
20
|
+
height: `${slideHeight.value}px`,
|
|
21
|
+
width: `${slideWidth.value}px`,
|
|
24
22
|
}))
|
|
25
23
|
|
|
26
24
|
const DrawingPreview = shallowRef<any>()
|
|
@@ -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' })
|
|
@@ -25,7 +25,7 @@ const root = ref<HTMLDivElement>()
|
|
|
25
25
|
const element = useElementSize(root)
|
|
26
26
|
|
|
27
27
|
const width = computed(() => props.width ? props.width : element.width.value)
|
|
28
|
-
const height = computed(() => props.width ? props.width / slideAspect : element.height.value)
|
|
28
|
+
const height = computed(() => props.width ? props.width / slideAspect.value : element.height.value)
|
|
29
29
|
|
|
30
30
|
if (props.width) {
|
|
31
31
|
watchEffect(() => {
|
|
@@ -41,14 +41,14 @@ const screenAspect = computed(() => width.value / height.value)
|
|
|
41
41
|
const scale = computed(() => {
|
|
42
42
|
if (props.scale && !isPrintMode.value)
|
|
43
43
|
return props.scale
|
|
44
|
-
if (screenAspect.value < slideAspect)
|
|
45
|
-
return width.value / slideWidth
|
|
46
|
-
return height.value * slideAspect / slideWidth
|
|
44
|
+
if (screenAspect.value < slideAspect.value)
|
|
45
|
+
return width.value / slideWidth.value
|
|
46
|
+
return height.value * slideAspect.value / slideWidth.value
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
const style = computed(() => ({
|
|
50
|
-
'height': `${slideHeight}px`,
|
|
51
|
-
'width': `${slideWidth}px`,
|
|
50
|
+
'height': `${slideHeight.value}px`,
|
|
51
|
+
'width': `${slideWidth.value}px`,
|
|
52
52
|
'transform': `translate(-50%, -50%) scale(${scale.value})`,
|
|
53
53
|
'--slidev-slide-scale': scale.value,
|
|
54
54
|
}))
|
|
@@ -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])
|