@slidev/client 0.48.9 → 0.49.0-beta.2
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 +3 -3
- package/builtin/KaTexBlockWrapper.vue +3 -3
- package/builtin/Monaco.vue +3 -0
- package/builtin/ShikiMagicMove.vue +3 -3
- package/builtin/SlidevVideo.vue +53 -65
- package/builtin/VClicks.ts +7 -2
- package/builtin/VDrag.vue +27 -0
- package/composables/useClicks.ts +80 -32
- package/composables/useDragElements.ts +286 -0
- package/composables/useNav.ts +20 -0
- package/composables/useSlideBounds.ts +30 -0
- package/composables/useSlideInfo.ts +12 -5
- package/constants.ts +3 -1
- package/env.ts +2 -0
- package/internals/CodeRunner.vue +26 -3
- package/internals/ContextMenu.vue +110 -0
- package/internals/Controls.vue +2 -0
- package/internals/DragControl.vue +397 -0
- package/internals/NavControls.vue +6 -11
- package/internals/SlideContainer.vue +10 -8
- package/internals/SlideWrapper.vue +15 -6
- package/internals/SlidesShow.vue +10 -17
- package/logic/contextMenu.ts +34 -0
- package/logic/utils.ts +0 -18
- package/modules/v-click.ts +34 -70
- package/modules/v-drag.ts +44 -0
- package/modules/v-mark.ts +3 -3
- package/modules/v-motion.ts +15 -26
- package/package.json +13 -13
- package/pages/play.vue +4 -2
- package/pages/presenter.vue +4 -0
- package/setup/code-runners.ts +7 -4
- package/setup/context-menu.ts +113 -0
- package/setup/main.ts +2 -0
- package/setup/shortcuts.ts +7 -5
- package/state/index.ts +4 -1
|
@@ -63,10 +63,10 @@ onMounted(() => {
|
|
|
63
63
|
if (!clicks || !props.ranges?.length)
|
|
64
64
|
return
|
|
65
65
|
|
|
66
|
-
const
|
|
67
|
-
clicks.register(id,
|
|
66
|
+
const clicksInfo = clicks.calculateSince(props.at, props.ranges.length - 1)
|
|
67
|
+
clicks.register(id, clicksInfo)
|
|
68
68
|
|
|
69
|
-
const index = computed(() => Math.max(0, clicks.current - start + 1))
|
|
69
|
+
const index = computed(() => Math.max(0, clicks.current - clicksInfo.start + 1))
|
|
70
70
|
|
|
71
71
|
const finallyRange = computed(() => {
|
|
72
72
|
return props.finally === 'last' ? props.ranges.at(-1) : props.finally.toString()
|
|
@@ -58,10 +58,10 @@ onMounted(() => {
|
|
|
58
58
|
if (!clicks || !props.ranges?.length)
|
|
59
59
|
return
|
|
60
60
|
|
|
61
|
-
const
|
|
62
|
-
clicks.register(id,
|
|
61
|
+
const clicksInfo = clicks.calculateSince(props.at, props.ranges.length - 1)
|
|
62
|
+
clicks.register(id, clicksInfo)
|
|
63
63
|
|
|
64
|
-
const index = computed(() => Math.max(0, clicks.current - start + 1))
|
|
64
|
+
const index = computed(() => Math.max(0, clicks.current - clicksInfo.start + 1))
|
|
65
65
|
|
|
66
66
|
const finallyRange = computed(() => {
|
|
67
67
|
return props.finally === 'last' ? props.ranges.at(-1) : props.finally.toString()
|
package/builtin/Monaco.vue
CHANGED
|
@@ -16,6 +16,7 @@ import { debounce } from '@antfu/utils'
|
|
|
16
16
|
import lz from 'lz-string'
|
|
17
17
|
import type * as monaco from 'monaco-editor'
|
|
18
18
|
import { computed, nextTick, onMounted, ref } from 'vue'
|
|
19
|
+
import type { RawAtValue } from '@slidev/types'
|
|
19
20
|
import { makeId } from '../logic/utils'
|
|
20
21
|
import CodeRunner from '../internals/CodeRunner.vue'
|
|
21
22
|
|
|
@@ -30,6 +31,7 @@ const props = withDefaults(defineProps<{
|
|
|
30
31
|
ata?: boolean
|
|
31
32
|
runnable?: boolean
|
|
32
33
|
autorun?: boolean | 'once'
|
|
34
|
+
showOutputAt?: RawAtValue
|
|
33
35
|
outputHeight?: string
|
|
34
36
|
highlightOutput?: boolean
|
|
35
37
|
runnerOptions?: Record<string, unknown>
|
|
@@ -165,6 +167,7 @@ onMounted(async () => {
|
|
|
165
167
|
v-model="code"
|
|
166
168
|
:lang="lang"
|
|
167
169
|
:autorun="props.autorun"
|
|
170
|
+
:show-output-at="props.showOutputAt"
|
|
168
171
|
:height="props.outputHeight"
|
|
169
172
|
:highlight-output="props.highlightOutput"
|
|
170
173
|
:runner-options="props.runnerOptions"
|
|
@@ -36,14 +36,14 @@ onMounted(() => {
|
|
|
36
36
|
throw new Error('[slidev] The length of stepRanges does not match the length of steps, this is an internal error.')
|
|
37
37
|
|
|
38
38
|
const clickCounts = ranges.value.map(s => s.length).reduce((a, b) => a + b, 0)
|
|
39
|
-
const
|
|
40
|
-
clicks.register(id,
|
|
39
|
+
const clickInfo = clicks.calculateSince(props.at ?? '+1', clickCounts - 1)
|
|
40
|
+
clicks.register(id, clickInfo)
|
|
41
41
|
|
|
42
42
|
watch(
|
|
43
43
|
() => clicks.current,
|
|
44
44
|
() => {
|
|
45
45
|
// Calculate the step and rangeStr based on the current click count
|
|
46
|
-
const clickCount = clicks.current - start
|
|
46
|
+
const clickCount = clicks.current - clickInfo.start
|
|
47
47
|
let step = steps.length - 1
|
|
48
48
|
let currentClickSum = 0
|
|
49
49
|
let rangeStr = 'all'
|
package/builtin/SlidevVideo.vue
CHANGED
|
@@ -1,86 +1,74 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, onMounted,
|
|
2
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
|
3
|
+
import { and } from '@vueuse/math'
|
|
3
4
|
import { useSlideContext } from '../context'
|
|
5
|
+
import { resolvedClickMap } from '../modules/v-click'
|
|
6
|
+
import { useNav } from '../composables/useNav'
|
|
4
7
|
|
|
5
8
|
const props = defineProps<{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
autoplay?: boolean | 'once'
|
|
10
|
+
autoreset?: 'slide' | 'click'
|
|
11
|
+
poster?: string
|
|
12
|
+
printPoster?: string
|
|
13
|
+
timestamp?: string | number
|
|
14
|
+
printTimestamp?: string | number | 'last'
|
|
15
|
+
controls?: boolean
|
|
9
16
|
}>()
|
|
10
17
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
$clicksContext: clicks,
|
|
14
|
-
$renderContext: currentContext,
|
|
15
|
-
$route: route,
|
|
16
|
-
} = useSlideContext()
|
|
18
|
+
const printPoster = computed(() => props.printPoster ?? props.poster)
|
|
19
|
+
const printTimestamp = computed(() => props.printTimestamp ?? props.timestamp ?? 0)
|
|
17
20
|
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const ended = ref(false)
|
|
21
|
-
|
|
22
|
-
const matchRoute = computed(() => {
|
|
23
|
-
if (!video.value || currentContext?.value !== 'slide')
|
|
24
|
-
return false
|
|
25
|
-
return route && route.no === $slidev?.nav.currentSlideNo
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
const matchClick = computed(() => {
|
|
29
|
-
if (!video.value || currentContext?.value !== 'slide' || !clicks)
|
|
30
|
-
return false
|
|
31
|
-
return clicks.map.get(video.value)?.isShown?.value ?? true
|
|
32
|
-
})
|
|
21
|
+
const { $slidev, $renderContext, $route } = useSlideContext()
|
|
22
|
+
const { isPrintMode } = useNav()
|
|
33
23
|
|
|
34
|
-
const
|
|
24
|
+
const noPlay = computed(() => isPrintMode.value || !['slide', 'presenter'].includes($renderContext.value))
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
39
|
-
|
|
40
|
-
if (matchRouteAndClick.value) {
|
|
41
|
-
if (props.autoReset === 'click')
|
|
42
|
-
video.value.currentTime = 0
|
|
43
|
-
if (props.autoPlay && (!played.value || props.autoPlay === 'resume' || (props.autoPlay === 'resumeOnce' && !ended.value)))
|
|
44
|
-
video.value.play()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if ((props.autoPause === 'click' && !matchRouteAndClick.value) || (props.autoPause === 'slide' && !matchRoute.value))
|
|
48
|
-
video.value.pause()
|
|
49
|
-
})
|
|
26
|
+
const video = ref<HTMLMediaElement>()
|
|
27
|
+
const played = ref(false)
|
|
50
28
|
|
|
51
|
-
|
|
52
|
-
if (
|
|
29
|
+
onMounted(() => {
|
|
30
|
+
if (noPlay.value)
|
|
53
31
|
return
|
|
54
32
|
|
|
55
|
-
|
|
56
|
-
|
|
33
|
+
const timestamp = +(props.timestamp ?? 0)
|
|
34
|
+
video.value!.currentTime = timestamp
|
|
35
|
+
|
|
36
|
+
const matchRoute = computed(() => !!$route && $route.no === $slidev?.nav.currentSlideNo)
|
|
37
|
+
const matchClick = computed(() => !!video.value && (resolvedClickMap.get(video.value)?.isShown?.value ?? true))
|
|
38
|
+
const matchRouteAndClick = and(matchRoute, matchClick)
|
|
39
|
+
|
|
40
|
+
watch(matchRouteAndClick, () => {
|
|
41
|
+
if (matchRouteAndClick.value) {
|
|
42
|
+
if (props.autoplay === true || (props.autoplay === 'once' && !played.value))
|
|
43
|
+
video.value!.play()
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
video.value!.pause()
|
|
47
|
+
if (props.autoreset === 'click' || (props.autoreset === 'slide' && !matchRoute.value))
|
|
48
|
+
video.value!.currentTime = timestamp
|
|
49
|
+
}
|
|
50
|
+
}, { immediate: true })
|
|
57
51
|
})
|
|
58
52
|
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
function onLoadedMetadata(ev: Event) {
|
|
54
|
+
// The video may be loaded before component mounted
|
|
55
|
+
const element = ev.target as HTMLMediaElement
|
|
56
|
+
if (noPlay.value && (!printPoster.value || props.printTimestamp)) {
|
|
57
|
+
element.currentTime = printTimestamp.value === 'last'
|
|
58
|
+
? element.duration
|
|
59
|
+
: +printTimestamp.value
|
|
60
|
+
}
|
|
65
61
|
}
|
|
66
|
-
|
|
67
|
-
onMounted(() => {
|
|
68
|
-
if (!video.value || currentContext?.value !== 'slide')
|
|
69
|
-
return
|
|
70
|
-
video.value?.addEventListener('play', onPlay)
|
|
71
|
-
video.value?.addEventListener('ended', onEnded)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
onUnmounted(() => {
|
|
75
|
-
if (!video.value || currentContext?.value !== 'slide')
|
|
76
|
-
return
|
|
77
|
-
video.value?.removeEventListener('play', onPlay)
|
|
78
|
-
video.value?.removeEventListener('ended', onEnded)
|
|
79
|
-
})
|
|
80
62
|
</script>
|
|
81
63
|
|
|
82
64
|
<template>
|
|
83
|
-
<video
|
|
65
|
+
<video
|
|
66
|
+
ref="video"
|
|
67
|
+
:poster="noPlay ? printPoster : props.poster"
|
|
68
|
+
:controls="!noPlay && props.controls"
|
|
69
|
+
@play="played = true"
|
|
70
|
+
@loadedmetadata="onLoadedMetadata"
|
|
71
|
+
>
|
|
84
72
|
<slot />
|
|
85
73
|
</video>
|
|
86
74
|
</template>
|
package/builtin/VClicks.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { toArray } from '@antfu/utils'
|
|
8
8
|
import type { VNode, VNodeArrayChildren } from 'vue'
|
|
9
9
|
import { Comment, createVNode, defineComponent, h, isVNode, resolveDirective, withDirectives } from 'vue'
|
|
10
|
-
import {
|
|
10
|
+
import { normalizeAtValue } from '../composables/useClicks'
|
|
11
11
|
import VClickGap from './VClickGap.vue'
|
|
12
12
|
|
|
13
13
|
const listTags = ['ul', 'ol']
|
|
@@ -37,7 +37,12 @@ export default defineComponent({
|
|
|
37
37
|
},
|
|
38
38
|
render() {
|
|
39
39
|
const every = +this.every
|
|
40
|
-
const
|
|
40
|
+
const at = normalizeAtValue(this.at)
|
|
41
|
+
const isRelative = typeof at === 'string'
|
|
42
|
+
if (typeof at !== 'string' && typeof at !== 'number') {
|
|
43
|
+
console.warn('[slidev] Invalid at prop for v-clicks component:', at)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
41
46
|
|
|
42
47
|
const click = resolveDirective('click')!
|
|
43
48
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, onUnmounted } from 'vue'
|
|
3
|
+
import type { DragElementMarkdownSource } from '../composables/useDragElements'
|
|
4
|
+
import { useDragElement } from '../composables/useDragElements'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
pos?: string
|
|
8
|
+
markdownSource?: DragElementMarkdownSource
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const { id, container, containerStyle, mounted, unmounted, startDragging } = useDragElement(null, props.pos, props.markdownSource)
|
|
12
|
+
|
|
13
|
+
onMounted(mounted)
|
|
14
|
+
onUnmounted(unmounted)
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<div
|
|
19
|
+
ref="container"
|
|
20
|
+
:data-drag-id="id"
|
|
21
|
+
:style="containerStyle"
|
|
22
|
+
class="p-1"
|
|
23
|
+
@dblclick="startDragging"
|
|
24
|
+
>
|
|
25
|
+
<slot />
|
|
26
|
+
</div>
|
|
27
|
+
</template>
|
package/composables/useClicks.ts
CHANGED
|
@@ -1,67 +1,115 @@
|
|
|
1
1
|
import { clamp, sum } from '@antfu/utils'
|
|
2
|
-
import type { ClicksContext, SlideRoute } from '@slidev/types'
|
|
2
|
+
import type { ClicksContext, NormalizedAtValue, RawAtValue, SlideRoute } from '@slidev/types'
|
|
3
3
|
import type { Ref } from 'vue'
|
|
4
|
-
import { ref, shallowReactive } from 'vue'
|
|
5
|
-
import { normalizeAtProp } from '../logic/utils'
|
|
4
|
+
import { computed, ref, shallowReactive } from 'vue'
|
|
6
5
|
import { routeForceRefresh } from '../logic/route'
|
|
7
6
|
|
|
7
|
+
export function normalizeAtValue(at: RawAtValue): NormalizedAtValue {
|
|
8
|
+
if (at === false || at === 'false')
|
|
9
|
+
return null
|
|
10
|
+
if (at == null || at === true || at === 'true')
|
|
11
|
+
return '+1'
|
|
12
|
+
if (Array.isArray(at))
|
|
13
|
+
return [+at[0], +at[1]]
|
|
14
|
+
if (typeof at === 'string' && '+-'.includes(at[0]))
|
|
15
|
+
return at
|
|
16
|
+
return +at
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
export function createClicksContextBase(
|
|
9
20
|
current: Ref<number>,
|
|
10
21
|
clicksStart = 0,
|
|
11
22
|
clicksTotalOverrides?: number,
|
|
12
23
|
): ClicksContext {
|
|
13
|
-
const
|
|
14
|
-
const map: ClicksContext['map'] = shallowReactive(new Map())
|
|
15
|
-
|
|
16
|
-
return {
|
|
24
|
+
const context: ClicksContext = {
|
|
17
25
|
get current() {
|
|
18
26
|
// Here we haven't know clicksTotal yet.
|
|
19
|
-
return clamp(+current.value, clicksStart,
|
|
27
|
+
return clamp(+current.value, clicksStart, context.total)
|
|
20
28
|
},
|
|
21
29
|
set current(value) {
|
|
22
|
-
current.value = clamp(+value, clicksStart,
|
|
30
|
+
current.value = clamp(+value, clicksStart, context.total)
|
|
23
31
|
},
|
|
24
32
|
clicksStart,
|
|
25
|
-
relativeOffsets,
|
|
26
|
-
|
|
33
|
+
relativeOffsets: new Map(),
|
|
34
|
+
maxMap: shallowReactive(new Map()),
|
|
27
35
|
onMounted() { },
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
const offset =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
36
|
+
calculateSince(at, size = 1) {
|
|
37
|
+
let start: number, max: number, delta: number
|
|
38
|
+
if (typeof at === 'string') {
|
|
39
|
+
const offset = context.currentOffset
|
|
40
|
+
const value = +at
|
|
41
|
+
start = offset + value
|
|
42
|
+
max = offset + value + size - 1
|
|
43
|
+
delta = value + size - 1
|
|
37
44
|
}
|
|
38
45
|
else {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
start = at
|
|
47
|
+
max = at + size - 1
|
|
48
|
+
delta = 0
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
start,
|
|
52
|
+
end: +Number.POSITIVE_INFINITY,
|
|
53
|
+
max,
|
|
54
|
+
delta,
|
|
55
|
+
isCurrent: computed(() => context.current === start),
|
|
56
|
+
isActive: computed(() => context.current >= start),
|
|
44
57
|
}
|
|
45
58
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
calculateRange([a, b]) {
|
|
60
|
+
let start: number, end: number, delta: number
|
|
61
|
+
if (typeof a === 'string') {
|
|
62
|
+
const offset = context.currentOffset
|
|
63
|
+
start = offset + +a
|
|
64
|
+
delta = +a
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
start = a
|
|
68
|
+
delta = 0
|
|
69
|
+
}
|
|
70
|
+
if (typeof b === 'string') {
|
|
71
|
+
end = start + +b
|
|
72
|
+
delta += +b
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
end = b
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
start,
|
|
79
|
+
end,
|
|
80
|
+
max: end,
|
|
81
|
+
delta,
|
|
82
|
+
isCurrent: computed(() => context.current === start),
|
|
83
|
+
isActive: computed(() => start <= context.current && context.current < end),
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
calculate(at) {
|
|
87
|
+
if (at == null)
|
|
88
|
+
return null
|
|
89
|
+
if (Array.isArray(at))
|
|
90
|
+
return context.calculateRange(at)
|
|
91
|
+
return context.calculateSince(at)
|
|
92
|
+
},
|
|
93
|
+
register(el, { delta, max }) {
|
|
94
|
+
context.relativeOffsets.set(el, delta)
|
|
95
|
+
context.maxMap.set(el, max)
|
|
49
96
|
},
|
|
50
97
|
unregister(el) {
|
|
51
|
-
relativeOffsets.delete(el)
|
|
52
|
-
|
|
98
|
+
context.relativeOffsets.delete(el)
|
|
99
|
+
context.maxMap.delete(el)
|
|
53
100
|
},
|
|
54
101
|
get currentOffset() {
|
|
55
102
|
// eslint-disable-next-line no-unused-expressions
|
|
56
103
|
routeForceRefresh.value
|
|
57
|
-
return sum(...relativeOffsets.values())
|
|
104
|
+
return sum(...context.relativeOffsets.values())
|
|
58
105
|
},
|
|
59
106
|
get total() {
|
|
60
107
|
// eslint-disable-next-line no-unused-expressions
|
|
61
108
|
routeForceRefresh.value
|
|
62
|
-
return clicksTotalOverrides ?? Math.max(0, ...
|
|
109
|
+
return clicksTotalOverrides ?? Math.max(0, ...context.maxMap.values())
|
|
63
110
|
},
|
|
64
111
|
}
|
|
112
|
+
return context
|
|
65
113
|
}
|
|
66
114
|
|
|
67
115
|
export function createFixedClicks(
|