@slidev/client 0.39.0 → 0.40.0
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/constants.ts +5 -0
- package/internals/Draggable.vue +2 -2
- package/internals/{NoteViewer.vue → NoteDisplay.vue} +13 -3
- package/internals/NoteEditor.vue +3 -3
- package/internals/NoteStatic.vue +3 -3
- package/internals/NotesView.vue +76 -0
- package/internals/Presenter.vue +1 -1
- package/internals/PresenterPrint.vue +3 -3
- package/internals/SlidesShow.vue +1 -1
- package/internals/WebCamera.vue +3 -3
- package/logic/dark.ts +2 -2
- package/logic/drawings.ts +4 -4
- package/logic/nav.ts +1 -0
- package/logic/recording.ts +2 -2
- package/package.json +10 -10
- package/routes.ts +24 -17
- package/setup/main.ts +1 -1
- package/setup/monaco.ts +1 -1
- package/setup/root.ts +25 -2
- package/setup/shortcuts.ts +1 -1
- package/state/index.ts +8 -8
- package/state/shared.ts +18 -1
package/constants.ts
CHANGED
|
@@ -19,3 +19,8 @@ export const CLASS_VCLICK_GONE = 'slidev-vclick-gone'
|
|
|
19
19
|
export const CLASS_VCLICK_HIDDEN_EXP = 'slidev-vclick-hidden-explicitly'
|
|
20
20
|
export const CLASS_VCLICK_CURRENT = 'slidev-vclick-current'
|
|
21
21
|
export const CLASS_VCLICK_PRIOR = 'slidev-vclick-prior'
|
|
22
|
+
|
|
23
|
+
export const TRUST_ORIGINS = [
|
|
24
|
+
'localhost',
|
|
25
|
+
'127.0.0.1',
|
|
26
|
+
]
|
package/internals/Draggable.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref } from 'vue'
|
|
3
|
-
import { useDraggable,
|
|
3
|
+
import { useDraggable, useLocalStorage } from '@vueuse/core'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
storageKey?: string
|
|
@@ -10,7 +10,7 @@ const props = defineProps<{
|
|
|
10
10
|
const el = ref<HTMLElement | null>(null)
|
|
11
11
|
const initial = props.initial ?? { x: 0, y: 0 }
|
|
12
12
|
const point = props.storageKey
|
|
13
|
-
?
|
|
13
|
+
? useLocalStorage(props.storageKey, initial)
|
|
14
14
|
: ref(initial)
|
|
15
15
|
const { style } = useDraggable(el, { initialValue: point })
|
|
16
16
|
</script>
|
|
@@ -3,6 +3,7 @@ const props = defineProps<{
|
|
|
3
3
|
class?: string
|
|
4
4
|
noteHtml?: string
|
|
5
5
|
note?: string
|
|
6
|
+
placeholder?: string
|
|
6
7
|
}>()
|
|
7
8
|
|
|
8
9
|
defineEmits(['click'])
|
|
@@ -17,10 +18,19 @@ defineEmits(['click'])
|
|
|
17
18
|
v-html="noteHtml"
|
|
18
19
|
/>
|
|
19
20
|
<div
|
|
20
|
-
v-else
|
|
21
|
+
v-else-if="note"
|
|
21
22
|
class="prose overflow-auto outline-none"
|
|
22
23
|
:class="props.class"
|
|
23
24
|
@click="$emit('click')"
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
>
|
|
26
|
+
<p v-text="note" />
|
|
27
|
+
</div>
|
|
28
|
+
<div
|
|
29
|
+
v-else
|
|
30
|
+
class="prose overflow-auto outline-none opacity-50 italic"
|
|
31
|
+
:class="props.class"
|
|
32
|
+
@click="$emit('click')"
|
|
33
|
+
>
|
|
34
|
+
<p v-text="props.placeholder || 'No notes.'" />
|
|
35
|
+
</div>
|
|
26
36
|
</template>
|
package/internals/NoteEditor.vue
CHANGED
|
@@ -3,7 +3,7 @@ import { ignorableWatch, onClickOutside } from '@vueuse/core'
|
|
|
3
3
|
import { nextTick, ref, watch } from 'vue'
|
|
4
4
|
import { currentSlideId } from '../logic/nav'
|
|
5
5
|
import { useDynamicSlideInfo } from '../logic/note'
|
|
6
|
-
import
|
|
6
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
7
7
|
|
|
8
8
|
const props = defineProps({
|
|
9
9
|
class: {
|
|
@@ -60,11 +60,11 @@ onClickOutside(input, () => {
|
|
|
60
60
|
</script>
|
|
61
61
|
|
|
62
62
|
<template>
|
|
63
|
-
<
|
|
63
|
+
<NoteDisplay
|
|
64
64
|
v-if="!editing && note"
|
|
65
65
|
:class="props.class"
|
|
66
66
|
:note="note"
|
|
67
|
-
:note-html="info?.
|
|
67
|
+
:note-html="info?.noteHTML"
|
|
68
68
|
@click="switchNoteEdit"
|
|
69
69
|
/>
|
|
70
70
|
<textarea
|
package/internals/NoteStatic.vue
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import { currentRoute } from '../logic/nav'
|
|
4
|
-
import
|
|
4
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
7
|
class?: string
|
|
8
8
|
}>()
|
|
9
9
|
|
|
10
10
|
const note = computed(() => currentRoute.value?.meta?.slide?.note)
|
|
11
|
-
const noteHtml = computed(() => currentRoute.value?.meta?.slide?.
|
|
11
|
+
const noteHtml = computed(() => currentRoute.value?.meta?.slide?.noteHTML)
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<template>
|
|
15
|
-
<
|
|
15
|
+
<NoteDisplay
|
|
16
16
|
:class="props.class"
|
|
17
17
|
:note="note"
|
|
18
18
|
:note-html="noteHtml"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useHead } from '@vueuse/head'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { useLocalStorage } from '@vueuse/core'
|
|
5
|
+
import { configs } from '../env'
|
|
6
|
+
import { sharedState } from '../state/shared'
|
|
7
|
+
import { fullscreen } from '../state'
|
|
8
|
+
import { total } from '../logic/nav'
|
|
9
|
+
import { rawRoutes } from '../routes'
|
|
10
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
11
|
+
|
|
12
|
+
const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
|
|
13
|
+
useHead({
|
|
14
|
+
title: `Notes - ${slideTitle}`,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const { isFullscreen, toggle: toggleFullscreen } = fullscreen
|
|
18
|
+
|
|
19
|
+
const fontSize = useLocalStorage('slidev-notes-font-size', 18)
|
|
20
|
+
const pageNo = computed(() => sharedState.lastUpdate?.type === 'viewer' ? sharedState.viewerPage : sharedState.page)
|
|
21
|
+
const currentRoute = computed(() => rawRoutes.find(i => i.path === `${pageNo.value}`))
|
|
22
|
+
const nextRoute = computed(() => rawRoutes.find(i => i.path === `${pageNo.value + 1}`))
|
|
23
|
+
|
|
24
|
+
function increaseFontSize() {
|
|
25
|
+
fontSize.value = fontSize.value + 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function decreaseFontSize() {
|
|
29
|
+
fontSize.value = fontSize.value - 1
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div
|
|
35
|
+
class="fixed top-0 left-0 h-2px bg-teal-500 transition-all duration-500"
|
|
36
|
+
:style="{ width: `${(pageNo - 1) / total * 100}%` }"
|
|
37
|
+
/>
|
|
38
|
+
<div class="h-full flex flex-col">
|
|
39
|
+
<div
|
|
40
|
+
class="px-5 flex-auto h-full overflow-auto"
|
|
41
|
+
:style="{ fontSize: `${fontSize}px` }"
|
|
42
|
+
>
|
|
43
|
+
<NoteDisplay
|
|
44
|
+
:note="currentRoute?.meta?.slide?.note"
|
|
45
|
+
:note-html="currentRoute?.meta?.slide?.noteHTML"
|
|
46
|
+
:placeholder="`No notes for Slide ${pageNo}.`"
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
<div v-if="nextRoute" class="px-5 py-2 max-h-60 overflow-auto border-t border-gray-400 border-opacity-20">
|
|
50
|
+
<NoteDisplay
|
|
51
|
+
class="opacity-50"
|
|
52
|
+
:note="nextRoute?.meta?.slide?.note"
|
|
53
|
+
:note-html="nextRoute?.meta?.slide?.noteHTML"
|
|
54
|
+
placeholder="No notes for next slide."
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="flex-none border-t border-gray-400 border-opacity-20">
|
|
58
|
+
<div class="flex gap-1 items-center px-6 py-3">
|
|
59
|
+
<button class="icon-btn" @click="toggleFullscreen">
|
|
60
|
+
<carbon:minimize v-if="isFullscreen" />
|
|
61
|
+
<carbon:maximize v-else />
|
|
62
|
+
</button>
|
|
63
|
+
<button class="icon-btn" @click="increaseFontSize">
|
|
64
|
+
<carbon:zoom-in />
|
|
65
|
+
</button>
|
|
66
|
+
<button class="icon-btn" @click="decreaseFontSize">
|
|
67
|
+
<carbon:zoom-out />
|
|
68
|
+
</button>
|
|
69
|
+
<div class="flex-auto" />
|
|
70
|
+
<div class="p2 text-center">
|
|
71
|
+
{{ pageNo }} / {{ total }}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
package/internals/Presenter.vue
CHANGED
|
@@ -118,7 +118,7 @@ onMounted(() => {
|
|
|
118
118
|
class="h-full w-full"
|
|
119
119
|
>
|
|
120
120
|
<SlideWrapper
|
|
121
|
-
:is="nextSlide.route?.component"
|
|
121
|
+
:is="nextSlide.route?.component as any"
|
|
122
122
|
v-model:clicks-elements="nextTabElements"
|
|
123
123
|
:clicks="nextSlide.clicks"
|
|
124
124
|
:clicks-disabled="false"
|
|
@@ -4,7 +4,7 @@ import { useStyleTag } from '@vueuse/core'
|
|
|
4
4
|
import { useHead } from '@vueuse/head'
|
|
5
5
|
import { configs, themeVars } from '../env'
|
|
6
6
|
import { rawRoutes, total } from '../logic/nav'
|
|
7
|
-
import
|
|
7
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
8
8
|
|
|
9
9
|
useStyleTag(`
|
|
10
10
|
@page {
|
|
@@ -29,7 +29,7 @@ useHead({ title: `Notes - ${configs.title}` })
|
|
|
29
29
|
const slidesWithNote = computed(() => rawRoutes
|
|
30
30
|
.slice(0, -1)
|
|
31
31
|
.map(route => route.meta?.slide)
|
|
32
|
-
.filter(slide => slide !== undefined && slide.
|
|
32
|
+
.filter(slide => slide !== undefined && slide.noteHTML !== ''))
|
|
33
33
|
</script>
|
|
34
34
|
|
|
35
35
|
<template>
|
|
@@ -55,7 +55,7 @@ const slidesWithNote = computed(() => rawRoutes
|
|
|
55
55
|
<div class="flex-auto" />
|
|
56
56
|
</div>
|
|
57
57
|
</h2>
|
|
58
|
-
<
|
|
58
|
+
<NoteDisplay :note-html="slide!.noteHTML" class="max-w-full" />
|
|
59
59
|
</div>
|
|
60
60
|
<hr v-if="index < slidesWithNote.length - 1" class="border-gray-400/50 mb-8">
|
|
61
61
|
</div>
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { computed, shallowRef, watch } from 'vue'
|
|
3
3
|
import { clicks, currentRoute, isPresenter, nextRoute, rawRoutes, transition } from '../logic/nav'
|
|
4
4
|
import { getSlideClass } from '../utils'
|
|
5
5
|
import SlideWrapper from './SlideWrapper'
|
package/internals/WebCamera.vue
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useDraggable, useEventListener,
|
|
2
|
+
import { useDraggable, useEventListener, useLocalStorage } from '@vueuse/core'
|
|
3
3
|
import { computed, onMounted, ref, watchEffect } from 'vue'
|
|
4
4
|
import { currentCamera } from '../state'
|
|
5
5
|
import { recorder } from '../logic/recording'
|
|
6
6
|
|
|
7
|
-
const size =
|
|
8
|
-
const position =
|
|
7
|
+
const size = useLocalStorage('slidev-webcam-size', Math.round(Math.min(window.innerHeight, (window.innerWidth) / 8)))
|
|
8
|
+
const position = useLocalStorage('slidev-webcam-pos', {
|
|
9
9
|
x: window.innerWidth - size.value - 30,
|
|
10
10
|
y: window.innerHeight - size.value - 30,
|
|
11
11
|
}, undefined, { deep: true })
|
package/logic/dark.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { isClient,
|
|
1
|
+
import { isClient, useLocalStorage, usePreferredDark, useToggle } from '@vueuse/core'
|
|
2
2
|
import { computed, watch } from 'vue'
|
|
3
3
|
import { configs } from '../env'
|
|
4
4
|
|
|
5
5
|
const preferredDark = usePreferredDark()
|
|
6
|
-
const store =
|
|
6
|
+
const store = useLocalStorage('slidev-color-schema', 'auto')
|
|
7
7
|
|
|
8
8
|
export const isColorSchemaConfigured = computed(() => configs.colorSchema !== 'auto')
|
|
9
9
|
export const isDark = computed<boolean>({
|
package/logic/drawings.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed, markRaw, nextTick, reactive, ref, watch } from 'vue'
|
|
2
2
|
import type { Brush, Options as DrauuOptions, DrawingMode } from 'drauu'
|
|
3
3
|
import { createDrauu } from 'drauu'
|
|
4
|
-
import { toReactive,
|
|
4
|
+
import { toReactive, useLocalStorage } from '@vueuse/core'
|
|
5
5
|
import { drawingState, onPatch, patch } from '../state/drawings'
|
|
6
6
|
import { configs } from '../env'
|
|
7
7
|
import { currentPage, isPresenter } from './nav'
|
|
@@ -16,14 +16,14 @@ export const brushColors = [
|
|
|
16
16
|
'#000000',
|
|
17
17
|
]
|
|
18
18
|
|
|
19
|
-
export const drawingEnabled =
|
|
20
|
-
export const drawingPinned =
|
|
19
|
+
export const drawingEnabled = useLocalStorage('slidev-drawing-enabled', false)
|
|
20
|
+
export const drawingPinned = useLocalStorage('slidev-drawing-pinned', false)
|
|
21
21
|
export const canUndo = ref(false)
|
|
22
22
|
export const canRedo = ref(false)
|
|
23
23
|
export const canClear = ref(false)
|
|
24
24
|
export const isDrawing = ref(false)
|
|
25
25
|
|
|
26
|
-
export const brush = toReactive(
|
|
26
|
+
export const brush = toReactive(useLocalStorage<Brush>('slidev-drawing-brush', {
|
|
27
27
|
color: brushColors[0],
|
|
28
28
|
size: 4,
|
|
29
29
|
mode: 'stylus',
|
package/logic/nav.ts
CHANGED
|
@@ -26,6 +26,7 @@ export const isPrintMode = computed(() => route.value.query.print !== undefined)
|
|
|
26
26
|
export const isPrintWithClicks = computed(() => route.value.query.print === 'clicks')
|
|
27
27
|
export const isEmbedded = computed(() => route.value.query.embedded !== undefined)
|
|
28
28
|
export const isPresenter = computed(() => route.value.path.startsWith('/presenter'))
|
|
29
|
+
export const isNotesViewer = computed(() => route.value.path.startsWith('/notes'))
|
|
29
30
|
export const isClicksDisabled = computed(() => isPrintMode.value && !isPrintWithClicks.value)
|
|
30
31
|
export const presenterPassword = computed(() => route.value.query.password)
|
|
31
32
|
export const showPresenter = computed(() => !isPresenter.value && (!configs.remote || presenterPassword.value === configs.remote))
|
package/logic/recording.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Ref } from 'vue'
|
|
2
2
|
import { nextTick, ref, shallowRef, watch } from 'vue'
|
|
3
|
-
import { useDevicesList, useEventListener,
|
|
3
|
+
import { useDevicesList, useEventListener, useLocalStorage } from '@vueuse/core'
|
|
4
4
|
import { isTruthy } from '@antfu/utils'
|
|
5
5
|
import type RecorderType from 'recordrtc'
|
|
6
6
|
import type { Options as RecorderOptions } from 'recordrtc'
|
|
@@ -11,7 +11,7 @@ type MimeType = Defined<RecorderOptions['mimeType']>
|
|
|
11
11
|
|
|
12
12
|
export const recordingName = ref('')
|
|
13
13
|
export const recordCamera = ref(true)
|
|
14
|
-
export const mimeType =
|
|
14
|
+
export const mimeType = useLocalStorage<MimeType>('slidev-record-mimetype', 'video/webm')
|
|
15
15
|
|
|
16
16
|
export const mimeExtMap: Record<string, string> = {
|
|
17
17
|
'video/webm': 'webm',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "Presentation slides for developers",
|
|
5
5
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@antfu/utils": "^0.7.2",
|
|
19
|
-
"@unocss/reset": "^0.49.
|
|
20
|
-
"@vueuse/core": "^9.
|
|
21
|
-
"@vueuse/head": "^1.0.
|
|
22
|
-
"@vueuse/math": "^9.
|
|
19
|
+
"@unocss/reset": "^0.49.4",
|
|
20
|
+
"@vueuse/core": "^9.12.0",
|
|
21
|
+
"@vueuse/head": "^1.0.24",
|
|
22
|
+
"@vueuse/math": "^9.12.0",
|
|
23
23
|
"@vueuse/motion": "^2.0.0-beta.27",
|
|
24
24
|
"codemirror": "^5.65.5",
|
|
25
25
|
"defu": "^6.1.2",
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"prettier": "^2.8.3",
|
|
35
35
|
"recordrtc": "^5.6.2",
|
|
36
36
|
"resolve": "^1.22.1",
|
|
37
|
-
"unocss": "^0.49.
|
|
37
|
+
"unocss": "^0.49.4",
|
|
38
38
|
"vite-plugin-windicss": "^1.8.10",
|
|
39
|
-
"vue": "^3.2.
|
|
39
|
+
"vue": "^3.2.47",
|
|
40
40
|
"vue-router": "^4.1.6",
|
|
41
41
|
"vue-starport": "^0.3.0",
|
|
42
42
|
"windicss": "^3.5.6",
|
|
43
|
-
"@slidev/parser": "0.
|
|
44
|
-
"@slidev/types": "0.
|
|
43
|
+
"@slidev/parser": "0.40.0",
|
|
44
|
+
"@slidev/types": "0.40.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"vite": "^4.
|
|
47
|
+
"vite": "^4.1.1"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/routes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { RouteRecordRaw } from 'vue-router'
|
|
2
|
-
import type { SlideTransition } from '@slidev/types'
|
|
1
|
+
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
|
3
2
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
|
3
|
+
import type { TransitionGroupProps } from 'vue'
|
|
4
4
|
import Play from './internals/Play.vue'
|
|
5
5
|
import Print from './internals/Print.vue'
|
|
6
6
|
// @ts-expect-error missing types
|
|
@@ -25,24 +25,31 @@ export const routes: RouteRecordRaw[] = [
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
if (__SLIDEV_FEATURE_PRESENTER__) {
|
|
28
|
+
function passwordGuard(to: RouteLocationNormalized) {
|
|
29
|
+
if (!_configs.remote || _configs.remote === to.query.password)
|
|
30
|
+
return true
|
|
31
|
+
if (_configs.remote && to.query.password === undefined) {
|
|
32
|
+
// eslint-disable-next-line no-alert
|
|
33
|
+
const password = prompt('Enter password')
|
|
34
|
+
if (_configs.remote === password)
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
if (to.params.no)
|
|
38
|
+
return { path: `/${to.params.no}` }
|
|
39
|
+
return { path: '' }
|
|
40
|
+
}
|
|
28
41
|
routes.push({ path: '/presenter/print', component: () => import('./internals/PresenterPrint.vue') })
|
|
42
|
+
routes.push({
|
|
43
|
+
name: 'notes',
|
|
44
|
+
path: '/notes',
|
|
45
|
+
component: () => import('./internals/NotesView.vue'),
|
|
46
|
+
beforeEnter: passwordGuard,
|
|
47
|
+
})
|
|
29
48
|
routes.push({
|
|
30
49
|
name: 'presenter',
|
|
31
50
|
path: '/presenter/:no',
|
|
32
51
|
component: () => import('./internals/Presenter.vue'),
|
|
33
|
-
beforeEnter:
|
|
34
|
-
if (!_configs.remote || _configs.remote === to.query.password)
|
|
35
|
-
return true
|
|
36
|
-
if (_configs.remote && to.query.password === undefined) {
|
|
37
|
-
// eslint-disable-next-line no-alert
|
|
38
|
-
const password = prompt('Enter password')
|
|
39
|
-
if (_configs.remote === password)
|
|
40
|
-
return true
|
|
41
|
-
}
|
|
42
|
-
if (to.params.no)
|
|
43
|
-
return { path: `/${to.params.no}` }
|
|
44
|
-
return { path: '' }
|
|
45
|
-
},
|
|
52
|
+
beforeEnter: passwordGuard,
|
|
46
53
|
})
|
|
47
54
|
routes.push({
|
|
48
55
|
path: '/presenter',
|
|
@@ -65,14 +72,14 @@ declare module 'vue-router' {
|
|
|
65
72
|
start: number
|
|
66
73
|
end: number
|
|
67
74
|
note?: string
|
|
68
|
-
|
|
75
|
+
noteHTML?: string
|
|
69
76
|
id: number
|
|
70
77
|
no: number
|
|
71
78
|
filepath: string
|
|
72
79
|
title?: string
|
|
73
80
|
level?: number
|
|
74
81
|
}
|
|
75
|
-
transition?: string |
|
|
82
|
+
transition?: string | TransitionGroupProps | undefined
|
|
76
83
|
// private fields
|
|
77
84
|
__clicksElements: HTMLElement[]
|
|
78
85
|
__preloaded?: boolean
|
package/setup/main.ts
CHANGED
|
@@ -17,7 +17,7 @@ export default function setupMain(context: AppContext) {
|
|
|
17
17
|
context.app.use(StarportPlugin({ keepAlive: true }))
|
|
18
18
|
|
|
19
19
|
// @ts-expect-error inject in runtime
|
|
20
|
-
// eslint-disable-next-line
|
|
20
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
21
21
|
const injection_arg = context
|
|
22
22
|
|
|
23
23
|
/* __injections__ */
|
package/setup/monaco.ts
CHANGED
|
@@ -49,7 +49,7 @@ const setup = createSingletonPromise(async () => {
|
|
|
49
49
|
])
|
|
50
50
|
|
|
51
51
|
// @ts-expect-error injected in runtime
|
|
52
|
-
// eslint-disable-next-line
|
|
52
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
53
53
|
const injection_arg = monaco
|
|
54
54
|
// eslint-disable-next-line prefer-const
|
|
55
55
|
let injection_return: MonacoSetupReturn = {}
|
package/setup/root.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/* __imports__ */
|
|
2
2
|
import { watch } from 'vue'
|
|
3
3
|
import { useHead, useHtmlAttrs } from '@vueuse/head'
|
|
4
|
+
import { nanoid } from 'nanoid'
|
|
4
5
|
import { configs } from '../env'
|
|
5
6
|
import { initSharedState, onPatch, patch } from '../state/shared'
|
|
6
7
|
import { initDrawingState } from '../state/drawings'
|
|
7
|
-
import { clicks, currentPage, getPath, isPresenter } from '../logic/nav'
|
|
8
|
+
import { clicks, currentPage, getPath, isNotesViewer, isPresenter } from '../logic/nav'
|
|
8
9
|
import { router } from '../routes'
|
|
10
|
+
import { TRUST_ORIGINS } from '../constants'
|
|
9
11
|
|
|
10
12
|
export default function setupRoot() {
|
|
11
13
|
// @ts-expect-error injected in runtime
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
const injection_arg = undefined
|
|
14
16
|
|
|
15
17
|
/* __injections__ */
|
|
@@ -20,17 +22,38 @@ export default function setupRoot() {
|
|
|
20
22
|
initSharedState(`${title} - shared`)
|
|
21
23
|
initDrawingState(`${title} - drawings`)
|
|
22
24
|
|
|
25
|
+
const id = `${location.origin}_${nanoid()}`
|
|
26
|
+
|
|
23
27
|
// update shared state
|
|
24
28
|
function updateSharedState() {
|
|
29
|
+
if (isNotesViewer.value)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
// we allow Presenter mode, or Viewer mode from trusted origins to update the shared state
|
|
33
|
+
if (!isPresenter.value && !TRUST_ORIGINS.includes(location.host.split(':')[0]))
|
|
34
|
+
return
|
|
35
|
+
|
|
25
36
|
if (isPresenter.value) {
|
|
26
37
|
patch('page', +currentPage.value)
|
|
27
38
|
patch('clicks', clicks.value)
|
|
28
39
|
}
|
|
40
|
+
else {
|
|
41
|
+
patch('viewerPage', +currentPage.value)
|
|
42
|
+
patch('viewerClicks', clicks.value)
|
|
43
|
+
}
|
|
44
|
+
patch('lastUpdate', {
|
|
45
|
+
id,
|
|
46
|
+
type: isPresenter.value ? 'presenter' : 'viewer',
|
|
47
|
+
time: new Date().getTime(),
|
|
48
|
+
})
|
|
29
49
|
}
|
|
30
50
|
router.afterEach(updateSharedState)
|
|
31
51
|
watch(clicks, updateSharedState)
|
|
32
52
|
|
|
33
53
|
onPatch((state) => {
|
|
54
|
+
const routePath = router.currentRoute.value.path
|
|
55
|
+
if (!routePath.match(/^\/(\d+|presenter)\/?/))
|
|
56
|
+
return
|
|
34
57
|
if (+state.page !== +currentPage.value || clicks.value !== state.clicks) {
|
|
35
58
|
router.replace({
|
|
36
59
|
path: getPath(state.page),
|
package/setup/shortcuts.ts
CHANGED
|
@@ -11,7 +11,7 @@ export default function setupShortcuts() {
|
|
|
11
11
|
const { escape, space, shift, left, right, up, down, enter, d, g, o } = magicKeys
|
|
12
12
|
|
|
13
13
|
// @ts-expect-error injected in runtime
|
|
14
|
-
// eslint-disable-next-line
|
|
14
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
15
15
|
const injection_arg: NavOperations = {
|
|
16
16
|
next,
|
|
17
17
|
prev,
|
package/state/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { breakpointsTailwind, isClient, useActiveElement, useBreakpoints, useFullscreen,
|
|
1
|
+
import { breakpointsTailwind, isClient, useActiveElement, useBreakpoints, useFullscreen, useLocalStorage, useMagicKeys, useToggle, useWindowSize } from '@vueuse/core'
|
|
2
2
|
import { computed, ref } from 'vue'
|
|
3
3
|
import { slideAspect } from '../env'
|
|
4
4
|
|
|
@@ -20,13 +20,13 @@ 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 =
|
|
24
|
-
export const currentMic =
|
|
25
|
-
export const slideScale =
|
|
23
|
+
export const currentCamera = useLocalStorage<string>('slidev-camera', 'default')
|
|
24
|
+
export const currentMic = useLocalStorage<string>('slidev-mic', 'default')
|
|
25
|
+
export const slideScale = useLocalStorage<number>('slidev-scale', 0)
|
|
26
26
|
|
|
27
|
-
export const showOverview =
|
|
28
|
-
export const showPresenterCursor =
|
|
29
|
-
export const showEditor =
|
|
30
|
-
export const editorWidth =
|
|
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 editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 100)
|
|
31
31
|
|
|
32
32
|
export const toggleOverview = useToggle(showOverview)
|
package/state/shared.ts
CHANGED
|
@@ -8,10 +8,27 @@ export interface SharedState {
|
|
|
8
8
|
x: number
|
|
9
9
|
y: number
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
viewerPage: number
|
|
13
|
+
viewerClicks: number
|
|
14
|
+
|
|
15
|
+
lastUpdate?: {
|
|
16
|
+
id: string
|
|
17
|
+
type: 'presenter' | 'viewer'
|
|
18
|
+
time: number
|
|
19
|
+
}
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
const { init, onPatch, patch, state } = createSyncState<SharedState>(serverState, {
|
|
14
23
|
page: 1,
|
|
15
24
|
clicks: 0,
|
|
25
|
+
viewerPage: 1,
|
|
26
|
+
viewerClicks: 0,
|
|
16
27
|
})
|
|
17
|
-
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
init as initSharedState,
|
|
31
|
+
onPatch,
|
|
32
|
+
patch,
|
|
33
|
+
state as sharedState,
|
|
34
|
+
}
|