@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
|
@@ -1,15 +1,16 @@
|
|
|
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
|
-
import { configs
|
|
8
|
-
import Controls from '
|
|
9
|
-
import SlideContainer from '
|
|
10
|
-
import NavControls from '
|
|
11
|
-
import SlidesShow from '
|
|
12
|
-
import PrintStyle from '
|
|
8
|
+
import { configs } from '../env'
|
|
9
|
+
import Controls from '../internals/Controls.vue'
|
|
10
|
+
import SlideContainer from '../internals/SlideContainer.vue'
|
|
11
|
+
import NavControls from '../internals/NavControls.vue'
|
|
12
|
+
import SlidesShow from '../internals/SlidesShow.vue'
|
|
13
|
+
import PrintStyle from '../internals/PrintStyle.vue'
|
|
13
14
|
|
|
14
15
|
registerShortcuts()
|
|
15
16
|
|
|
@@ -31,18 +32,21 @@ useSwipeControls(root)
|
|
|
31
32
|
|
|
32
33
|
const persistNav = computed(() => isScreenVertical.value || showEditor.value)
|
|
33
34
|
|
|
34
|
-
const
|
|
35
|
+
const SideEditor = shallowRef<any>()
|
|
35
36
|
if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
|
|
36
|
-
import('
|
|
37
|
+
import('../internals/SideEditor.vue').then(v => SideEditor.value = v.default)
|
|
37
38
|
|
|
38
39
|
const DrawingControls = shallowRef<any>()
|
|
39
40
|
if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
40
|
-
import('
|
|
41
|
+
import('../internals/DrawingControls.vue').then(v => DrawingControls.value = v.default)
|
|
41
42
|
</script>
|
|
42
43
|
|
|
43
44
|
<template>
|
|
44
45
|
<PrintStyle v-if="isPrintMode" />
|
|
45
|
-
<div
|
|
46
|
+
<div
|
|
47
|
+
id="page-root" ref="root" class="grid"
|
|
48
|
+
:class="isEditorVertical ? 'grid-rows-[1fr_max-content]' : 'grid-cols-[1fr_max-content]'"
|
|
49
|
+
>
|
|
46
50
|
<SlideContainer
|
|
47
51
|
class="w-full h-full"
|
|
48
52
|
:style="{ background: 'var(--slidev-slide-container-background, black)' }"
|
|
@@ -70,8 +74,8 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
70
74
|
</template>
|
|
71
75
|
</SlideContainer>
|
|
72
76
|
|
|
73
|
-
<template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ &&
|
|
74
|
-
<
|
|
77
|
+
<template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && SideEditor && showEditor">
|
|
78
|
+
<SideEditor :resize="true" />
|
|
75
79
|
</template>
|
|
76
80
|
</div>
|
|
77
81
|
<Controls />
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import { useStyleTag } from '@vueuse/core'
|
|
4
4
|
import { useHead } from '@unhead/vue'
|
|
5
|
-
import { configs
|
|
6
|
-
import {
|
|
7
|
-
import NoteDisplay from '
|
|
5
|
+
import { configs } from '../../env'
|
|
6
|
+
import { slides, total } from '../../logic/nav'
|
|
7
|
+
import NoteDisplay from '../../internals/NoteDisplay.vue'
|
|
8
8
|
|
|
9
9
|
useStyleTag(`
|
|
10
10
|
@page {
|
|
@@ -24,15 +24,17 @@ html #page-root {
|
|
|
24
24
|
}
|
|
25
25
|
`)
|
|
26
26
|
|
|
27
|
-
useHead({
|
|
27
|
+
useHead({
|
|
28
|
+
title: `Notes - ${configs.title}`,
|
|
29
|
+
})
|
|
28
30
|
|
|
29
|
-
const slidesWithNote = computed(() =>
|
|
31
|
+
const slidesWithNote = computed(() => slides.value
|
|
30
32
|
.map(route => route.meta?.slide)
|
|
31
33
|
.filter(slide => slide !== undefined && slide.noteHTML !== ''))
|
|
32
34
|
</script>
|
|
33
35
|
|
|
34
36
|
<template>
|
|
35
|
-
<div id="page-root"
|
|
37
|
+
<div id="page-root">
|
|
36
38
|
<div class="m-4">
|
|
37
39
|
<div class="mb-10">
|
|
38
40
|
<h1 class="text-4xl font-bold mt-2">
|
|
@@ -54,9 +56,12 @@ const slidesWithNote = computed(() => rawRoutes
|
|
|
54
56
|
<div class="flex-auto" />
|
|
55
57
|
</div>
|
|
56
58
|
</h2>
|
|
57
|
-
<NoteDisplay
|
|
59
|
+
<NoteDisplay
|
|
60
|
+
:note-html="slide!.noteHTML"
|
|
61
|
+
class="max-w-full"
|
|
62
|
+
/>
|
|
58
63
|
</div>
|
|
59
|
-
<hr v-if="index < slidesWithNote.length - 1" class="border-
|
|
64
|
+
<hr v-if="index < slidesWithNote.length - 1" class="border-main mb-8">
|
|
60
65
|
</div>
|
|
61
66
|
</div>
|
|
62
67
|
</div>
|
|
@@ -2,25 +2,27 @@
|
|
|
2
2
|
import { useHead } from '@unhead/vue'
|
|
3
3
|
import { computed, onMounted, reactive, ref, shallowRef, watch } from 'vue'
|
|
4
4
|
import { useMouse, useWindowFocus } from '@vueuse/core'
|
|
5
|
-
import { clicksContext,
|
|
5
|
+
import { clicksContext, currentSlideNo, currentSlideRoute, hasNext, nextRoute, queryClicks, slides, total } from '../logic/nav'
|
|
6
|
+
import { useSwipeControls } from '../composables/useSwipeControls'
|
|
6
7
|
import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showOverview, showPresenterCursor } from '../state'
|
|
7
|
-
import { configs
|
|
8
|
+
import { configs } from '../env'
|
|
8
9
|
import { sharedState } from '../state/shared'
|
|
9
10
|
import { registerShortcuts } from '../logic/shortcuts'
|
|
10
11
|
import { getSlideClass } from '../utils'
|
|
11
12
|
import { useTimer } from '../logic/utils'
|
|
12
13
|
import { isDrawing } from '../logic/drawings'
|
|
13
|
-
import { useFixedClicks } from '../composables/useClicks'
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import DrawingControls from '
|
|
23
|
-
import IconButton from '
|
|
14
|
+
import { useFixedClicks, usePrimaryClicks } from '../composables/useClicks'
|
|
15
|
+
import SlideWrapper from '../internals/SlideWrapper'
|
|
16
|
+
import SlideContainer from '../internals/SlideContainer.vue'
|
|
17
|
+
import NavControls from '../internals/NavControls.vue'
|
|
18
|
+
import QuickOverview from '../internals/QuickOverview.vue'
|
|
19
|
+
import NoteEditable from '../internals/NoteEditable.vue'
|
|
20
|
+
import NoteStatic from '../internals/NoteStatic.vue'
|
|
21
|
+
import Goto from '../internals/Goto.vue'
|
|
22
|
+
import SlidesShow from '../internals/SlidesShow.vue'
|
|
23
|
+
import DrawingControls from '../internals/DrawingControls.vue'
|
|
24
|
+
import IconButton from '../internals/IconButton.vue'
|
|
25
|
+
import ClicksSlider from '../internals/ClicksSlider.vue'
|
|
24
26
|
|
|
25
27
|
const main = ref<HTMLDivElement>()
|
|
26
28
|
|
|
@@ -36,25 +38,32 @@ const notesEditing = ref(false)
|
|
|
36
38
|
|
|
37
39
|
const { timer, resetTimer } = useTimer()
|
|
38
40
|
|
|
39
|
-
const clicksCtxMap =
|
|
41
|
+
const clicksCtxMap = computed(() => slides.value.map(route => useFixedClicks(route)))
|
|
40
42
|
const nextFrame = computed(() => {
|
|
41
43
|
if (clicksContext.value.current < clicksContext.value.total)
|
|
42
|
-
return [
|
|
44
|
+
return [currentSlideRoute.value!, clicksContext.value.current + 1] as const
|
|
43
45
|
else if (hasNext.value)
|
|
44
46
|
return [nextRoute.value!, 0] as const
|
|
45
47
|
else
|
|
46
48
|
return null
|
|
47
49
|
})
|
|
50
|
+
|
|
48
51
|
const nextFrameClicksCtx = computed(() => {
|
|
49
|
-
return nextFrame.value && clicksCtxMap[
|
|
52
|
+
return nextFrame.value && clicksCtxMap.value[nextFrame.value[0].no - 1]
|
|
50
53
|
})
|
|
51
|
-
watch([currentRoute, queryClicks], () => {
|
|
52
|
-
nextFrameClicksCtx.value && (nextFrameClicksCtx.value[0].value = nextFrame.value![1])
|
|
53
|
-
}, { immediate: true })
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
watch(
|
|
56
|
+
[currentSlideRoute, queryClicks],
|
|
57
|
+
() => {
|
|
58
|
+
if (nextFrameClicksCtx.value)
|
|
59
|
+
nextFrameClicksCtx.value.current = nextFrame.value![1]
|
|
60
|
+
},
|
|
61
|
+
{ immediate: true },
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const SideEditor = shallowRef<any>()
|
|
56
65
|
if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
|
|
57
|
-
import('
|
|
66
|
+
import('../internals/SideEditor.vue').then(v => SideEditor.value = v.default)
|
|
58
67
|
|
|
59
68
|
// sync presenter cursor
|
|
60
69
|
onMounted(() => {
|
|
@@ -86,68 +95,64 @@ onMounted(() => {
|
|
|
86
95
|
<template>
|
|
87
96
|
<div class="bg-main h-full slidev-presenter">
|
|
88
97
|
<div class="grid-container" :class="`layout${presenterLayout}`">
|
|
89
|
-
<div class="grid-section
|
|
90
|
-
<img src="../assets/logo-title-horizontal.png" class="ml-2 my-auto h-10 py-1 lg:h-14 lg:py-2" style="height: 3.5rem;" alt="Slidev logo">
|
|
91
|
-
<div class="flex-auto" />
|
|
92
|
-
<div
|
|
93
|
-
class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
|
|
94
|
-
opacity="50 hover:100"
|
|
95
|
-
@click="resetTimer"
|
|
96
|
-
>
|
|
97
|
-
<carbon:time class="absolute" />
|
|
98
|
-
<carbon:renew class="absolute opacity-0" />
|
|
99
|
-
</div>
|
|
100
|
-
<div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
|
|
101
|
-
{{ timer }}
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
<div ref="main" class="relative grid-section main flex flex-col p-2 lg:p-4" :style="themeVars">
|
|
98
|
+
<div ref="main" class="relative grid-section main flex flex-col">
|
|
105
99
|
<SlideContainer
|
|
106
100
|
key="main"
|
|
107
|
-
class="h-full w-full"
|
|
101
|
+
class="h-full w-full p-2 lg:p-4 flex-auto"
|
|
108
102
|
>
|
|
109
103
|
<template #default>
|
|
110
104
|
<SlidesShow render-context="presenter" />
|
|
111
105
|
</template>
|
|
112
106
|
</SlideContainer>
|
|
113
|
-
<
|
|
114
|
-
|
|
107
|
+
<ClicksSlider
|
|
108
|
+
:key="currentSlideRoute?.no"
|
|
109
|
+
:clicks-context="usePrimaryClicks(currentSlideRoute)"
|
|
110
|
+
class="w-full pb2 px4 flex-none"
|
|
111
|
+
/>
|
|
112
|
+
<div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
|
|
113
|
+
Current
|
|
115
114
|
</div>
|
|
116
115
|
</div>
|
|
117
|
-
<div class="relative grid-section next flex flex-col p-2 lg:p-4"
|
|
116
|
+
<div class="relative grid-section next flex flex-col p-2 lg:p-4">
|
|
118
117
|
<SlideContainer
|
|
119
118
|
v-if="nextFrame && nextFrameClicksCtx"
|
|
120
119
|
key="next"
|
|
121
120
|
class="h-full w-full"
|
|
122
121
|
>
|
|
123
122
|
<SlideWrapper
|
|
124
|
-
:is="nextFrame[0].component
|
|
125
|
-
:key="nextFrame[0].
|
|
126
|
-
:clicks-context="nextFrameClicksCtx
|
|
123
|
+
:is="nextFrame[0].component!"
|
|
124
|
+
:key="nextFrame[0].no"
|
|
125
|
+
:clicks-context="nextFrameClicksCtx"
|
|
127
126
|
:class="getSlideClass(nextFrame[0])"
|
|
128
127
|
:route="nextFrame[0]"
|
|
129
128
|
render-context="previewNext"
|
|
130
129
|
/>
|
|
131
130
|
</SlideContainer>
|
|
132
|
-
<div class="
|
|
133
|
-
|
|
131
|
+
<div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
|
|
132
|
+
Next
|
|
134
133
|
</div>
|
|
135
134
|
</div>
|
|
136
135
|
<!-- Notes -->
|
|
137
|
-
<div v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ &&
|
|
138
|
-
<
|
|
136
|
+
<div v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && SideEditor && showEditor" class="grid-section note of-auto">
|
|
137
|
+
<SideEditor />
|
|
139
138
|
</div>
|
|
140
139
|
<div v-else class="grid-section note grid grid-rows-[1fr_min-content] overflow-hidden">
|
|
141
|
-
<
|
|
140
|
+
<NoteEditable
|
|
142
141
|
v-if="__DEV__"
|
|
142
|
+
:key="`edit-${currentSlideNo}`"
|
|
143
|
+
v-model:editing="notesEditing"
|
|
144
|
+
:no="currentSlideNo"
|
|
143
145
|
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
|
|
144
|
-
:
|
|
146
|
+
:clicks-context="clicksContext"
|
|
145
147
|
:style="{ fontSize: `${presenterNotesFontSize}em` }"
|
|
146
148
|
/>
|
|
147
149
|
<NoteStatic
|
|
148
150
|
v-else
|
|
151
|
+
:key="`static-${currentSlideNo}`"
|
|
152
|
+
:no="currentSlideNo"
|
|
149
153
|
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
|
|
150
154
|
:style="{ fontSize: `${presenterNotesFontSize}em` }"
|
|
155
|
+
:clicks-context="clicksContext"
|
|
151
156
|
/>
|
|
152
157
|
<div class="border-t border-main py-1 px-2 text-sm">
|
|
153
158
|
<IconButton title="Increase font size" @click="increasePresenterFontSize">
|
|
@@ -165,116 +170,120 @@ onMounted(() => {
|
|
|
165
170
|
</IconButton>
|
|
166
171
|
</div>
|
|
167
172
|
</div>
|
|
168
|
-
<div class="grid-section bottom">
|
|
173
|
+
<div class="grid-section bottom flex">
|
|
169
174
|
<NavControls :persist="true" />
|
|
175
|
+
<div flex-auto />
|
|
176
|
+
<div
|
|
177
|
+
class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
|
|
178
|
+
opacity="50 hover:100"
|
|
179
|
+
@click="resetTimer"
|
|
180
|
+
>
|
|
181
|
+
<carbon:time class="absolute" />
|
|
182
|
+
<carbon:renew class="absolute opacity-0" />
|
|
183
|
+
</div>
|
|
184
|
+
<div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
|
|
185
|
+
{{ timer }}
|
|
186
|
+
</div>
|
|
170
187
|
</div>
|
|
171
188
|
<DrawingControls v-if="__SLIDEV_FEATURE_DRAWINGS__" />
|
|
172
189
|
</div>
|
|
173
190
|
<div class="progress-bar">
|
|
174
191
|
<div
|
|
175
|
-
class="progress h-
|
|
176
|
-
:style="{ width: `${(
|
|
192
|
+
class="progress h-3px bg-primary transition-all"
|
|
193
|
+
:style="{ width: `${(currentSlideNo - 1) / (total - 1) * 100}%` }"
|
|
177
194
|
/>
|
|
178
195
|
</div>
|
|
179
196
|
</div>
|
|
180
197
|
<Goto />
|
|
181
|
-
<
|
|
198
|
+
<QuickOverview v-model="showOverview" />
|
|
182
199
|
</template>
|
|
183
200
|
|
|
184
|
-
<style
|
|
201
|
+
<style scoped>
|
|
185
202
|
.slidev-presenter {
|
|
186
203
|
--slidev-controls-foreground: current;
|
|
187
204
|
}
|
|
188
205
|
|
|
189
|
-
.timer-btn:hover {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@apply opacity-100;
|
|
195
|
-
}
|
|
206
|
+
.timer-btn:hover > :first-child {
|
|
207
|
+
opacity: 0;
|
|
208
|
+
}
|
|
209
|
+
.timer-btn:hover > :last-child {
|
|
210
|
+
opacity: 1;
|
|
196
211
|
}
|
|
197
212
|
|
|
198
213
|
.section-title {
|
|
199
|
-
|
|
214
|
+
--uno: px-4 py-2 text-xl;
|
|
200
215
|
}
|
|
201
216
|
|
|
202
217
|
.grid-container {
|
|
203
|
-
|
|
218
|
+
--uno: bg-gray/20;
|
|
219
|
+
height: 100%;
|
|
220
|
+
width: 100%;
|
|
204
221
|
display: grid;
|
|
205
222
|
gap: 1px 1px;
|
|
206
223
|
}
|
|
207
224
|
|
|
208
225
|
.grid-container.layout1 {
|
|
209
226
|
grid-template-columns: 1fr 1fr;
|
|
210
|
-
grid-template-rows:
|
|
227
|
+
grid-template-rows: 2fr 1fr min-content;
|
|
211
228
|
grid-template-areas:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"bottom bottom";
|
|
229
|
+
'main main'
|
|
230
|
+
'note next'
|
|
231
|
+
'bottom bottom';
|
|
216
232
|
}
|
|
217
233
|
|
|
218
234
|
.grid-container.layout2 {
|
|
219
235
|
grid-template-columns: 3fr 2fr;
|
|
220
|
-
grid-template-rows:
|
|
236
|
+
grid-template-rows: 2fr 1fr min-content;
|
|
221
237
|
grid-template-areas:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
"bottom bottom";
|
|
238
|
+
'note main'
|
|
239
|
+
'note next'
|
|
240
|
+
'bottom bottom';
|
|
226
241
|
}
|
|
227
242
|
|
|
228
243
|
@media (max-aspect-ratio: 3/5) {
|
|
229
244
|
.grid-container.layout1 {
|
|
230
245
|
grid-template-columns: 1fr;
|
|
231
|
-
grid-template-rows:
|
|
246
|
+
grid-template-rows: 1fr 1fr 1fr min-content;
|
|
232
247
|
grid-template-areas:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
"bottom";
|
|
248
|
+
'main'
|
|
249
|
+
'note'
|
|
250
|
+
'next'
|
|
251
|
+
'bottom';
|
|
238
252
|
}
|
|
239
253
|
}
|
|
240
254
|
|
|
241
255
|
@media (min-aspect-ratio: 1/1) {
|
|
242
256
|
.grid-container.layout1 {
|
|
243
257
|
grid-template-columns: 1fr 1.1fr 0.9fr;
|
|
244
|
-
grid-template-rows:
|
|
258
|
+
grid-template-rows: 1fr 2fr min-content;
|
|
245
259
|
grid-template-areas:
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
"bottom bottom bottom";
|
|
260
|
+
'main main next'
|
|
261
|
+
'main main note'
|
|
262
|
+
'bottom bottom bottom';
|
|
250
263
|
}
|
|
251
264
|
}
|
|
252
265
|
|
|
253
266
|
.progress-bar {
|
|
254
|
-
|
|
267
|
+
--uno: fixed left-0 right-0 top-0;
|
|
255
268
|
}
|
|
256
269
|
|
|
257
270
|
.grid-section {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
&.top {
|
|
261
|
-
grid-area: top;
|
|
262
|
-
}
|
|
263
|
-
&.main {
|
|
264
|
-
grid-area: main;
|
|
265
|
-
}
|
|
266
|
-
&.next {
|
|
267
|
-
grid-area: next;
|
|
268
|
-
}
|
|
269
|
-
&.note {
|
|
270
|
-
grid-area: note;
|
|
271
|
-
}
|
|
272
|
-
&.bottom {
|
|
273
|
-
grid-area: bottom;
|
|
274
|
-
}
|
|
271
|
+
--uno: bg-main;
|
|
275
272
|
}
|
|
276
273
|
|
|
277
|
-
.
|
|
278
|
-
|
|
274
|
+
.grid-section.top {
|
|
275
|
+
grid-area: top;
|
|
276
|
+
}
|
|
277
|
+
.grid-section.main {
|
|
278
|
+
grid-area: main;
|
|
279
|
+
}
|
|
280
|
+
.grid-section.next {
|
|
281
|
+
grid-area: next;
|
|
282
|
+
}
|
|
283
|
+
.grid-section.note {
|
|
284
|
+
grid-area: note;
|
|
285
|
+
}
|
|
286
|
+
.grid-section.bottom {
|
|
287
|
+
grid-area: bottom;
|
|
279
288
|
}
|
|
280
289
|
</style>
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
import { watchEffect } from 'vue'
|
|
3
3
|
import { windowSize } from '../state'
|
|
4
4
|
import { isPrintMode } from '../logic/nav'
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import PrintStyle from './PrintStyle.vue'
|
|
5
|
+
import PrintContainer from '../internals/PrintContainer.vue'
|
|
6
|
+
import PrintStyle from '../internals/PrintStyle.vue'
|
|
8
7
|
|
|
9
8
|
watchEffect(() => {
|
|
10
9
|
if (isPrintMode)
|
|
@@ -16,7 +15,7 @@ watchEffect(() => {
|
|
|
16
15
|
|
|
17
16
|
<template>
|
|
18
17
|
<PrintStyle v-if="isPrintMode" />
|
|
19
|
-
<div id="page-root" class="grid grid-cols-[1fr_max-content]"
|
|
18
|
+
<div id="page-root" class="grid grid-cols-[1fr_max-content]">
|
|
20
19
|
<PrintContainer
|
|
21
20
|
class="w-full h-full"
|
|
22
21
|
:style="{ background: 'var(--slidev-slide-container-background, black)' }"
|
package/routes.ts
CHANGED
|
@@ -1,65 +1,59 @@
|
|
|
1
1
|
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
|
2
2
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
|
3
|
-
import
|
|
4
|
-
import type { ClicksContext } from '@slidev/types'
|
|
5
|
-
import Play from './internals/Play.vue'
|
|
6
|
-
import Print from './internals/Print.vue'
|
|
7
|
-
|
|
8
|
-
// @ts-expect-error missing types
|
|
9
|
-
import _rawRoutes, { redirects } from '/@slidev/routes'
|
|
10
|
-
|
|
11
|
-
// @ts-expect-error missing types
|
|
12
|
-
import _configs from '/@slidev/configs'
|
|
13
|
-
|
|
14
|
-
export const rawRoutes = _rawRoutes as RouteRecordRaw[]
|
|
3
|
+
import configs from '#slidev/configs'
|
|
15
4
|
|
|
16
5
|
export const routes: RouteRecordRaw[] = [
|
|
17
6
|
{
|
|
18
|
-
name: '
|
|
19
|
-
path: '/',
|
|
20
|
-
component:
|
|
21
|
-
children: [
|
|
22
|
-
...rawRoutes,
|
|
23
|
-
...redirects,
|
|
24
|
-
],
|
|
7
|
+
name: 'print',
|
|
8
|
+
path: '/print',
|
|
9
|
+
component: () => import('./pages/print.vue'),
|
|
25
10
|
},
|
|
26
|
-
|
|
11
|
+
|
|
12
|
+
// Redirects
|
|
27
13
|
{ path: '', redirect: { path: '/1' } },
|
|
28
|
-
{ path: '/:pathMatch(.*)', redirect: { path: '/1' } },
|
|
29
14
|
]
|
|
30
15
|
|
|
31
16
|
if (__SLIDEV_FEATURE_PRESENTER__) {
|
|
32
17
|
function passwordGuard(to: RouteLocationNormalized) {
|
|
33
|
-
if (!
|
|
18
|
+
if (!configs.remote || configs.remote === to.query.password)
|
|
34
19
|
return true
|
|
35
|
-
if (
|
|
20
|
+
if (configs.remote && to.query.password === undefined) {
|
|
36
21
|
// eslint-disable-next-line no-alert
|
|
37
22
|
const password = prompt('Enter password')
|
|
38
|
-
if (
|
|
23
|
+
if (configs.remote === password)
|
|
39
24
|
return true
|
|
40
25
|
}
|
|
41
26
|
if (to.params.no)
|
|
42
27
|
return { path: `/${to.params.no}` }
|
|
43
28
|
return { path: '' }
|
|
44
29
|
}
|
|
45
|
-
|
|
30
|
+
|
|
31
|
+
routes.push({
|
|
32
|
+
path: '/presenter/print',
|
|
33
|
+
component: () => import('./pages/presenter/print.vue'),
|
|
34
|
+
})
|
|
46
35
|
if (__SLIDEV_HAS_SERVER__) {
|
|
47
36
|
routes.push({
|
|
48
37
|
name: 'entry',
|
|
49
38
|
path: '/entry',
|
|
50
|
-
component: () => import('./
|
|
39
|
+
component: () => import('./pages/entry.vue'),
|
|
40
|
+
})
|
|
41
|
+
routes.push({
|
|
42
|
+
name: 'overview',
|
|
43
|
+
path: '/overview',
|
|
44
|
+
component: () => import('./pages/overview.vue'),
|
|
51
45
|
})
|
|
52
46
|
routes.push({
|
|
53
47
|
name: 'notes',
|
|
54
48
|
path: '/notes',
|
|
55
|
-
component: () => import('./
|
|
49
|
+
component: () => import('./pages/notes.vue'),
|
|
56
50
|
beforeEnter: passwordGuard,
|
|
57
51
|
})
|
|
58
52
|
}
|
|
59
53
|
routes.push({
|
|
60
54
|
name: 'presenter',
|
|
61
55
|
path: '/presenter/:no',
|
|
62
|
-
component: () => import('./
|
|
56
|
+
component: () => import('./pages/presenter.vue'),
|
|
63
57
|
beforeEnter: passwordGuard,
|
|
64
58
|
})
|
|
65
59
|
routes.push({
|
|
@@ -68,41 +62,15 @@ if (__SLIDEV_FEATURE_PRESENTER__) {
|
|
|
68
62
|
})
|
|
69
63
|
}
|
|
70
64
|
|
|
65
|
+
routes.push({
|
|
66
|
+
name: 'play',
|
|
67
|
+
path: '/:no',
|
|
68
|
+
component: () => import('./pages/play.vue'),
|
|
69
|
+
})
|
|
70
|
+
|
|
71
71
|
export const router = createRouter({
|
|
72
72
|
history: __SLIDEV_HASH_ROUTE__
|
|
73
73
|
? createWebHashHistory(import.meta.env.BASE_URL)
|
|
74
74
|
: createWebHistory(import.meta.env.BASE_URL),
|
|
75
75
|
routes,
|
|
76
76
|
})
|
|
77
|
-
|
|
78
|
-
declare module 'vue-router' {
|
|
79
|
-
interface RouteMeta {
|
|
80
|
-
// inherited from frontmatter
|
|
81
|
-
layout: string
|
|
82
|
-
name?: string
|
|
83
|
-
class?: string
|
|
84
|
-
clicks?: number
|
|
85
|
-
transition?: string | TransitionGroupProps | undefined
|
|
86
|
-
preload?: boolean
|
|
87
|
-
|
|
88
|
-
// slide info
|
|
89
|
-
slide?: {
|
|
90
|
-
start: number
|
|
91
|
-
end: number
|
|
92
|
-
note?: string
|
|
93
|
-
noteHTML?: string
|
|
94
|
-
id: number
|
|
95
|
-
no: number
|
|
96
|
-
filepath: string
|
|
97
|
-
title?: string
|
|
98
|
-
level?: number
|
|
99
|
-
raw: string
|
|
100
|
-
content: string
|
|
101
|
-
frontmatter: Record<string, any>
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// private fields
|
|
105
|
-
__clicksContext: null | ClicksContext
|
|
106
|
-
__preloaded?: boolean
|
|
107
|
-
}
|
|
108
|
-
}
|
package/setup/codemirror.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Ref, WritableComputedRef } from 'vue'
|
|
2
|
+
import { onClickOutside } from '@vueuse/core'
|
|
2
3
|
import { watch } from 'vue'
|
|
3
|
-
import
|
|
4
|
+
import CodeMirror from 'codemirror'
|
|
4
5
|
import 'codemirror/mode/javascript/javascript'
|
|
5
6
|
import 'codemirror/mode/css/css'
|
|
6
7
|
import 'codemirror/mode/markdown/markdown'
|
|
@@ -9,8 +10,6 @@ import 'codemirror/mode/htmlmixed/htmlmixed'
|
|
|
9
10
|
import 'codemirror/addon/display/placeholder'
|
|
10
11
|
import 'codemirror/lib/codemirror.css'
|
|
11
12
|
|
|
12
|
-
const CodeMirror = _CodeMirror.default ?? ('fromTextArea' in _CodeMirror ? _CodeMirror : globalThis.CodeMirror)
|
|
13
|
-
|
|
14
13
|
export async function useCodeMirror(
|
|
15
14
|
textarea: Ref<HTMLTextAreaElement | null | undefined>,
|
|
16
15
|
input: Ref<string> | WritableComputedRef<string>,
|
|
@@ -47,5 +46,11 @@ export async function useCodeMirror(
|
|
|
47
46
|
{ immediate: true },
|
|
48
47
|
)
|
|
49
48
|
|
|
49
|
+
onClickOutside(cm.getWrapperElement(), () => {
|
|
50
|
+
const el = cm.getInputField()
|
|
51
|
+
if (document.activeElement === el)
|
|
52
|
+
el.blur()
|
|
53
|
+
})
|
|
54
|
+
|
|
50
55
|
return cm
|
|
51
56
|
}
|