@slidev/client 0.48.0-beta.0 → 0.48.0-beta.10
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 +4 -3
- package/builtin/KaTexBlockWrapper.vue +4 -3
- package/builtin/RenderWhen.vue +3 -3
- package/builtin/SlideCurrentNo.vue +2 -3
- package/builtin/SlidesTotal.vue +3 -4
- package/builtin/SlidevVideo.vue +8 -6
- package/builtin/Toc.vue +3 -3
- package/builtin/Tweet.vue +4 -15
- package/builtin/VClickGap.vue +3 -5
- package/builtin/VClicks.ts +9 -9
- package/composables/useClicks.ts +16 -13
- package/composables/useTweetScript.ts +17 -0
- package/constants.ts +56 -8
- package/context.ts +70 -0
- package/internals/DrawingControls.vue +66 -46
- package/internals/DrawingLayer.vue +3 -2
- package/internals/Editor.vue +68 -34
- package/internals/IconButton.vue +15 -0
- package/internals/InfoDialog.vue +1 -1
- package/internals/Modal.vue +1 -1
- package/internals/NavControls.vue +37 -68
- package/internals/NoteDisplay.vue +1 -1
- package/internals/NoteEditor.vue +10 -6
- package/internals/NoteStatic.vue +5 -6
- package/internals/PrintContainer.vue +3 -2
- package/internals/PrintSlideClick.vue +3 -2
- package/internals/RecordingControls.vue +10 -14
- package/internals/RecordingDialog.vue +2 -3
- package/internals/SlideContainer.vue +7 -6
- package/internals/SlideWrapper.ts +28 -12
- package/internals/SlidesOverview.vue +19 -9
- package/logic/drawings.ts +6 -3
- package/logic/nav.ts +6 -6
- package/logic/note.ts +7 -7
- package/main.ts +5 -3
- package/modules/context.ts +4 -3
- package/modules/{directives.ts → v-click.ts} +15 -15
- package/modules/v-mark.ts +159 -0
- package/package.json +21 -13
- package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
- package/{internals/NotesView.vue → pages/notes.vue} +9 -12
- package/pages/overview.vue +157 -0
- package/{internals/Play.vue → pages/play.vue} +10 -10
- package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +7 -5
- package/{internals/Presenter.vue → pages/presenter.vue} +24 -22
- package/{internals/Print.vue → pages/print.vue} +2 -2
- package/routes.ts +25 -19
- package/setup/codemirror.ts +7 -0
- package/state/index.ts +11 -9
- package/styles/index.css +5 -0
- package/styles/layouts-base.css +6 -4
- package/styles/vars.css +1 -0
- package/uno.config.ts +6 -2
- package/internals/HiddenText.vue +0 -9
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { nextTick, onMounted, reactive, ref } from 'vue'
|
|
3
|
+
import { useHead } from '@unhead/vue'
|
|
4
|
+
import { themeVars } from '../env'
|
|
5
|
+
import { rawRoutes } from '../logic/nav'
|
|
6
|
+
import { useFixedClicks } from '../composables/useClicks'
|
|
7
|
+
import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
|
|
8
|
+
import { getSlideClass } from '../utils'
|
|
9
|
+
import SlideContainer from '../internals/SlideContainer.vue'
|
|
10
|
+
import SlideWrapper from '../internals/SlideWrapper'
|
|
11
|
+
import DrawingPreview from '../internals/DrawingPreview.vue'
|
|
12
|
+
import IconButton from '../internals/IconButton.vue'
|
|
13
|
+
import NoteEditor from '../internals/NoteEditor.vue'
|
|
14
|
+
|
|
15
|
+
const cardWidth = 450
|
|
16
|
+
|
|
17
|
+
useHead({
|
|
18
|
+
title: 'List Overview',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const blocks: Map<number, HTMLElement> = reactive(new Map())
|
|
22
|
+
const activeBlocks = ref<number[]>([])
|
|
23
|
+
const edittingNote = ref<number | null>(null)
|
|
24
|
+
|
|
25
|
+
function isElementInViewport(el: HTMLElement) {
|
|
26
|
+
const rect = el.getBoundingClientRect()
|
|
27
|
+
const delta = 20
|
|
28
|
+
return (
|
|
29
|
+
rect.top >= 0 - delta
|
|
30
|
+
&& rect.left >= 0 - delta
|
|
31
|
+
&& rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + delta
|
|
32
|
+
&& rect.right <= (window.innerWidth || document.documentElement.clientWidth) + delta
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function checkActiveBlocks() {
|
|
37
|
+
const active: number[] = []
|
|
38
|
+
Array.from(blocks.entries())
|
|
39
|
+
.forEach(([idx, el]) => {
|
|
40
|
+
if (isElementInViewport(el))
|
|
41
|
+
active.push(idx)
|
|
42
|
+
})
|
|
43
|
+
activeBlocks.value = active
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function openSlideInNewTab(path: string) {
|
|
47
|
+
const a = document.createElement('a')
|
|
48
|
+
a.target = '_blank'
|
|
49
|
+
a.href = path
|
|
50
|
+
a.click()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function scrollToSlide(idx: number) {
|
|
54
|
+
const el = blocks.get(idx)
|
|
55
|
+
if (el)
|
|
56
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
onMounted(() => {
|
|
60
|
+
nextTick(() => {
|
|
61
|
+
checkActiveBlocks()
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<div class="h-screen w-screen of-hidden flex">
|
|
68
|
+
<nav class="h-full flex flex-col border-r border-main p2 select-none">
|
|
69
|
+
<div class="flex flex-col flex-auto items-center justify-center group gap-1">
|
|
70
|
+
<div
|
|
71
|
+
v-for="(route, idx) of rawRoutes"
|
|
72
|
+
:key="route.path"
|
|
73
|
+
class="relative"
|
|
74
|
+
>
|
|
75
|
+
<button
|
|
76
|
+
class="relative transition duration-300 w-8 h-8 rounded hover:bg-active hover:op100"
|
|
77
|
+
:class="activeBlocks.includes(idx) ? 'op100 text-primary bg-gray:5' : 'op20'"
|
|
78
|
+
@click="scrollToSlide(idx)"
|
|
79
|
+
>
|
|
80
|
+
<div>{{ idx + 1 }}</div>
|
|
81
|
+
</button>
|
|
82
|
+
<div
|
|
83
|
+
v-if="route.meta?.slide?.title"
|
|
84
|
+
class="pointer-events-none select-none absolute left-110% bg-main top-50% translate-y--50% ws-nowrap z-10 px2 shadow-xl rounded border border-main transition duration-400 op0 group-hover:op100"
|
|
85
|
+
:class="activeBlocks.includes(idx) ? 'text-primary' : 'text-main important-text-op-50'"
|
|
86
|
+
>
|
|
87
|
+
{{ route.meta?.slide?.title }}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
<IconButton
|
|
92
|
+
v-if="!isColorSchemaConfigured"
|
|
93
|
+
:title="isDark ? 'Switch to light mode theme' : 'Switch to dark mode theme'"
|
|
94
|
+
@click="toggleDark()"
|
|
95
|
+
>
|
|
96
|
+
<carbon-moon v-if="isDark" />
|
|
97
|
+
<carbon-sun v-else />
|
|
98
|
+
</IconButton>
|
|
99
|
+
</nav>
|
|
100
|
+
<main
|
|
101
|
+
class="flex-1 h-full of-auto"
|
|
102
|
+
:style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
|
|
103
|
+
@scroll="checkActiveBlocks"
|
|
104
|
+
>
|
|
105
|
+
<div
|
|
106
|
+
v-for="(route, idx) of rawRoutes"
|
|
107
|
+
:key="route.path"
|
|
108
|
+
:ref="el => blocks.set(idx, el as any)"
|
|
109
|
+
class="relative border-t border-main of-hidden flex gap-4 min-h-50 group"
|
|
110
|
+
>
|
|
111
|
+
<div class="select-none w-13 text-right my4">
|
|
112
|
+
<div class="text-3xl op20">
|
|
113
|
+
{{ idx + 1 }}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div
|
|
117
|
+
class="border rounded border-main overflow-hidden bg-main my5 select-none h-max"
|
|
118
|
+
:style="themeVars"
|
|
119
|
+
@dblclick="openSlideInNewTab(route.path)"
|
|
120
|
+
>
|
|
121
|
+
<SlideContainer
|
|
122
|
+
:key="route.path"
|
|
123
|
+
:width="cardWidth"
|
|
124
|
+
:clicks-disabled="true"
|
|
125
|
+
class="pointer-events-none important:[&_*]:select-none"
|
|
126
|
+
>
|
|
127
|
+
<SlideWrapper
|
|
128
|
+
:is="route.component"
|
|
129
|
+
v-if="route?.component"
|
|
130
|
+
:clicks-context="useFixedClicks(route, 99999)[1]"
|
|
131
|
+
:class="getSlideClass(route)"
|
|
132
|
+
:route="route"
|
|
133
|
+
render-context="overview"
|
|
134
|
+
/>
|
|
135
|
+
<DrawingPreview :page="+route.path" />
|
|
136
|
+
</SlideContainer>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="py3 mt-0.5 mr--8 ml--4 op0 transition group-hover:op100">
|
|
139
|
+
<IconButton
|
|
140
|
+
title="Edit Note"
|
|
141
|
+
class="rounded-full w-9 h-9 text-sm"
|
|
142
|
+
:class="edittingNote === idx ? 'important:op0' : ''"
|
|
143
|
+
@click="edittingNote = idx"
|
|
144
|
+
>
|
|
145
|
+
<carbon:pen />
|
|
146
|
+
</IconButton>
|
|
147
|
+
</div>
|
|
148
|
+
<NoteEditor
|
|
149
|
+
:no="idx"
|
|
150
|
+
class="max-w-250 w-250 text-lg rounded p3"
|
|
151
|
+
:editing="edittingNote === idx"
|
|
152
|
+
@update:editing="edittingNote = null"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</main>
|
|
156
|
+
</div>
|
|
157
|
+
</template>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, shallowRef } from 'vue'
|
|
3
|
-
import { isScreenVertical, showEditor, slideScale, windowSize } from '../state'
|
|
3
|
+
import { isEditorVertical, isScreenVertical, showEditor, slideScale, windowSize } from '../state'
|
|
4
4
|
import { isEmbedded, isPrintMode, next, prev, useSwipeControls } from '../logic/nav'
|
|
5
5
|
import { isDrawing } from '../logic/drawings'
|
|
6
6
|
import { registerShortcuts } from '../logic/shortcuts'
|
|
7
7
|
import { configs, themeVars } from '../env'
|
|
8
|
-
import Controls from '
|
|
9
|
-
import SlideContainer from '
|
|
10
|
-
import NavControls from '
|
|
11
|
-
import SlidesShow from '
|
|
12
|
-
import PrintStyle from '
|
|
8
|
+
import Controls from '../internals/Controls.vue'
|
|
9
|
+
import SlideContainer from '../internals/SlideContainer.vue'
|
|
10
|
+
import NavControls from '../internals/NavControls.vue'
|
|
11
|
+
import SlidesShow from '../internals/SlidesShow.vue'
|
|
12
|
+
import PrintStyle from '../internals/PrintStyle.vue'
|
|
13
13
|
|
|
14
14
|
registerShortcuts()
|
|
15
15
|
|
|
@@ -33,16 +33,16 @@ const persistNav = computed(() => isScreenVertical.value || showEditor.value)
|
|
|
33
33
|
|
|
34
34
|
const Editor = shallowRef<any>()
|
|
35
35
|
if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
|
|
36
|
-
import('
|
|
36
|
+
import('../internals/Editor.vue').then(v => Editor.value = v.default)
|
|
37
37
|
|
|
38
38
|
const DrawingControls = shallowRef<any>()
|
|
39
39
|
if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
40
|
-
import('
|
|
40
|
+
import('../internals/DrawingControls.vue').then(v => DrawingControls.value = v.default)
|
|
41
41
|
</script>
|
|
42
42
|
|
|
43
43
|
<template>
|
|
44
44
|
<PrintStyle v-if="isPrintMode" />
|
|
45
|
-
<div id="page-root" ref="root" class="grid grid-cols-[1fr_max-content]" :style="themeVars">
|
|
45
|
+
<div id="page-root" ref="root" class="grid" :class="isEditorVertical ? 'grid-rows-[1fr_max-content]' : 'grid-cols-[1fr_max-content]'" :style="themeVars">
|
|
46
46
|
<SlideContainer
|
|
47
47
|
class="w-full h-full"
|
|
48
48
|
:style="{ background: 'var(--slidev-slide-container-background, black)' }"
|
|
@@ -58,7 +58,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
58
58
|
<div
|
|
59
59
|
class="absolute bottom-0 left-0 transition duration-300 opacity-0 hover:opacity-100"
|
|
60
60
|
:class="[
|
|
61
|
-
persistNav ? 'opacity-100 right-0' : 'opacity-0 p-2',
|
|
61
|
+
persistNav ? '!opacity-100 right-0' : 'opacity-0 p-2',
|
|
62
62
|
isDrawing ? 'pointer-events-none' : '',
|
|
63
63
|
]"
|
|
64
64
|
>
|
|
@@ -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, themeVars } from '
|
|
6
|
-
import { rawRoutes, total } from '
|
|
7
|
-
import NoteDisplay from '
|
|
5
|
+
import { configs, themeVars } from '../../env'
|
|
6
|
+
import { rawRoutes, total } from '../../logic/nav'
|
|
7
|
+
import NoteDisplay from '../../internals/NoteDisplay.vue'
|
|
8
8
|
|
|
9
9
|
useStyleTag(`
|
|
10
10
|
@page {
|
|
@@ -24,7 +24,9 @@ html #page-root {
|
|
|
24
24
|
}
|
|
25
25
|
`)
|
|
26
26
|
|
|
27
|
-
useHead({
|
|
27
|
+
useHead({
|
|
28
|
+
title: `Notes - ${configs.title}`,
|
|
29
|
+
})
|
|
28
30
|
|
|
29
31
|
const slidesWithNote = computed(() => rawRoutes
|
|
30
32
|
.map(route => route.meta?.slide)
|
|
@@ -56,7 +58,7 @@ const slidesWithNote = computed(() => rawRoutes
|
|
|
56
58
|
</h2>
|
|
57
59
|
<NoteDisplay :note-html="slide!.noteHTML" class="max-w-full" />
|
|
58
60
|
</div>
|
|
59
|
-
<hr v-if="index < slidesWithNote.length - 1" class="border-
|
|
61
|
+
<hr v-if="index < slidesWithNote.length - 1" class="border-main mb-8">
|
|
60
62
|
</div>
|
|
61
63
|
</div>
|
|
62
64
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
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, currentPage, currentRoute, hasNext, nextRoute, queryClicks, rawRoutes, total, useSwipeControls } from '../logic/nav'
|
|
5
|
+
import { clicksContext, currentPage, currentRoute, currentSlideId, hasNext, nextRoute, queryClicks, rawRoutes, total, useSwipeControls } from '../logic/nav'
|
|
6
6
|
import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showOverview, showPresenterCursor } from '../state'
|
|
7
7
|
import { configs, themeVars } from '../env'
|
|
8
8
|
import { sharedState } from '../state/shared'
|
|
@@ -11,16 +11,16 @@ import { getSlideClass } from '../utils'
|
|
|
11
11
|
import { useTimer } from '../logic/utils'
|
|
12
12
|
import { isDrawing } from '../logic/drawings'
|
|
13
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
|
|
14
|
+
import SlideWrapper from '../internals/SlideWrapper'
|
|
15
|
+
import SlideContainer from '../internals/SlideContainer.vue'
|
|
16
|
+
import NavControls from '../internals/NavControls.vue'
|
|
17
|
+
import SlidesOverview from '../internals/SlidesOverview.vue'
|
|
18
|
+
import NoteEditor from '../internals/NoteEditor.vue'
|
|
19
|
+
import NoteStatic from '../internals/NoteStatic.vue'
|
|
20
|
+
import Goto from '../internals/Goto.vue'
|
|
21
|
+
import SlidesShow from '../internals/SlidesShow.vue'
|
|
22
|
+
import DrawingControls from '../internals/DrawingControls.vue'
|
|
23
|
+
import IconButton from '../internals/IconButton.vue'
|
|
24
24
|
|
|
25
25
|
const main = ref<HTMLDivElement>()
|
|
26
26
|
|
|
@@ -54,7 +54,7 @@ watch([currentRoute, queryClicks], () => {
|
|
|
54
54
|
|
|
55
55
|
const Editor = shallowRef<any>()
|
|
56
56
|
if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
|
|
57
|
-
import('
|
|
57
|
+
import('../internals/Editor.vue').then(v => Editor.value = v.default)
|
|
58
58
|
|
|
59
59
|
// sync presenter cursor
|
|
60
60
|
onMounted(() => {
|
|
@@ -140,31 +140,33 @@ onMounted(() => {
|
|
|
140
140
|
<div v-else class="grid-section note grid grid-rows-[1fr_min-content] overflow-hidden">
|
|
141
141
|
<NoteEditor
|
|
142
142
|
v-if="__DEV__"
|
|
143
|
+
:key="`edit-${currentSlideId}`"
|
|
144
|
+
:no="currentSlideId"
|
|
143
145
|
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
|
|
144
146
|
:editing="notesEditing"
|
|
145
147
|
:style="{ fontSize: `${presenterNotesFontSize}em` }"
|
|
146
148
|
/>
|
|
147
149
|
<NoteStatic
|
|
148
150
|
v-else
|
|
151
|
+
:key="`static-${currentSlideId}`"
|
|
152
|
+
:no="currentSlideId"
|
|
149
153
|
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
|
|
150
154
|
:style="{ fontSize: `${presenterNotesFontSize}em` }"
|
|
151
155
|
/>
|
|
152
156
|
<div class="border-t border-main py-1 px-2 text-sm">
|
|
153
|
-
<
|
|
154
|
-
<HiddenText text="Increase font size" />
|
|
157
|
+
<IconButton title="Increase font size" @click="increasePresenterFontSize">
|
|
155
158
|
<carbon:zoom-in />
|
|
156
|
-
</
|
|
157
|
-
<
|
|
158
|
-
<HiddenText text="Decrease font size" />
|
|
159
|
+
</IconButton>
|
|
160
|
+
<IconButton title="Decrease font size" @click="decreasePresenterFontSize">
|
|
159
161
|
<carbon:zoom-out />
|
|
160
|
-
</
|
|
161
|
-
<
|
|
162
|
+
</IconButton>
|
|
163
|
+
<IconButton
|
|
162
164
|
v-if="__DEV__"
|
|
163
|
-
|
|
165
|
+
title="Edit Notes"
|
|
166
|
+
@click="notesEditing = !notesEditing"
|
|
164
167
|
>
|
|
165
|
-
<HiddenText text="Edit Notes" />
|
|
166
168
|
<carbon:edit />
|
|
167
|
-
</
|
|
169
|
+
</IconButton>
|
|
168
170
|
</div>
|
|
169
171
|
</div>
|
|
170
172
|
<div class="grid-section bottom">
|
|
@@ -3,8 +3,8 @@ import { watchEffect } from 'vue'
|
|
|
3
3
|
import { windowSize } from '../state'
|
|
4
4
|
import { isPrintMode } from '../logic/nav'
|
|
5
5
|
import { themeVars } from '../env'
|
|
6
|
-
import PrintContainer from '
|
|
7
|
-
import PrintStyle from '
|
|
6
|
+
import PrintContainer from '../internals/PrintContainer.vue'
|
|
7
|
+
import PrintStyle from '../internals/PrintStyle.vue'
|
|
8
8
|
|
|
9
9
|
watchEffect(() => {
|
|
10
10
|
if (isPrintMode)
|
package/routes.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
|
2
2
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
|
3
3
|
import type { TransitionGroupProps } from 'vue'
|
|
4
|
-
import type { ClicksContext } from '@slidev/types'
|
|
5
|
-
import Play from './internals/Play.vue'
|
|
6
|
-
import Print from './internals/Print.vue'
|
|
4
|
+
import type { ClicksContext, SlideInfo } from '@slidev/types'
|
|
7
5
|
|
|
8
6
|
// @ts-expect-error missing types
|
|
9
7
|
import _rawRoutes, { redirects } from '/@slidev/routes'
|
|
@@ -17,13 +15,19 @@ export const routes: RouteRecordRaw[] = [
|
|
|
17
15
|
{
|
|
18
16
|
name: 'play',
|
|
19
17
|
path: '/',
|
|
20
|
-
component:
|
|
18
|
+
component: () => import('./pages/play.vue'),
|
|
21
19
|
children: [
|
|
22
20
|
...rawRoutes,
|
|
23
21
|
...redirects,
|
|
24
22
|
],
|
|
25
23
|
},
|
|
26
|
-
{
|
|
24
|
+
{
|
|
25
|
+
name: 'print',
|
|
26
|
+
path: '/print',
|
|
27
|
+
component: () => import('./pages/print.vue'),
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
// Redirects
|
|
27
31
|
{ path: '', redirect: { path: '/1' } },
|
|
28
32
|
{ path: '/:pathMatch(.*)', redirect: { path: '/1' } },
|
|
29
33
|
]
|
|
@@ -42,24 +46,33 @@ if (__SLIDEV_FEATURE_PRESENTER__) {
|
|
|
42
46
|
return { path: `/${to.params.no}` }
|
|
43
47
|
return { path: '' }
|
|
44
48
|
}
|
|
45
|
-
|
|
49
|
+
|
|
50
|
+
routes.push({
|
|
51
|
+
path: '/presenter/print',
|
|
52
|
+
component: () => import('./pages/presenter/print.vue'),
|
|
53
|
+
})
|
|
46
54
|
if (__SLIDEV_HAS_SERVER__) {
|
|
47
55
|
routes.push({
|
|
48
56
|
name: 'entry',
|
|
49
57
|
path: '/entry',
|
|
50
|
-
component: () => import('./
|
|
58
|
+
component: () => import('./pages/entry.vue'),
|
|
59
|
+
})
|
|
60
|
+
routes.push({
|
|
61
|
+
name: 'overview',
|
|
62
|
+
path: '/overview',
|
|
63
|
+
component: () => import('./pages/overview.vue'),
|
|
51
64
|
})
|
|
52
65
|
routes.push({
|
|
53
66
|
name: 'notes',
|
|
54
67
|
path: '/notes',
|
|
55
|
-
component: () => import('./
|
|
68
|
+
component: () => import('./pages/notes.vue'),
|
|
56
69
|
beforeEnter: passwordGuard,
|
|
57
70
|
})
|
|
58
71
|
}
|
|
59
72
|
routes.push({
|
|
60
73
|
name: 'presenter',
|
|
61
74
|
path: '/presenter/:no',
|
|
62
|
-
component: () => import('./
|
|
75
|
+
component: () => import('./pages/presenter.vue'),
|
|
63
76
|
beforeEnter: passwordGuard,
|
|
64
77
|
})
|
|
65
78
|
routes.push({
|
|
@@ -86,19 +99,12 @@ declare module 'vue-router' {
|
|
|
86
99
|
preload?: boolean
|
|
87
100
|
|
|
88
101
|
// slide info
|
|
89
|
-
slide?: {
|
|
102
|
+
slide?: Omit<SlideInfo, 'source'> & {
|
|
103
|
+
noteHTML: string
|
|
104
|
+
filepath: string
|
|
90
105
|
start: number
|
|
91
|
-
end: number
|
|
92
|
-
note?: string
|
|
93
|
-
noteHTML?: string
|
|
94
106
|
id: number
|
|
95
107
|
no: number
|
|
96
|
-
filepath: string
|
|
97
|
-
title?: string
|
|
98
|
-
level?: number
|
|
99
|
-
raw: string
|
|
100
|
-
content: string
|
|
101
|
-
frontmatter: Record<string, any>
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
// private fields
|
package/setup/codemirror.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Ref, WritableComputedRef } from 'vue'
|
|
2
|
+
import { onClickOutside } from '@vueuse/core'
|
|
2
3
|
import { watch } from 'vue'
|
|
3
4
|
import * as _CodeMirror from 'codemirror'
|
|
4
5
|
import 'codemirror/mode/javascript/javascript'
|
|
@@ -47,5 +48,11 @@ export async function useCodeMirror(
|
|
|
47
48
|
{ immediate: true },
|
|
48
49
|
)
|
|
49
50
|
|
|
51
|
+
onClickOutside(cm.getWrapperElement(), () => {
|
|
52
|
+
const el = cm.getInputField()
|
|
53
|
+
if (document.activeElement === el)
|
|
54
|
+
el.blur()
|
|
55
|
+
})
|
|
56
|
+
|
|
50
57
|
return cm
|
|
51
58
|
}
|
package/state/index.ts
CHANGED
|
@@ -13,24 +13,26 @@ export const breakpoints = useBreakpoints({
|
|
|
13
13
|
})
|
|
14
14
|
export const windowSize = useWindowSize()
|
|
15
15
|
export const magicKeys = useMagicKeys()
|
|
16
|
-
export const isScreenVertical = computed(() => windowSize.height.value - windowSize.width.value / slideAspect >
|
|
16
|
+
export const isScreenVertical = computed(() => windowSize.height.value - windowSize.width.value / slideAspect > 120)
|
|
17
17
|
export const fullscreen = useFullscreen(isClient ? document.body : null)
|
|
18
18
|
|
|
19
19
|
export const activeElement = useActiveElement()
|
|
20
20
|
export const isInputting = computed(() => ['INPUT', 'TEXTAREA'].includes(activeElement.value?.tagName || '') || activeElement.value?.classList.contains('CodeMirror-code'))
|
|
21
21
|
export const isOnFocus = computed(() => ['BUTTON', 'A'].includes(activeElement.value?.tagName || ''))
|
|
22
22
|
|
|
23
|
-
export const currentCamera = useLocalStorage<string>('slidev-camera', 'default')
|
|
24
|
-
export const currentMic = useLocalStorage<string>('slidev-mic', 'default')
|
|
23
|
+
export const currentCamera = useLocalStorage<string>('slidev-camera', 'default', { listenToStorageChanges: false })
|
|
24
|
+
export const currentMic = useLocalStorage<string>('slidev-mic', 'default', { listenToStorageChanges: false })
|
|
25
25
|
export const slideScale = useLocalStorage<number>('slidev-scale', 0)
|
|
26
26
|
|
|
27
|
-
export const showOverview = useLocalStorage('slidev-show-overview', false)
|
|
28
|
-
export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true)
|
|
29
|
-
export const showEditor = useLocalStorage('slidev-show-editor', false)
|
|
30
|
-
export const
|
|
27
|
+
export const showOverview = useLocalStorage('slidev-show-overview', false, { listenToStorageChanges: false })
|
|
28
|
+
export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true, { listenToStorageChanges: false })
|
|
29
|
+
export const showEditor = useLocalStorage('slidev-show-editor', false, { listenToStorageChanges: false })
|
|
30
|
+
export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false, { listenToStorageChanges: false })
|
|
31
|
+
export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 318, { listenToStorageChanges: false })
|
|
32
|
+
export const editorHeight = useLocalStorage('slidev-editor-height', isClient ? window.innerHeight * 0.4 : 300, { listenToStorageChanges: false })
|
|
31
33
|
|
|
32
|
-
export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1)
|
|
33
|
-
export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1)
|
|
34
|
+
export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1, { listenToStorageChanges: false })
|
|
35
|
+
export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1, { listenToStorageChanges: false })
|
|
34
36
|
|
|
35
37
|
export function togglePresenterLayout() {
|
|
36
38
|
presenterLayout.value = presenterLayout.value + 1
|
package/styles/index.css
CHANGED
package/styles/layouts-base.css
CHANGED
|
@@ -54,7 +54,9 @@
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
blockquote {
|
|
57
|
-
|
|
57
|
+
background: var(--slidev-code-background);
|
|
58
|
+
color: var(--slidev-code-foreground);
|
|
59
|
+
@apply text-sm px-2 py-1 border-primary border-l rounded;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
blockquote > * {
|
|
@@ -66,7 +68,7 @@
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
tr {
|
|
69
|
-
@apply border-b border-
|
|
71
|
+
@apply border-b border-main;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
th {
|
|
@@ -74,7 +76,7 @@
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
a {
|
|
77
|
-
@apply border-current border-b border-dashed hover:text
|
|
79
|
+
@apply border-current border-b border-dashed hover:text-primary hover:border-solid;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
td, th {
|
|
@@ -86,7 +88,7 @@
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
kbd {
|
|
89
|
-
@apply border border-
|
|
91
|
+
@apply border border-main border-b-2 rounded;
|
|
90
92
|
@apply bg-gray-400 bg-opacity-5 py-0.5 px-1 text-xs font-mono;
|
|
91
93
|
}
|
|
92
94
|
}
|
package/styles/vars.css
CHANGED
package/uno.config.ts
CHANGED
|
@@ -15,9 +15,13 @@ export default defineConfig({
|
|
|
15
15
|
'prose',
|
|
16
16
|
],
|
|
17
17
|
shortcuts: {
|
|
18
|
-
'bg-main': 'bg-white
|
|
18
|
+
'bg-main': 'bg-white dark:bg-[#121212]',
|
|
19
19
|
'bg-active': 'bg-gray-400/10',
|
|
20
|
-
'border-main': 'border-gray
|
|
20
|
+
'border-main': 'border-gray/20',
|
|
21
|
+
'text-main': 'text-[#181818] dark:text-[#ddd]',
|
|
22
|
+
'text-primary': 'color-$slidev-theme-primary',
|
|
23
|
+
'bg-primary': 'bg-$slidev-theme-primary',
|
|
24
|
+
'border-primary': 'border-$slidev-theme-primary',
|
|
21
25
|
'abs-tl': 'absolute top-0 left-0',
|
|
22
26
|
'abs-tr': 'absolute top-0 right-0',
|
|
23
27
|
'abs-b': 'absolute bottom-0 left-0 right-0',
|