@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/internals/NoteStatic.vue
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type { ClicksContext } from '@slidev/types'
|
|
3
|
+
import { useSlideInfo } from '../logic/note'
|
|
4
4
|
import NoteDisplay from './NoteDisplay.vue'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
|
+
no?: number
|
|
7
8
|
class?: string
|
|
9
|
+
clicksContext?: ClicksContext
|
|
8
10
|
}>()
|
|
9
11
|
|
|
10
|
-
const
|
|
11
|
-
const noteHtml = computed(() => currentRoute.value?.meta?.slide?.noteHTML)
|
|
12
|
+
const { info } = useSlideInfo(props.no)
|
|
12
13
|
</script>
|
|
13
14
|
|
|
14
15
|
<template>
|
|
15
16
|
<NoteDisplay
|
|
16
17
|
:class="props.class"
|
|
17
|
-
:note="note"
|
|
18
|
-
:note-html="
|
|
18
|
+
:note="info?.note"
|
|
19
|
+
:note-html="info?.noteHTML"
|
|
20
|
+
:clicks-context="clicksContext"
|
|
19
21
|
/>
|
|
20
22
|
</template>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { parseRangeString } from '@slidev/parser/core'
|
|
3
|
-
import { computed
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { provideLocal } from '@vueuse/core'
|
|
4
5
|
import { configs, slideAspect, slideWidth } from '../env'
|
|
5
6
|
import { injectionSlideScale } from '../constants'
|
|
6
|
-
import {
|
|
7
|
+
import { currentRoute, slides } from '../logic/nav'
|
|
7
8
|
import PrintSlide from './PrintSlide.vue'
|
|
8
9
|
|
|
9
10
|
const props = defineProps<{
|
|
@@ -21,7 +22,8 @@ const scale = computed(() => {
|
|
|
21
22
|
return (height.value * slideAspect) / slideWidth
|
|
22
23
|
})
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
// In print mode, the routes will never change. So we don't need reactivity here.
|
|
26
|
+
let routes = slides.value
|
|
25
27
|
if (currentRoute.value.query.range) {
|
|
26
28
|
const r = parseRangeString(routes.length, currentRoute.value.query.range as string)
|
|
27
29
|
routes = r.map(i => routes[i - 1])
|
|
@@ -31,13 +33,13 @@ const className = computed(() => ({
|
|
|
31
33
|
'select-none': !configs.selectable,
|
|
32
34
|
}))
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
provideLocal(injectionSlideScale, scale)
|
|
35
37
|
</script>
|
|
36
38
|
|
|
37
39
|
<template>
|
|
38
40
|
<div id="print-container" :class="className">
|
|
39
41
|
<div id="print-content">
|
|
40
|
-
<PrintSlide v-for="route of routes" :key="route.
|
|
42
|
+
<PrintSlide v-for="route of routes" :key="route.no" :route="route" />
|
|
41
43
|
</div>
|
|
42
44
|
<slot name="controls" />
|
|
43
45
|
</div>
|
|
@@ -49,6 +51,6 @@ provide(injectionSlideScale, scale)
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
.print-slide-container {
|
|
52
|
-
@apply relative overflow-hidden break-after-page;
|
|
54
|
+
@apply relative overflow-hidden break-after-page translate-0;
|
|
53
55
|
}
|
|
54
56
|
</style>
|
package/internals/PrintSlide.vue
CHANGED
|
@@ -1,24 +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)[1]
|
|
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
|
-
<PrintSlideClick
|
|
17
|
+
<PrintSlideClick
|
|
18
|
+
v-for="i of clicks0.total"
|
|
19
|
+
:key="i"
|
|
20
|
+
:nav="useFixedNav(route, useFixedClicks(route, i))"
|
|
21
|
+
/>
|
|
23
22
|
</template>
|
|
24
23
|
</template>
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type { ClicksContext } from '@slidev/types'
|
|
2
|
+
import { computed, reactive, shallowRef } from 'vue'
|
|
3
|
+
import { provideLocal } from '@vueuse/core'
|
|
5
4
|
import { injectionSlidevContext } from '../constants'
|
|
6
5
|
import { configs, slideHeight, slideWidth } from '../env'
|
|
7
6
|
import { getSlideClass } from '../utils'
|
|
8
|
-
import type { SlidevContextNav } from '../
|
|
7
|
+
import type { SlidevContextNav } from '../composables/useNav'
|
|
9
8
|
import SlideWrapper from './SlideWrapper'
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
import
|
|
10
|
+
import GlobalTop from '#slidev/global-components/top'
|
|
11
|
+
import GlobalBottom from '#slidev/global-components/bottom'
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
import GlobalBottom from '/@slidev/global-components/bottom'
|
|
16
|
-
|
|
17
|
-
const props = defineProps<{
|
|
18
|
-
clicksContext: ClicksContext
|
|
13
|
+
const { nav } = defineProps<{
|
|
19
14
|
nav: SlidevContextNav
|
|
20
|
-
route: RouteRecordRaw
|
|
21
15
|
}>()
|
|
22
16
|
|
|
17
|
+
const route = computed(() => nav.currentSlideRoute.value)
|
|
18
|
+
|
|
23
19
|
const style = computed(() => ({
|
|
24
20
|
height: `${slideHeight}px`,
|
|
25
21
|
width: `${slideWidth}px`,
|
|
@@ -30,11 +26,11 @@ if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
|
|
|
30
26
|
import('./DrawingPreview.vue').then(v => (DrawingPreview.value = v.default))
|
|
31
27
|
|
|
32
28
|
const id = computed(() =>
|
|
33
|
-
`${
|
|
29
|
+
`${route.value.no.toString().padStart(3, '0')}-${(nav.clicks.value + 1).toString().padStart(2, '0')}`,
|
|
34
30
|
)
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
nav
|
|
32
|
+
provideLocal(injectionSlidevContext, reactive({
|
|
33
|
+
nav,
|
|
38
34
|
configs,
|
|
39
35
|
themeConfigs: computed(() => configs.themeConfig),
|
|
40
36
|
}))
|
|
@@ -45,8 +41,8 @@ provide(injectionSlidevContext, reactive({
|
|
|
45
41
|
<GlobalBottom />
|
|
46
42
|
|
|
47
43
|
<SlideWrapper
|
|
48
|
-
:is="route
|
|
49
|
-
:clicks-context="clicksContext"
|
|
44
|
+
:is="route.component!"
|
|
45
|
+
:clicks-context="nav.clicksContext.value"
|
|
50
46
|
:class="getSlideClass(route)"
|
|
51
47
|
:route="route"
|
|
52
48
|
/>
|
|
@@ -57,7 +53,7 @@ provide(injectionSlidevContext, reactive({
|
|
|
57
53
|
&& DrawingPreview
|
|
58
54
|
"
|
|
59
55
|
>
|
|
60
|
-
<DrawingPreview :page="
|
|
56
|
+
<DrawingPreview :page="route.no" />
|
|
61
57
|
</template>
|
|
62
58
|
|
|
63
59
|
<GlobalTop />
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useEventListener, useVModel } from '@vueuse/core'
|
|
3
3
|
import { computed, ref, watchEffect } from 'vue'
|
|
4
|
-
import { themeVars } from '../env'
|
|
5
4
|
import { breakpoints, showOverview, windowSize } from '../state'
|
|
6
|
-
import {
|
|
5
|
+
import { currentSlideNo, go as goSlide, slides } from '../logic/nav'
|
|
7
6
|
import { currentOverviewPage, overviewRowCount } from '../logic/overview'
|
|
8
7
|
import { useFixedClicks } from '../composables/useClicks'
|
|
9
8
|
import { getSlideClass } from '../utils'
|
|
9
|
+
import { CLICKS_MAX } from '../constants'
|
|
10
10
|
import SlideContainer from './SlideContainer.vue'
|
|
11
11
|
import SlideWrapper from './SlideWrapper'
|
|
12
12
|
import DrawingPreview from './DrawingPreview.vue'
|
|
@@ -14,7 +14,7 @@ import IconButton from './IconButton.vue'
|
|
|
14
14
|
|
|
15
15
|
const props = defineProps<{ modelValue: boolean }>()
|
|
16
16
|
|
|
17
|
-
const emit = defineEmits([])
|
|
17
|
+
const emit = defineEmits(['update:modelValue'])
|
|
18
18
|
const value = useVModel(props, 'modelValue', emit)
|
|
19
19
|
|
|
20
20
|
function close() {
|
|
@@ -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
|
})
|
|
@@ -112,7 +112,7 @@ watchEffect(() => {
|
|
|
112
112
|
>
|
|
113
113
|
<div
|
|
114
114
|
v-show="value"
|
|
115
|
-
class="bg-main !bg-opacity-75 p-16 overflow-y-auto backdrop-blur-5px fixed left-0 right-0 top-0 h-[calc(var(--vh,1vh)*100)]"
|
|
115
|
+
class="bg-main !bg-opacity-75 p-16 py-20 overflow-y-auto backdrop-blur-5px fixed left-0 right-0 top-0 h-[calc(var(--vh,1vh)*100)]"
|
|
116
116
|
@click="close()"
|
|
117
117
|
>
|
|
118
118
|
<div
|
|
@@ -120,18 +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
|
-
class="inline-block border rounded
|
|
129
|
-
:class="(focus(idx + 1) || currentOverviewPage === idx + 1) ? 'border
|
|
130
|
-
|
|
131
|
-
@click="go(+route.path)"
|
|
128
|
+
class="inline-block border rounded overflow-hidden bg-main hover:border-primary transition"
|
|
129
|
+
:class="(focus(idx + 1) || currentOverviewPage === idx + 1) ? 'border-primary' : 'border-main'"
|
|
130
|
+
@click="go(route.no)"
|
|
132
131
|
>
|
|
133
132
|
<SlideContainer
|
|
134
|
-
:key="route.
|
|
133
|
+
:key="route.no"
|
|
135
134
|
:width="cardWidth"
|
|
136
135
|
:clicks-disabled="true"
|
|
137
136
|
class="pointer-events-none"
|
|
@@ -139,12 +138,12 @@ watchEffect(() => {
|
|
|
139
138
|
<SlideWrapper
|
|
140
139
|
:is="route.component"
|
|
141
140
|
v-if="route?.component"
|
|
142
|
-
:clicks-context="useFixedClicks(route,
|
|
141
|
+
:clicks-context="useFixedClicks(route, CLICKS_MAX)"
|
|
143
142
|
:class="getSlideClass(route)"
|
|
144
143
|
:route="route"
|
|
145
144
|
render-context="overview"
|
|
146
145
|
/>
|
|
147
|
-
<DrawingPreview :page="
|
|
146
|
+
<DrawingPreview :page="route.no" />
|
|
148
147
|
</SlideContainer>
|
|
149
148
|
</div>
|
|
150
149
|
<div
|
|
@@ -163,7 +162,19 @@ watchEffect(() => {
|
|
|
163
162
|
</div>
|
|
164
163
|
</div>
|
|
165
164
|
</Transition>
|
|
166
|
-
<
|
|
167
|
-
<
|
|
168
|
-
|
|
165
|
+
<div v-if="value" class="fixed top-4 right-4 text-gray-400 flex flex-col items-center gap-2">
|
|
166
|
+
<IconButton title="Close" class="text-2xl" @click="close">
|
|
167
|
+
<carbon:close />
|
|
168
|
+
</IconButton>
|
|
169
|
+
<IconButton
|
|
170
|
+
as="a"
|
|
171
|
+
title="Slides Overview"
|
|
172
|
+
target="_blank"
|
|
173
|
+
href="/overview"
|
|
174
|
+
tab-index="-1"
|
|
175
|
+
class="text-2xl"
|
|
176
|
+
>
|
|
177
|
+
<carbon:list-boxes />
|
|
178
|
+
</IconButton>
|
|
179
|
+
</div>
|
|
169
180
|
</template>
|
|
@@ -54,7 +54,7 @@ onMounted(() => {
|
|
|
54
54
|
</IconButton>
|
|
55
55
|
<MenuButton :disabled="recording">
|
|
56
56
|
<template #button>
|
|
57
|
-
<IconButton title="Select recording device" class="h-full !text-sm !px-0">
|
|
57
|
+
<IconButton title="Select recording device" class="h-full !text-sm !px-0 aspect-initial">
|
|
58
58
|
<carbon:chevron-up class="opacity-50" />
|
|
59
59
|
</IconButton>
|
|
60
60
|
</template>
|
|
@@ -108,19 +108,18 @@ async function start() {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
input[type=
|
|
112
|
-
@apply border border-
|
|
111
|
+
input[type='text'] {
|
|
112
|
+
@apply border border-main rounded px-2 py-1;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
button {
|
|
116
116
|
@apply bg-orange-400 text-white px-4 py-1 rounded border-b-2 border-orange-600;
|
|
117
|
-
@apply hover:(bg-orange-500 border-orange-700)
|
|
117
|
+
@apply hover:(bg-orange-500 border-orange-700);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
button.cancel {
|
|
121
|
-
@apply bg-gray-400 text-white px-4 py-1 rounded border-b-2 border-
|
|
122
|
-
@apply bg-opacity-
|
|
123
|
-
@apply hover:(bg-opacity-75 border-opacity-75)
|
|
121
|
+
@apply bg-gray-400 bg-opacity-50 text-white px-4 py-1 rounded border-b-2 border-main;
|
|
122
|
+
@apply hover:(bg-opacity-75 border-opacity-75);
|
|
124
123
|
}
|
|
125
124
|
}
|
|
126
125
|
</style>
|
|
@@ -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,
|
|
@@ -38,7 +38,6 @@ watch(
|
|
|
38
38
|
async function save() {
|
|
39
39
|
dirty.value = false
|
|
40
40
|
await update({
|
|
41
|
-
raw: null!,
|
|
42
41
|
note: note.value || undefined,
|
|
43
42
|
content: content.value,
|
|
44
43
|
// frontmatter: frontmatter.value,
|
|
@@ -103,6 +102,11 @@ onMounted(async () => {
|
|
|
103
102
|
noteEditor.refresh()
|
|
104
103
|
})
|
|
105
104
|
})
|
|
105
|
+
|
|
106
|
+
watch(currentSlideNo, () => {
|
|
107
|
+
contentEditor.clearHistory()
|
|
108
|
+
noteEditor.clearHistory()
|
|
109
|
+
}, { flush: 'post' })
|
|
106
110
|
})
|
|
107
111
|
|
|
108
112
|
const handlerDown = ref(false)
|
|
@@ -168,13 +172,13 @@ throttledWatch(
|
|
|
168
172
|
<div class="flex pb-2 text-xl -mt-1">
|
|
169
173
|
<div class="mr-4 rounded flex">
|
|
170
174
|
<IconButton
|
|
171
|
-
title="Switch to content tab" :class="tab === 'content' ? 'text
|
|
175
|
+
title="Switch to content tab" :class="tab === 'content' ? 'text-primary' : ''"
|
|
172
176
|
@click="switchTab('content')"
|
|
173
177
|
>
|
|
174
178
|
<carbon:account />
|
|
175
179
|
</IconButton>
|
|
176
180
|
<IconButton
|
|
177
|
-
title="Switch to notes tab" :class="tab === 'note' ? 'text
|
|
181
|
+
title="Switch to notes tab" :class="tab === 'note' ? 'text-primary' : ''"
|
|
178
182
|
@click="switchTab('note')"
|
|
179
183
|
>
|
|
180
184
|
<carbon:align-box-bottom-right />
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useElementSize, useStyleTag } from '@vueuse/core'
|
|
3
|
-
import { computed,
|
|
2
|
+
import { provideLocal, useElementSize, useStyleTag } from '@vueuse/core'
|
|
3
|
+
import { computed, ref, watchEffect } from 'vue'
|
|
4
4
|
import { configs, slideAspect, slideHeight, slideWidth } from '../env'
|
|
5
5
|
import { injectionSlideScale } from '../constants'
|
|
6
|
-
import { isPrintMode } from '../logic/nav'
|
|
6
|
+
import { clicksDirection, isPrintMode } from '../logic/nav'
|
|
7
7
|
|
|
8
8
|
const props = defineProps({
|
|
9
9
|
width: {
|
|
@@ -47,13 +47,16 @@ const scale = computed(() => {
|
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
const style = computed(() => ({
|
|
50
|
-
height: `${slideHeight}px`,
|
|
51
|
-
width: `${slideWidth}px`,
|
|
52
|
-
transform: `translate(-50%, -50%) scale(${scale.value})`,
|
|
50
|
+
'height': `${slideHeight}px`,
|
|
51
|
+
'width': `${slideWidth}px`,
|
|
52
|
+
'transform': `translate(-50%, -50%) scale(${scale.value})`,
|
|
53
|
+
'--slidev-slide-scale': scale.value,
|
|
53
54
|
}))
|
|
54
55
|
|
|
55
56
|
const className = computed(() => ({
|
|
56
57
|
'select-none': !configs.selectable,
|
|
58
|
+
'slidev-nav-go-forward': clicksDirection.value > 0,
|
|
59
|
+
'slidev-nav-go-backward': clicksDirection.value < 0,
|
|
57
60
|
}))
|
|
58
61
|
|
|
59
62
|
if (props.isMain) {
|
|
@@ -64,12 +67,12 @@ if (props.isMain) {
|
|
|
64
67
|
`))
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
provideLocal(injectionSlideScale, scale as any)
|
|
68
71
|
</script>
|
|
69
72
|
|
|
70
73
|
<template>
|
|
71
|
-
<div id="slide-container" ref="root" :class="className">
|
|
72
|
-
<div id="slide-content" :style="style">
|
|
74
|
+
<div id="slide-container" ref="root" class="slidev-slides-container" :class="className">
|
|
75
|
+
<div id="slide-content" class="slidev-slide-content" :style="style">
|
|
73
76
|
<slot />
|
|
74
77
|
</div>
|
|
75
78
|
<slot name="controls" />
|
|
@@ -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,7 +1,9 @@
|
|
|
1
|
-
import { defineComponent, h,
|
|
1
|
+
import { computed, defineAsyncComponent, defineComponent, h, ref, toRef } from 'vue'
|
|
2
2
|
import type { PropType } from 'vue'
|
|
3
|
-
import
|
|
3
|
+
import { provideLocal } from '@vueuse/core'
|
|
4
|
+
import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
|
|
4
5
|
import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
|
|
6
|
+
import SlideLoading from './SlideLoading.vue'
|
|
5
7
|
|
|
6
8
|
export default defineComponent({
|
|
7
9
|
name: 'SlideWrapper',
|
|
@@ -19,24 +21,38 @@ export default defineComponent({
|
|
|
19
21
|
default: false,
|
|
20
22
|
},
|
|
21
23
|
is: {
|
|
22
|
-
|
|
23
|
-
default: undefined,
|
|
24
|
+
required: true,
|
|
24
25
|
},
|
|
25
26
|
route: {
|
|
26
|
-
type: Object
|
|
27
|
-
|
|
27
|
+
type: Object as PropType<SlideRoute>,
|
|
28
|
+
required: true,
|
|
28
29
|
},
|
|
29
30
|
},
|
|
30
31
|
setup(props) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
|
|
32
|
+
provideLocal(injectionRoute, props.route)
|
|
33
|
+
provideLocal(injectionCurrentPage, ref(props.route.no))
|
|
34
|
+
provideLocal(injectionRenderContext, ref(props.renderContext as RenderContext))
|
|
35
|
+
provideLocal(injectionActive, toRef(props, 'active'))
|
|
36
|
+
provideLocal(injectionClicksContext, toRef(props, 'clicksContext'))
|
|
37
|
+
|
|
38
|
+
const style = computed(() => {
|
|
39
|
+
const zoom = props.route.meta?.slide?.frontmatter.zoom ?? 1
|
|
40
|
+
return zoom === 1
|
|
41
|
+
? undefined
|
|
42
|
+
: {
|
|
43
|
+
width: `${100 / zoom}%`,
|
|
44
|
+
height: `${100 / zoom}%`,
|
|
45
|
+
transformOrigin: 'top left',
|
|
46
|
+
transform: `scale(${zoom})`,
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
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 })
|
|
41
57
|
},
|
|
42
58
|
})
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -1,27 +1,24 @@
|
|
|
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'
|
|
7
7
|
import { usePrimaryClicks } from '../composables/useClicks'
|
|
8
8
|
import SlideWrapper from './SlideWrapper'
|
|
9
|
-
|
|
10
|
-
// @ts-expect-error virtual module
|
|
11
|
-
import GlobalTop from '/@slidev/global-components/top'
|
|
12
|
-
|
|
13
|
-
// @ts-expect-error virtual module
|
|
14
|
-
import GlobalBottom from '/@slidev/global-components/bottom'
|
|
15
9
|
import PresenterMouse from './PresenterMouse.vue'
|
|
16
10
|
|
|
11
|
+
import GlobalTop from '#slidev/global-components/top'
|
|
12
|
+
import GlobalBottom from '#slidev/global-components/bottom'
|
|
13
|
+
|
|
17
14
|
defineProps<{
|
|
18
15
|
renderContext: 'slide' | 'presenter'
|
|
19
16
|
}>()
|
|
20
17
|
|
|
21
18
|
// preload next route
|
|
22
|
-
watch(
|
|
23
|
-
if (
|
|
24
|
-
|
|
19
|
+
watch(currentSlideRoute, () => {
|
|
20
|
+
if (currentSlideRoute.value?.meta && currentSlideRoute.value.meta.preload !== false)
|
|
21
|
+
currentSlideRoute.value.meta.__preloaded = true
|
|
25
22
|
if (nextRoute.value?.meta && nextRoute.value.meta.preload !== false)
|
|
26
23
|
nextRoute.value.meta.__preloaded = true
|
|
27
24
|
}, { immediate: true })
|
|
@@ -32,7 +29,7 @@ const DrawingLayer = shallowRef<any>()
|
|
|
32
29
|
if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
|
|
33
30
|
import('./DrawingLayer.vue').then(v => DrawingLayer.value = v.default)
|
|
34
31
|
|
|
35
|
-
const loadedRoutes = computed(() =>
|
|
32
|
+
const loadedRoutes = computed(() => slides.value.filter(r => r.meta?.__preloaded || r === currentSlideRoute.value))
|
|
36
33
|
|
|
37
34
|
function onAfterLeave() {
|
|
38
35
|
// After transition, we disable it so HMR won't trigger it again
|
|
@@ -48,22 +45,25 @@ function onAfterLeave() {
|
|
|
48
45
|
<!-- Slides -->
|
|
49
46
|
<component
|
|
50
47
|
:is="hasViewTransition ? 'div' : TransitionGroup"
|
|
51
|
-
v-bind="skipTransition ? {} :
|
|
48
|
+
v-bind="skipTransition ? {} : currentTransition"
|
|
52
49
|
id="slideshow"
|
|
53
50
|
tag="div"
|
|
54
51
|
@after-leave="onAfterLeave"
|
|
55
52
|
>
|
|
56
|
-
<
|
|
53
|
+
<div
|
|
54
|
+
v-for="route of loadedRoutes"
|
|
55
|
+
v-show="route === currentSlideRoute"
|
|
56
|
+
:key="route.no"
|
|
57
|
+
>
|
|
57
58
|
<SlideWrapper
|
|
58
|
-
:is="route
|
|
59
|
-
v-show="route === currentRoute"
|
|
59
|
+
:is="route.component!"
|
|
60
60
|
:clicks-context="usePrimaryClicks(route)"
|
|
61
61
|
:class="getSlideClass(route)"
|
|
62
62
|
:route="route"
|
|
63
63
|
:render-context="renderContext"
|
|
64
64
|
class="overflow-hidden"
|
|
65
65
|
/>
|
|
66
|
-
</
|
|
66
|
+
</div>
|
|
67
67
|
</component>
|
|
68
68
|
|
|
69
69
|
<!-- Global Top -->
|
|
@@ -77,10 +77,12 @@ function onAfterLeave() {
|
|
|
77
77
|
|
|
78
78
|
<style scoped>
|
|
79
79
|
#slideshow {
|
|
80
|
-
|
|
80
|
+
height: 100%;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
#slideshow > div {
|
|
84
|
-
|
|
84
|
+
position: absolute;
|
|
85
|
+
height: 100%;
|
|
86
|
+
width: 100%;
|
|
85
87
|
}
|
|
86
88
|
</style>
|
|
@@ -51,9 +51,15 @@ const props = defineProps({
|
|
|
51
51
|
grid-template-rows: repeat(2, 1fr);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
.col-header {
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
.col-header {
|
|
55
|
+
grid-area: 1 / 1 / 2 / 3;
|
|
56
|
+
}
|
|
57
|
+
.col-left {
|
|
58
|
+
grid-area: 2 / 1 / 3 / 2;
|
|
59
|
+
}
|
|
60
|
+
.col-right {
|
|
61
|
+
grid-area: 2 / 2 / 3 / 3;
|
|
62
|
+
}
|
|
57
63
|
.col-bottom {
|
|
58
64
|
align-self: end;
|
|
59
65
|
grid-area: 3 / 1 / 3 / 3;
|