@slidev/client 52.14.1 → 52.15.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/builtin/BlueSky.vue +167 -0
- package/builtin/CodeBlockWrapper.vue +1 -1
- package/builtin/Link.vue +2 -2
- package/builtin/Monaco.vue +1 -2
- package/builtin/Tweet.vue +1 -1
- package/builtin/VClick.ts +8 -0
- package/builtin/VClicks.ts +24 -4
- package/builtin/VDrag.vue +1 -0
- package/composables/useDragElements.ts +5 -2
- package/composables/useDrawings.ts +6 -6
- package/composables/useMousePosInSlide.ts +21 -0
- package/composables/usePreloadImages.ts +2 -1
- package/composables/useSlideInfo.ts +1 -2
- package/constants.ts +2 -1
- package/internals/DrawingControls.vue +2 -2
- package/internals/Goto.vue +1 -1
- package/internals/IconButton.vue +3 -1
- package/internals/LaserPointer.vue +66 -0
- package/internals/MenuButton.vue +1 -1
- package/internals/NavControls.vue +28 -18
- package/internals/PresenterMouse.vue +1 -1
- package/internals/Settings.vue +10 -1
- package/internals/ShikiEditor.vue +2 -4
- package/internals/SideEditor.vue +3 -1
- package/internals/SlideContainer.vue +18 -1
- package/logic/recording.ts +16 -8
- package/logic/slides.ts +23 -11
- package/modules/v-click.ts +42 -8
- package/package.json +23 -22
- package/pages/404.vue +3 -1
- package/pages/export.vue +5 -1
- package/pages/notes-edit.vue +3 -1
- package/pages/play.vue +2 -0
- package/pages/presenter.vue +18 -23
- package/setup/main.ts +1 -2
- package/setup/monaco.ts +4 -4
- package/setup/root.ts +1 -1
- package/shim-vue.d.ts +3 -1
- package/state/shared.ts +1 -0
- package/state/storage.ts +7 -0
- package/styles/index.css +8 -22
- package/styles/index.ts +23 -0
- package/styles/layouts-base.css +1 -1
- package/styles/vclick.css +54 -0
package/internals/Settings.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useWakeLock } from '@vueuse/core'
|
|
3
3
|
import { useNav } from '../composables/useNav'
|
|
4
|
-
import { hideCursorIdle, slideScale, viewerCssFilter, viewerCssFilterDefaults, wakeLockEnabled } from '../state'
|
|
4
|
+
import { cursorStyle, hideCursorIdle, slideScale, viewerCssFilter, viewerCssFilterDefaults, wakeLockEnabled } from '../state'
|
|
5
5
|
import FormCheckbox from './FormCheckbox.vue'
|
|
6
6
|
import FormItem from './FormItem.vue'
|
|
7
7
|
import FormSlider from './FormSlider.vue'
|
|
@@ -86,6 +86,15 @@ const { isSupported } = useWakeLock()
|
|
|
86
86
|
/>
|
|
87
87
|
</FormItem>
|
|
88
88
|
<div class="h-1px opacity-5 bg-current w-full my2" />
|
|
89
|
+
<FormItem title="Cursor Style">
|
|
90
|
+
<SegmentControl
|
|
91
|
+
v-model="cursorStyle"
|
|
92
|
+
:options="[
|
|
93
|
+
{ label: 'Cursor', value: 'cursor' },
|
|
94
|
+
{ label: 'Laser', value: 'laser' },
|
|
95
|
+
]"
|
|
96
|
+
/>
|
|
97
|
+
</FormItem>
|
|
89
98
|
<FormItem
|
|
90
99
|
v-if="!isPresenter"
|
|
91
100
|
title="Slide Scale"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { shallowRef } from 'vue'
|
|
3
3
|
import { useIME } from '../composables/useIME'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{
|
|
@@ -8,8 +8,6 @@ const props = defineProps<{
|
|
|
8
8
|
const content = defineModel<string>({ required: true })
|
|
9
9
|
const { composingContent, onInput, onCompositionEnd } = useIME(content)
|
|
10
10
|
|
|
11
|
-
const textareaEl = ref<HTMLTextAreaElement | null>(null)
|
|
12
|
-
|
|
13
11
|
const highlight = shallowRef<((code: string) => string) | null>(null)
|
|
14
12
|
import('../setup/shiki').then(async (m) => {
|
|
15
13
|
const { getEagerHighlighter, defaultHighlightOptions } = await m.default()
|
|
@@ -26,7 +24,7 @@ import('../setup/shiki').then(async (m) => {
|
|
|
26
24
|
<div v-if="highlight" class="relative w-full h-max min-h-full">
|
|
27
25
|
<div class="relative w-full h-max" v-html="`${highlight(composingContent)} `" />
|
|
28
26
|
<textarea
|
|
29
|
-
|
|
27
|
+
v-model="composingContent" :placeholder="props.placeholder"
|
|
30
28
|
class="absolute inset-0 resize-none text-transparent bg-transparent focus:outline-none caret-black dark:caret-white overflow-y-hidden"
|
|
31
29
|
@input="onInput"
|
|
32
30
|
@compositionend="onCompositionEnd"
|
package/internals/SideEditor.vue
CHANGED
|
@@ -11,6 +11,8 @@ const props = defineProps<{
|
|
|
11
11
|
resize?: boolean
|
|
12
12
|
}>()
|
|
13
13
|
|
|
14
|
+
const RE_FRONTMATTER_BLOCK = /^---\n([\s\S]*?)\n---\n/
|
|
15
|
+
|
|
14
16
|
const { currentSlideNo, openInEditor } = useNav()
|
|
15
17
|
|
|
16
18
|
const tab = ref<'content' | 'note'>('content')
|
|
@@ -37,7 +39,7 @@ async function save() {
|
|
|
37
39
|
dirty.value = false
|
|
38
40
|
|
|
39
41
|
let frontmatterRaw: string | undefined
|
|
40
|
-
const contentOnly = content.value.trim().replace(
|
|
42
|
+
const contentOnly = content.value.trim().replace(RE_FRONTMATTER_BLOCK, (_, f) => {
|
|
41
43
|
frontmatterRaw = f
|
|
42
44
|
return ''
|
|
43
45
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { provideLocal, useElementSize } from '@vueuse/core'
|
|
3
|
-
import {
|
|
3
|
+
import { recomputeAllPoppers } from 'floating-vue'
|
|
4
|
+
import { computed, onMounted, onUnmounted, ref, watch, watchEffect, watchSyncEffect } from 'vue'
|
|
4
5
|
import { useNav } from '../composables/useNav'
|
|
5
6
|
import { injectionSlideElement, injectionSlideScale } from '../constants'
|
|
6
7
|
import { slideAspect, slideHeight, slideWidth } from '../env'
|
|
@@ -72,6 +73,18 @@ if (props.isMain) {
|
|
|
72
73
|
provideLocal(injectionSlideScale, scale)
|
|
73
74
|
provideLocal(injectionSlideElement, slideElement)
|
|
74
75
|
|
|
76
|
+
watchSyncEffect(() => {
|
|
77
|
+
if (props.isMain) {
|
|
78
|
+
mainSlideElement.value = slideElement.value
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
onMounted(() => {
|
|
83
|
+
watch(() => props.isMain && scale.value, () => {
|
|
84
|
+
recomputeAllPoppers()
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
75
88
|
const snapshot = computed(() => {
|
|
76
89
|
if (props.no == null || !props.useSnapshot)
|
|
77
90
|
return undefined
|
|
@@ -79,6 +92,10 @@ const snapshot = computed(() => {
|
|
|
79
92
|
})
|
|
80
93
|
</script>
|
|
81
94
|
|
|
95
|
+
<script lang="ts">
|
|
96
|
+
export const mainSlideElement = ref<HTMLElement | null>(null)
|
|
97
|
+
</script>
|
|
98
|
+
|
|
82
99
|
<template>
|
|
83
100
|
<div
|
|
84
101
|
v-if="!snapshot"
|
package/logic/recording.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Options as RecorderOptions } from 'recordrtc'
|
|
|
3
3
|
import type { Ref } from 'vue'
|
|
4
4
|
import { isTruthy } from '@antfu/utils'
|
|
5
5
|
import { useDevicesList, useEventListener, useLocalStorage } from '@vueuse/core'
|
|
6
|
+
import fixWebmDuration from 'fix-webm-duration'
|
|
6
7
|
import { nextTick, ref, shallowRef, watch } from 'vue'
|
|
7
8
|
import { currentCamera, currentMic } from '../state'
|
|
8
9
|
|
|
@@ -50,11 +51,11 @@ export const {
|
|
|
50
51
|
} = useDevicesList({
|
|
51
52
|
onUpdated() {
|
|
52
53
|
if (currentCamera.value !== 'none') {
|
|
53
|
-
if (!cameras.value.
|
|
54
|
+
if (!cameras.value.some(i => i.deviceId === currentCamera.value))
|
|
54
55
|
currentCamera.value = cameras.value[0]?.deviceId || 'default'
|
|
55
56
|
}
|
|
56
57
|
if (currentMic.value !== 'none') {
|
|
57
|
-
if (!microphones.value.
|
|
58
|
+
if (!microphones.value.some(i => i.deviceId === currentMic.value))
|
|
58
59
|
currentMic.value = microphones.value[0]?.deviceId || 'default'
|
|
59
60
|
}
|
|
60
61
|
},
|
|
@@ -78,6 +79,7 @@ export function useRecording() {
|
|
|
78
79
|
const streamCamera: Ref<MediaStream | undefined> = shallowRef()
|
|
79
80
|
const streamCapture: Ref<MediaStream | undefined> = shallowRef()
|
|
80
81
|
const streamSlides: Ref<MediaStream | undefined> = shallowRef()
|
|
82
|
+
let recordingStartTime = 0
|
|
81
83
|
|
|
82
84
|
const config: RecorderOptions = {
|
|
83
85
|
type: 'video',
|
|
@@ -183,17 +185,18 @@ export function useRecording() {
|
|
|
183
185
|
)
|
|
184
186
|
|
|
185
187
|
recorderSlides.value.startRecording()
|
|
188
|
+
recordingStartTime = Date.now()
|
|
186
189
|
recording.value = true
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
async function stopRecording() {
|
|
190
193
|
recording.value = false
|
|
194
|
+
const duration = Date.now() - recordingStartTime
|
|
195
|
+
|
|
191
196
|
recorderCamera.value?.stopRecording(() => {
|
|
192
197
|
if (recordCamera.value) {
|
|
193
198
|
const blob = recorderCamera.value!.getBlob()
|
|
194
|
-
|
|
195
|
-
download(getFilename('camera', config.mimeType), url)
|
|
196
|
-
window.URL.revokeObjectURL(url)
|
|
199
|
+
downloadBlob(blob, duration, getFilename('camera', config.mimeType))
|
|
197
200
|
}
|
|
198
201
|
recorderCamera.value = undefined
|
|
199
202
|
if (!showAvatar.value)
|
|
@@ -201,15 +204,20 @@ export function useRecording() {
|
|
|
201
204
|
})
|
|
202
205
|
recorderSlides.value?.stopRecording(() => {
|
|
203
206
|
const blob = recorderSlides.value!.getBlob()
|
|
204
|
-
|
|
205
|
-
download(getFilename('screen', config.mimeType), url)
|
|
206
|
-
window.URL.revokeObjectURL(url)
|
|
207
|
+
downloadBlob(blob, duration, getFilename('screen', config.mimeType))
|
|
207
208
|
closeStream(streamCapture)
|
|
208
209
|
closeStream(streamSlides)
|
|
209
210
|
recorderSlides.value = undefined
|
|
210
211
|
})
|
|
211
212
|
}
|
|
212
213
|
|
|
214
|
+
async function downloadBlob(blob: Blob, duration: number, filename: string) {
|
|
215
|
+
const fixedBlob = await fixWebmDuration(blob, duration, { logger: false })
|
|
216
|
+
const url = URL.createObjectURL(fixedBlob)
|
|
217
|
+
download(filename, url)
|
|
218
|
+
window.URL.revokeObjectURL(url)
|
|
219
|
+
}
|
|
220
|
+
|
|
213
221
|
function closeStream(stream: Ref<MediaStream | undefined>) {
|
|
214
222
|
const s = stream.value
|
|
215
223
|
if (!s)
|
package/logic/slides.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SlideRoute } from '@slidev/types'
|
|
2
2
|
import { slides } from '#slidev/slides'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { tryOnMounted } from '@vueuse/core'
|
|
4
|
+
import { computed, watch } from 'vue'
|
|
5
5
|
import { useSlideContext } from '../context'
|
|
6
6
|
|
|
7
7
|
export { slides }
|
|
@@ -24,17 +24,29 @@ export function getSlidePath(
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function useIsSlideActive() {
|
|
27
|
-
const { $page } = useSlideContext()
|
|
28
|
-
|
|
29
|
-
return computed(() => $page.value ===
|
|
27
|
+
const { $page, $nav } = useSlideContext()
|
|
28
|
+
// Use `$nav.value.currentSlideNo` rather than `useNav().currentSlideNo` to make it work in print/export mode. See https://github.com/slidevjs/slidev/issues/2310.
|
|
29
|
+
return computed(() => $page.value === $nav.value.currentSlideNo)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export function onSlideEnter(cb: () =>
|
|
33
|
-
const
|
|
34
|
-
|
|
32
|
+
export function onSlideEnter(cb: (to: number, from: number | undefined) => any) {
|
|
33
|
+
const { $page, $nav } = useSlideContext()
|
|
34
|
+
|
|
35
|
+
tryOnMounted(() => {
|
|
36
|
+
watch(() => $nav.value.currentSlideNo, (to, from) => {
|
|
37
|
+
if ($page.value === to)
|
|
38
|
+
cb(to, from)
|
|
39
|
+
}, { immediate: true })
|
|
40
|
+
})
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
export function onSlideLeave(cb: () =>
|
|
38
|
-
const
|
|
39
|
-
|
|
43
|
+
export function onSlideLeave(cb: (to: number, from: number | undefined) => any) {
|
|
44
|
+
const { $page, $nav } = useSlideContext()
|
|
45
|
+
|
|
46
|
+
tryOnMounted(() => {
|
|
47
|
+
watch(() => $nav.value.currentSlideNo, (to, from) => {
|
|
48
|
+
if ($page.value === from)
|
|
49
|
+
cb(to, from)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
40
52
|
}
|
package/modules/v-click.ts
CHANGED
|
@@ -2,16 +2,29 @@ import type { ClicksElement, RawAtValue } from '@slidev/types'
|
|
|
2
2
|
import type { App, DirectiveBinding } from 'vue'
|
|
3
3
|
import { computed, watchEffect } from 'vue'
|
|
4
4
|
import {
|
|
5
|
+
CLASS_VCLICK_ANIMATION_PREFIX,
|
|
5
6
|
CLASS_VCLICK_CURRENT,
|
|
6
|
-
CLASS_VCLICK_FADE,
|
|
7
7
|
CLASS_VCLICK_HIDDEN,
|
|
8
8
|
CLASS_VCLICK_HIDDEN_EXP,
|
|
9
9
|
CLASS_VCLICK_PRIOR,
|
|
10
10
|
CLASS_VCLICK_TARGET,
|
|
11
11
|
injectionClicksContext,
|
|
12
|
+
injectionFrontmatter,
|
|
12
13
|
} from '../constants'
|
|
14
|
+
import { configs } from '../env'
|
|
13
15
|
import { directiveInject } from '../utils'
|
|
14
16
|
|
|
17
|
+
const RE_WHITESPACE_OR_COMMA = /[\s,]+/
|
|
18
|
+
|
|
19
|
+
function syncAnimationClasses(el: HTMLElement, animations: string[]) {
|
|
20
|
+
const targetClasses = animations.map(a => `${CLASS_VCLICK_ANIMATION_PREFIX}${a}`)
|
|
21
|
+
el.classList.forEach((c) => {
|
|
22
|
+
if (c.startsWith(CLASS_VCLICK_ANIMATION_PREFIX) && !targetClasses.includes(c))
|
|
23
|
+
el.classList.remove(c)
|
|
24
|
+
})
|
|
25
|
+
targetClasses.forEach(c => el.classList.add(c))
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
export function createVClickDirectives() {
|
|
16
29
|
return {
|
|
17
30
|
install(app: App) {
|
|
@@ -38,13 +51,15 @@ export function createVClickDirectives() {
|
|
|
38
51
|
const prior = active && !current
|
|
39
52
|
|
|
40
53
|
if (resolved.flagHide) {
|
|
41
|
-
el.classList.toggle(
|
|
54
|
+
el.classList.toggle(CLASS_VCLICK_HIDDEN, active)
|
|
42
55
|
el.classList.toggle(CLASS_VCLICK_HIDDEN_EXP, active)
|
|
43
56
|
}
|
|
44
57
|
else {
|
|
45
|
-
el.classList.toggle(
|
|
58
|
+
el.classList.toggle(CLASS_VCLICK_HIDDEN, !active)
|
|
46
59
|
}
|
|
47
60
|
|
|
61
|
+
syncAnimationClasses(el, resolved.flagAnimations.value)
|
|
62
|
+
|
|
48
63
|
el.classList.toggle(CLASS_VCLICK_CURRENT, current)
|
|
49
64
|
el.classList.toggle(CLASS_VCLICK_PRIOR, prior)
|
|
50
65
|
})
|
|
@@ -70,13 +85,15 @@ export function createVClickDirectives() {
|
|
|
70
85
|
const prior = active && !current
|
|
71
86
|
|
|
72
87
|
if (resolved.flagHide) {
|
|
73
|
-
el.classList.toggle(
|
|
88
|
+
el.classList.toggle(CLASS_VCLICK_HIDDEN, active)
|
|
74
89
|
el.classList.toggle(CLASS_VCLICK_HIDDEN_EXP, active)
|
|
75
90
|
}
|
|
76
91
|
else {
|
|
77
|
-
el.classList.toggle(
|
|
92
|
+
el.classList.toggle(CLASS_VCLICK_HIDDEN, !active)
|
|
78
93
|
}
|
|
79
94
|
|
|
95
|
+
syncAnimationClasses(el, resolved.flagAnimations.value)
|
|
96
|
+
|
|
80
97
|
el.classList.toggle(CLASS_VCLICK_CURRENT, current)
|
|
81
98
|
el.classList.toggle(CLASS_VCLICK_PRIOR, prior)
|
|
82
99
|
})
|
|
@@ -101,9 +118,11 @@ export function createVClickDirectives() {
|
|
|
101
118
|
const current = resolved.isCurrent.value
|
|
102
119
|
const prior = active && !current
|
|
103
120
|
|
|
104
|
-
el.classList.toggle(
|
|
121
|
+
el.classList.toggle(CLASS_VCLICK_HIDDEN, active)
|
|
105
122
|
el.classList.toggle(CLASS_VCLICK_HIDDEN_EXP, active)
|
|
106
123
|
|
|
124
|
+
syncAnimationClasses(el, resolved.flagAnimations.value)
|
|
125
|
+
|
|
107
126
|
el.classList.toggle(CLASS_VCLICK_CURRENT, current)
|
|
108
127
|
el.classList.toggle(CLASS_VCLICK_PRIOR, prior)
|
|
109
128
|
})
|
|
@@ -118,12 +137,27 @@ export const resolvedClickMap = new Map<ClicksElement, ReturnType<typeof resolve
|
|
|
118
137
|
|
|
119
138
|
export function resolveClick(el: Element | string, dir: DirectiveBinding<any>, value: RawAtValue, explicitHide = false) {
|
|
120
139
|
const ctx = directiveInject(dir, injectionClicksContext)?.value
|
|
140
|
+
const frontmatter = directiveInject(dir, injectionFrontmatter)
|
|
121
141
|
|
|
122
142
|
if (!el || !ctx)
|
|
123
143
|
return null
|
|
124
144
|
|
|
125
145
|
const flagHide = explicitHide || (dir.modifiers.hide !== false && dir.modifiers.hide != null)
|
|
126
|
-
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Resolves the animation presets for this element.
|
|
149
|
+
* Priority: directive modifiers (stacked) > slide frontmatter > global config.
|
|
150
|
+
* Modifiers allow composition, e.g., v-click.fade.up.scale.
|
|
151
|
+
*/
|
|
152
|
+
const elModifiers = Object.keys({ ...dir.modifiers }).filter(m => m !== 'hide')
|
|
153
|
+
const flagAnimations = computed(() => {
|
|
154
|
+
if (elModifiers.length > 0)
|
|
155
|
+
return elModifiers
|
|
156
|
+
const preset = frontmatter?.clickAnimation || configs.clickAnimation
|
|
157
|
+
if (preset)
|
|
158
|
+
return preset.split(RE_WHITESPACE_OR_COMMA).filter(Boolean)
|
|
159
|
+
return []
|
|
160
|
+
})
|
|
127
161
|
|
|
128
162
|
const info = ctx.calculate(value)
|
|
129
163
|
if (!info)
|
|
@@ -145,8 +179,8 @@ export function resolveClick(el: Element | string, dir: DirectiveBinding<any>, v
|
|
|
145
179
|
...info,
|
|
146
180
|
isShown,
|
|
147
181
|
visibilityState,
|
|
148
|
-
flagFade,
|
|
149
182
|
flagHide,
|
|
183
|
+
flagAnimations,
|
|
150
184
|
}
|
|
151
185
|
resolvedClickMap.set(el, resolved)
|
|
152
186
|
return resolved
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "52.
|
|
4
|
+
"version": "52.15.0",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -25,48 +25,49 @@
|
|
|
25
25
|
},
|
|
26
26
|
"main": "./public.ts",
|
|
27
27
|
"engines": {
|
|
28
|
-
"node": ">=
|
|
28
|
+
"node": ">=20.12.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@antfu/utils": "^9.3.0",
|
|
32
|
-
"@iconify-json/carbon": "^1.2.
|
|
32
|
+
"@iconify-json/carbon": "^1.2.20",
|
|
33
33
|
"@iconify-json/ph": "^1.2.2",
|
|
34
34
|
"@iconify-json/svg-spinners": "^1.2.4",
|
|
35
|
-
"@shikijs/engine-javascript": "^4.0.
|
|
36
|
-
"@shikijs/monaco": "^4.0.
|
|
37
|
-
"@shikijs/vitepress-twoslash": "^4.0.
|
|
35
|
+
"@shikijs/engine-javascript": "^4.0.2",
|
|
36
|
+
"@shikijs/monaco": "^4.0.2",
|
|
37
|
+
"@shikijs/vitepress-twoslash": "^4.0.2",
|
|
38
38
|
"@slidev/rough-notation": "^0.1.0",
|
|
39
39
|
"@typescript/ata": "^0.9.8",
|
|
40
|
-
"@unhead/vue": "^2.1.
|
|
41
|
-
"@unocss/extractor-mdc": "^66.6.
|
|
42
|
-
"@unocss/preset-mini": "^66.6.
|
|
43
|
-
"@unocss/reset": "^66.6.
|
|
40
|
+
"@unhead/vue": "^2.1.13",
|
|
41
|
+
"@unocss/extractor-mdc": "^66.6.8",
|
|
42
|
+
"@unocss/preset-mini": "^66.6.8",
|
|
43
|
+
"@unocss/reset": "^66.6.8",
|
|
44
44
|
"@vueuse/core": "^14.2.1",
|
|
45
45
|
"@vueuse/math": "^14.2.1",
|
|
46
46
|
"@vueuse/motion": "^3.0.3",
|
|
47
47
|
"ansis": "^4.2.0",
|
|
48
48
|
"drauu": "^1.0.0",
|
|
49
49
|
"file-saver": "^2.0.5",
|
|
50
|
+
"fix-webm-duration": "^1.0.6",
|
|
50
51
|
"floating-vue": "^5.2.2",
|
|
51
|
-
"fuse.js": "^7.
|
|
52
|
-
"katex": "^0.16.
|
|
52
|
+
"fuse.js": "^7.3.0",
|
|
53
|
+
"katex": "^0.16.45",
|
|
53
54
|
"lz-string": "^1.5.0",
|
|
54
|
-
"mermaid": "^11.
|
|
55
|
+
"mermaid": "^11.14.0",
|
|
55
56
|
"monaco-editor": "^0.55.1",
|
|
56
57
|
"nanotar": "^0.3.0",
|
|
57
58
|
"pptxgenjs": "^4.0.1",
|
|
58
59
|
"recordrtc": "^5.6.2",
|
|
59
|
-
"shiki": "^4.0.
|
|
60
|
-
"shiki-magic-move": "^1.
|
|
60
|
+
"shiki": "^4.0.2",
|
|
61
|
+
"shiki-magic-move": "^1.3.0",
|
|
61
62
|
"typescript": "^5.9.3",
|
|
62
|
-
"unocss": "^66.6.
|
|
63
|
-
"vue": "^3.5.
|
|
64
|
-
"vue-router": "^5.0.
|
|
65
|
-
"yaml": "^2.8.
|
|
66
|
-
"@slidev/parser": "52.
|
|
67
|
-
"@slidev/types": "52.
|
|
63
|
+
"unocss": "^66.6.8",
|
|
64
|
+
"vue": "^3.5.33",
|
|
65
|
+
"vue-router": "^5.0.6",
|
|
66
|
+
"yaml": "^2.8.3",
|
|
67
|
+
"@slidev/parser": "52.15.0",
|
|
68
|
+
"@slidev/types": "52.15.0"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
70
|
-
"vite": "^
|
|
71
|
+
"vite": "^8.0.10"
|
|
71
72
|
}
|
|
72
73
|
}
|
package/pages/404.vue
CHANGED
|
@@ -3,12 +3,14 @@ import { computed } from 'vue'
|
|
|
3
3
|
import { useRouter } from 'vue-router'
|
|
4
4
|
import { useNav } from '../composables/useNav'
|
|
5
5
|
|
|
6
|
+
const RE_DIGITS = /\d+/
|
|
7
|
+
|
|
6
8
|
const { currentRoute } = useRouter()
|
|
7
9
|
const { total } = useNav()
|
|
8
10
|
|
|
9
11
|
const guessedSlide = computed(() => {
|
|
10
12
|
const path = currentRoute.value.path
|
|
11
|
-
const match = path.match(
|
|
13
|
+
const match = path.match(RE_DIGITS)
|
|
12
14
|
if (match) {
|
|
13
15
|
const slideNo = +match[0]
|
|
14
16
|
if (slideNo > 0 && slideNo <= total.value)
|
package/pages/export.vue
CHANGED
|
@@ -161,7 +161,7 @@ async function pngsGz() {
|
|
|
161
161
|
})),
|
|
162
162
|
)
|
|
163
163
|
const a = document.createElement('a')
|
|
164
|
-
const blob = new Blob([data], { type: 'application/gzip' })
|
|
164
|
+
const blob = new Blob([data as any], { type: 'application/gzip' })
|
|
165
165
|
a.href = URL.createObjectURL(blob)
|
|
166
166
|
a.download = `${title.value}.tar.gz`
|
|
167
167
|
a.click()
|
|
@@ -351,6 +351,10 @@ h2 {
|
|
|
351
351
|
#export-content {
|
|
352
352
|
--uno: pointer-events-none;
|
|
353
353
|
}
|
|
354
|
+
|
|
355
|
+
#export-container {
|
|
356
|
+
--slidev-slide-container-scale: var(--slidev-slide-scale);
|
|
357
|
+
}
|
|
354
358
|
</style>
|
|
355
359
|
|
|
356
360
|
<style>
|
package/pages/notes-edit.vue
CHANGED
|
@@ -9,6 +9,8 @@ import { slidesTitle } from '../env'
|
|
|
9
9
|
import IconButton from '../internals/IconButton.vue'
|
|
10
10
|
import Modal from '../internals/Modal.vue'
|
|
11
11
|
|
|
12
|
+
const RE_SLIDE_HEADER = /^---\s*#(\d+)\s*$/
|
|
13
|
+
|
|
12
14
|
useHead({ title: `Notes Edit - ${slidesTitle}` })
|
|
13
15
|
|
|
14
16
|
const { slides } = useNav()
|
|
@@ -35,7 +37,7 @@ function deserializeNotes(notes: string, slides: SlideRoute[]) {
|
|
|
35
37
|
const lines = notes.split(/^(---\s*#\d+\s*)$/gm)
|
|
36
38
|
|
|
37
39
|
lines.forEach((line, index) => {
|
|
38
|
-
const match = line.match(
|
|
40
|
+
const match = line.match(RE_SLIDE_HEADER)
|
|
39
41
|
if (match) {
|
|
40
42
|
const no = Number.parseInt(match[1])
|
|
41
43
|
const note = lines[index + 1].trim()
|
package/pages/play.vue
CHANGED
|
@@ -7,6 +7,7 @@ import { useNav } from '../composables/useNav'
|
|
|
7
7
|
import { useSwipeControls } from '../composables/useSwipeControls'
|
|
8
8
|
import { useWakeLock } from '../composables/useWakeLock'
|
|
9
9
|
import Controls from '../internals/Controls.vue'
|
|
10
|
+
import LaserPointer from '../internals/LaserPointer.vue'
|
|
10
11
|
import NavControls from '../internals/NavControls.vue'
|
|
11
12
|
import PresenterMouse from '../internals/PresenterMouse.vue'
|
|
12
13
|
import SlideContainer from '../internals/SlideContainer.vue'
|
|
@@ -97,6 +98,7 @@ const contentStyle = computed(() => {
|
|
|
97
98
|
<template #default>
|
|
98
99
|
<SlidesShow render-context="slide" />
|
|
99
100
|
<PresenterMouse />
|
|
101
|
+
<LaserPointer />
|
|
100
102
|
</template>
|
|
101
103
|
<template #controls>
|
|
102
104
|
<div
|
package/pages/presenter.vue
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useHead } from '@unhead/vue'
|
|
3
|
-
import { useEventListener, useLocalStorage, useMediaQuery,
|
|
4
|
-
import { computed, onMounted,
|
|
3
|
+
import { useEventListener, useLocalStorage, useMediaQuery, useWindowFocus } from '@vueuse/core'
|
|
4
|
+
import { computed, onMounted, ref, shallowRef, watch, watchEffect } from 'vue'
|
|
5
5
|
import { createClicksContextBase } from '../composables/useClicks'
|
|
6
6
|
import { useDrawings } from '../composables/useDrawings'
|
|
7
|
+
import { useMousePosInSlide } from '../composables/useMousePosInSlide'
|
|
7
8
|
import { useNav } from '../composables/useNav'
|
|
8
9
|
import { useSwipeControls } from '../composables/useSwipeControls'
|
|
9
10
|
import { useWakeLock } from '../composables/useWakeLock'
|
|
@@ -14,6 +15,7 @@ import CurrentProgressBar from '../internals/CurrentProgressBar.vue'
|
|
|
14
15
|
import DrawingControls from '../internals/DrawingControls.vue'
|
|
15
16
|
import Goto from '../internals/Goto.vue'
|
|
16
17
|
import IconButton from '../internals/IconButton.vue'
|
|
18
|
+
import LaserPointer from '../internals/LaserPointer.vue'
|
|
17
19
|
import NavControls from '../internals/NavControls.vue'
|
|
18
20
|
import NoteEditable from '../internals/NoteEditable.vue'
|
|
19
21
|
import NoteStatic from '../internals/NoteStatic.vue'
|
|
@@ -27,7 +29,7 @@ import TimerBar from '../internals/TimerBar.vue'
|
|
|
27
29
|
import TimerInlined from '../internals/TimerInlined.vue'
|
|
28
30
|
import { onContextMenu } from '../logic/contextMenu'
|
|
29
31
|
import { registerShortcuts } from '../logic/shortcuts'
|
|
30
|
-
import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showPresenterCursor } from '../state'
|
|
32
|
+
import { cursorStyle, decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showPresenterCursor } from '../state'
|
|
31
33
|
import { sharedState } from '../state/shared'
|
|
32
34
|
|
|
33
35
|
const inFocus = useWindowFocus()
|
|
@@ -244,28 +246,20 @@ if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
|
|
|
244
246
|
|
|
245
247
|
// sync presenter cursor
|
|
246
248
|
onMounted(() => {
|
|
247
|
-
const
|
|
248
|
-
const mouse = reactive(useMouse())
|
|
249
|
+
const mouse = useMousePosInSlide()
|
|
249
250
|
const focus = useWindowFocus()
|
|
250
251
|
|
|
251
|
-
|
|
252
|
-
()
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return { x, y }
|
|
264
|
-
},
|
|
265
|
-
(pos) => {
|
|
266
|
-
sharedState.cursor = pos
|
|
267
|
-
},
|
|
268
|
-
)
|
|
252
|
+
watchEffect(() => {
|
|
253
|
+
if (!mouse.value || !focus.value || isDrawing.value || !showPresenterCursor.value) {
|
|
254
|
+
sharedState.cursor = undefined
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
sharedState.cursor = {
|
|
258
|
+
...mouse.value,
|
|
259
|
+
style: cursorStyle.value,
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
})
|
|
269
263
|
})
|
|
270
264
|
</script>
|
|
271
265
|
|
|
@@ -328,6 +322,7 @@ onMounted(() => {
|
|
|
328
322
|
@contextmenu="onContextMenu"
|
|
329
323
|
>
|
|
330
324
|
<SlidesShow render-context="presenter" />
|
|
325
|
+
<LaserPointer />
|
|
331
326
|
</SlideContainer>
|
|
332
327
|
|
|
333
328
|
<ClicksSlider
|
package/setup/main.ts
CHANGED
|
@@ -9,8 +9,7 @@ import { createVDragDirective } from '../modules/v-drag'
|
|
|
9
9
|
import { createVMarkDirective } from '../modules/v-mark'
|
|
10
10
|
import { createVMotionDirectives } from '../modules/v-motion'
|
|
11
11
|
import setupRoutes from '../setup/routes'
|
|
12
|
-
|
|
13
|
-
import '#slidev/styles'
|
|
12
|
+
import '../styles'
|
|
14
13
|
|
|
15
14
|
export default async function setupMain(app: App) {
|
|
16
15
|
function setMaxHeight() {
|
package/setup/monaco.ts
CHANGED
|
@@ -56,12 +56,12 @@ StandaloneServices.initialize({
|
|
|
56
56
|
})
|
|
57
57
|
|
|
58
58
|
const setup = createSingletonPromise(async () => {
|
|
59
|
-
const defaults = monaco.
|
|
59
|
+
const defaults = monaco.typescript.typescriptDefaults
|
|
60
60
|
defaults.setCompilerOptions({
|
|
61
61
|
...defaults.getCompilerOptions(),
|
|
62
62
|
strict: true,
|
|
63
|
-
moduleResolution: monaco.
|
|
64
|
-
module: monaco.
|
|
63
|
+
moduleResolution: monaco.typescript.ModuleResolutionKind.NodeJs,
|
|
64
|
+
module: monaco.typescript.ModuleKind.ESNext,
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
const ata = configs.monacoTypesSource === 'cdn'
|
|
@@ -137,7 +137,7 @@ const setup = createSingletonPromise(async () => {
|
|
|
137
137
|
async function _addFile(raw: () => Promise<{ default: string }>, path: string) {
|
|
138
138
|
const uri = monaco.Uri.file(path)
|
|
139
139
|
const code = (await raw()).default
|
|
140
|
-
monaco.
|
|
140
|
+
monaco.typescript.typescriptDefaults.addExtraLib(code, `file:///${path}`)
|
|
141
141
|
monaco.editor.createModel(code, 'javascript', uri)
|
|
142
142
|
}
|
|
143
143
|
|
package/setup/root.ts
CHANGED
package/shim-vue.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
declare module 'vue' {
|
|
2
|
-
type
|
|
2
|
+
import type { ShallowUnwrapRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
type SlideContext = ShallowUnwrapRef<import('./context').SlideContext>
|
|
3
5
|
interface ComponentCustomProperties extends SlideContext {
|
|
4
6
|
}
|
|
5
7
|
}
|