@slidev/client 0.49.0-beta.1 → 0.49.0-beta.3
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 +27 -4
- package/builtin/ShikiMagicMove.vue +3 -3
- package/builtin/SlidevVideo.vue +3 -7
- package/builtin/VClicks.ts +7 -2
- package/composables/useClicks.ts +80 -32
- package/composables/useDragElements.ts +7 -3
- package/composables/useNav.ts +20 -0
- package/constants.ts +1 -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 +1 -0
- package/internals/NavControls.vue +6 -11
- package/internals/QuickOverview.vue +6 -0
- package/logic/contextMenu.ts +34 -0
- package/logic/utils.ts +0 -18
- package/modules/v-click.ts +34 -70
- package/modules/v-mark.ts +3 -3
- package/modules/v-motion.ts +15 -26
- package/package.json +8 -8
- 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 -2
- package/setup/monaco.ts +0 -3
- package/setup/routes.ts +80 -0
- package/routes.ts +0 -68
|
@@ -102,6 +102,11 @@ watchEffect(() => {
|
|
|
102
102
|
// Watch rowCount, make sure up and down shortcut work correctly.
|
|
103
103
|
overviewRowCount.value = rowCount.value
|
|
104
104
|
})
|
|
105
|
+
|
|
106
|
+
const activeSlidesLoaded = ref(false)
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
activeSlidesLoaded.value = true
|
|
109
|
+
}, 3000)
|
|
105
110
|
</script>
|
|
106
111
|
|
|
107
112
|
<template>
|
|
@@ -112,6 +117,7 @@ watchEffect(() => {
|
|
|
112
117
|
leave-to-class="opacity-0 scale-102 !backdrop-blur-0px"
|
|
113
118
|
>
|
|
114
119
|
<div
|
|
120
|
+
v-if="value || activeSlidesLoaded"
|
|
115
121
|
v-show="value"
|
|
116
122
|
class="bg-main !bg-opacity-75 p-16 py-20 overflow-y-auto backdrop-blur-5px fixed left-0 right-0 top-0 h-[calc(var(--vh,1vh)*100)]"
|
|
117
123
|
@click="close()"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ContextMenuItem } from '@slidev/types'
|
|
2
|
+
import type { ComputedRef } from 'vue'
|
|
3
|
+
import { shallowRef } from 'vue'
|
|
4
|
+
import setupContextMenu from '../setup/context-menu'
|
|
5
|
+
import { configs, mode } from '../env'
|
|
6
|
+
|
|
7
|
+
export const currentContextMenu = shallowRef<null | {
|
|
8
|
+
x: number
|
|
9
|
+
y: number
|
|
10
|
+
items: ComputedRef<ContextMenuItem[]>
|
|
11
|
+
}>(null)
|
|
12
|
+
|
|
13
|
+
export function openContextMenu(x: number, y: number) {
|
|
14
|
+
currentContextMenu.value = {
|
|
15
|
+
x,
|
|
16
|
+
y,
|
|
17
|
+
items: setupContextMenu(),
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function closeContextMenu() {
|
|
22
|
+
currentContextMenu.value = null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function onContextMenu(ev: MouseEvent) {
|
|
26
|
+
if (configs.contextMenu !== true && configs.contextMenu !== undefined && configs.contextMenu !== mode)
|
|
27
|
+
return
|
|
28
|
+
if (ev.shiftKey || ev.defaultPrevented)
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
openContextMenu(ev.pageX, ev.pageY)
|
|
32
|
+
ev.preventDefault()
|
|
33
|
+
ev.stopPropagation()
|
|
34
|
+
}
|
package/logic/utils.ts
CHANGED
|
@@ -32,24 +32,6 @@ export function makeId(length = 5) {
|
|
|
32
32
|
return result.join('')
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
/**
|
|
36
|
-
* '+3' => '+3'
|
|
37
|
-
* '-3' => '-3'
|
|
38
|
-
* '3' => 3
|
|
39
|
-
* 3 => 3
|
|
40
|
-
*/
|
|
41
|
-
export function normalizeAtProp(at: string | number = '+1'): [isRelative: boolean, value: number] {
|
|
42
|
-
let n = +at
|
|
43
|
-
if (Number.isNaN(n)) {
|
|
44
|
-
console.warn('[slidev] Invalid click position:', at)
|
|
45
|
-
n = 0
|
|
46
|
-
}
|
|
47
|
-
return [
|
|
48
|
-
typeof at === 'string' && '+-'.includes(at[0]),
|
|
49
|
-
n,
|
|
50
|
-
]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
35
|
export function updateCodeHighlightRange(
|
|
54
36
|
rangeStr: string,
|
|
55
37
|
linesCount: number,
|
package/modules/v-click.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ResolvedClicksInfo } from '@slidev/types'
|
|
2
1
|
import type { App, DirectiveBinding } from 'vue'
|
|
3
2
|
import { computed, watchEffect } from 'vue'
|
|
3
|
+
import type { ClicksElement, RawAtValue } from '@slidev/types'
|
|
4
4
|
import {
|
|
5
5
|
CLASS_VCLICK_CURRENT,
|
|
6
6
|
CLASS_VCLICK_FADE,
|
|
@@ -8,32 +8,29 @@ import {
|
|
|
8
8
|
CLASS_VCLICK_HIDDEN_EXP,
|
|
9
9
|
CLASS_VCLICK_PRIOR,
|
|
10
10
|
CLASS_VCLICK_TARGET,
|
|
11
|
-
injectionClickVisibility,
|
|
12
11
|
injectionClicksContext,
|
|
13
12
|
} from '../constants'
|
|
14
|
-
import { directiveInject
|
|
15
|
-
|
|
16
|
-
export type VClickValue = undefined | string | number | [string | number, string | number] | boolean
|
|
13
|
+
import { directiveInject } from '../utils'
|
|
14
|
+
import { normalizeAtValue } from '../composables/useClicks'
|
|
17
15
|
|
|
18
16
|
export function createVClickDirectives() {
|
|
19
17
|
return {
|
|
20
18
|
install(app: App) {
|
|
21
|
-
app.directive<HTMLElement,
|
|
19
|
+
app.directive<HTMLElement, RawAtValue>('click', {
|
|
22
20
|
// @ts-expect-error extra prop
|
|
23
21
|
name: 'v-click',
|
|
24
22
|
|
|
25
23
|
mounted(el, dir) {
|
|
26
|
-
const resolved = resolveClick(el, dir, dir.value
|
|
24
|
+
const resolved = resolveClick(el, dir, dir.value)
|
|
27
25
|
if (resolved == null)
|
|
28
26
|
return
|
|
29
27
|
|
|
30
28
|
el.classList.toggle(CLASS_VCLICK_TARGET, true)
|
|
31
29
|
|
|
32
30
|
// Expose the resolved clicks info to the element to make it easier to understand and debug
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
el.dataset.slidevClicksEnd = String(clicks[1])
|
|
31
|
+
el.dataset.slidevClicksStart = String(resolved.start)
|
|
32
|
+
if (Number.isFinite(resolved.end))
|
|
33
|
+
el.dataset.slidevClicksEnd = String(resolved.end)
|
|
37
34
|
|
|
38
35
|
// @ts-expect-error extra prop
|
|
39
36
|
el.watchStopHandle = watchEffect(() => {
|
|
@@ -56,12 +53,12 @@ export function createVClickDirectives() {
|
|
|
56
53
|
unmounted,
|
|
57
54
|
})
|
|
58
55
|
|
|
59
|
-
app.directive<HTMLElement,
|
|
56
|
+
app.directive<HTMLElement, RawAtValue>('after', {
|
|
60
57
|
// @ts-expect-error extra prop
|
|
61
58
|
name: 'v-after',
|
|
62
59
|
|
|
63
60
|
mounted(el, dir) {
|
|
64
|
-
const resolved = resolveClick(el, dir,
|
|
61
|
+
const resolved = resolveClick(el, dir, '+0')
|
|
65
62
|
if (resolved == null)
|
|
66
63
|
return
|
|
67
64
|
|
|
@@ -88,12 +85,12 @@ export function createVClickDirectives() {
|
|
|
88
85
|
unmounted,
|
|
89
86
|
})
|
|
90
87
|
|
|
91
|
-
app.directive<HTMLElement,
|
|
88
|
+
app.directive<HTMLElement, RawAtValue>('click-hide', {
|
|
92
89
|
// @ts-expect-error extra prop
|
|
93
90
|
name: 'v-click-hide',
|
|
94
91
|
|
|
95
92
|
mounted(el, dir) {
|
|
96
|
-
const resolved = resolveClick(el, dir, dir.value, true
|
|
93
|
+
const resolved = resolveClick(el, dir, dir.value, true)
|
|
97
94
|
if (resolved == null)
|
|
98
95
|
return
|
|
99
96
|
|
|
@@ -118,75 +115,42 @@ export function createVClickDirectives() {
|
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
|
|
122
|
-
return Array.isArray(thisClick)
|
|
123
|
-
? thisClick[0] <= clicks && clicks < thisClick[1]
|
|
124
|
-
: thisClick <= clicks
|
|
125
|
-
}
|
|
118
|
+
export const resolvedClickMap = new Map<ClicksElement, ReturnType<typeof resolveClick>>()
|
|
126
119
|
|
|
127
|
-
function
|
|
128
|
-
return Array.isArray(thisClick)
|
|
129
|
-
? thisClick[0] === clicks
|
|
130
|
-
: thisClick === clicks
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export function resolveClick(el: Element | string, dir: DirectiveBinding<any>, value: VClickValue, provideVisibility = false, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
|
|
120
|
+
export function resolveClick(el: Element | string, dir: DirectiveBinding<any>, value: RawAtValue, explicitHide = false) {
|
|
134
121
|
const ctx = directiveInject(dir, injectionClicksContext)?.value
|
|
135
122
|
|
|
136
123
|
if (!el || !ctx)
|
|
137
124
|
return null
|
|
138
125
|
|
|
139
|
-
|
|
140
|
-
return null
|
|
141
|
-
|
|
142
|
-
flagHide ||= dir.modifiers.hide !== false && dir.modifiers.hide != null
|
|
126
|
+
const flagHide = explicitHide || (dir.modifiers.hide !== false && dir.modifiers.hide != null)
|
|
143
127
|
const flagFade = dir.modifiers.fade !== false && dir.modifiers.fade != null
|
|
144
128
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let delta: number
|
|
151
|
-
let thisClick: number | [number, number]
|
|
152
|
-
let maxClick: number
|
|
153
|
-
if (Array.isArray(value)) {
|
|
154
|
-
// range (absolute)
|
|
155
|
-
delta = 0
|
|
156
|
-
thisClick = [+value[0], +value[1]]
|
|
157
|
-
maxClick = +value[1]
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
({ start: thisClick, end: maxClick, delta } = ctx.resolve(value))
|
|
161
|
-
}
|
|
129
|
+
const at = normalizeAtValue(value)
|
|
130
|
+
const info = ctx.calculate(at)
|
|
131
|
+
if (!info)
|
|
132
|
+
return null
|
|
162
133
|
|
|
163
|
-
|
|
164
|
-
const isCurrent = computed(() => isClickCurrent(thisClick, ctx.current))
|
|
165
|
-
const isShown = computed(() => flagHide ? !isActive.value : isActive.value)
|
|
134
|
+
ctx.register(el, info)
|
|
166
135
|
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
136
|
+
const isShown = computed(() => flagHide ? !info.isActive.value : info.isActive.value)
|
|
137
|
+
const visibilityState = computed(() => {
|
|
138
|
+
if (isShown.value)
|
|
139
|
+
return 'shown'
|
|
140
|
+
if (Number.isFinite(info.end))
|
|
141
|
+
return ctx.current < info.start ? 'before' : 'after'
|
|
142
|
+
else
|
|
143
|
+
return flagHide ? 'after' : 'before'
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
const resolved = {
|
|
147
|
+
...info,
|
|
173
148
|
isShown,
|
|
149
|
+
visibilityState,
|
|
174
150
|
flagFade,
|
|
175
151
|
flagHide,
|
|
176
152
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (provideVisibility) {
|
|
180
|
-
directiveProvide(dir, injectionClickVisibility, computed(() => {
|
|
181
|
-
if (isShown.value)
|
|
182
|
-
return true
|
|
183
|
-
if (Array.isArray(thisClick))
|
|
184
|
-
return ctx.current < thisClick[0] ? 'before' : 'after'
|
|
185
|
-
else
|
|
186
|
-
return flagHide ? 'after' : 'before'
|
|
187
|
-
}))
|
|
188
|
-
}
|
|
189
|
-
|
|
153
|
+
resolvedClickMap.set(el, resolved)
|
|
190
154
|
return resolved
|
|
191
155
|
}
|
|
192
156
|
|
package/modules/v-mark.ts
CHANGED
|
@@ -2,14 +2,14 @@ import type { RoughAnnotationConfig } from '@slidev/rough-notation'
|
|
|
2
2
|
import { annotate } from '@slidev/rough-notation'
|
|
3
3
|
import type { App } from 'vue'
|
|
4
4
|
import { computed, watchEffect } from 'vue'
|
|
5
|
-
import type {
|
|
5
|
+
import type { RawAtValue } from '@slidev/types'
|
|
6
6
|
import { resolveClick } from './v-click'
|
|
7
7
|
|
|
8
8
|
export interface RoughDirectiveOptions extends Partial<RoughAnnotationConfig> {
|
|
9
|
-
at:
|
|
9
|
+
at: RawAtValue
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export type RoughDirectiveValue =
|
|
12
|
+
export type RoughDirectiveValue = RawAtValue | RoughDirectiveOptions
|
|
13
13
|
|
|
14
14
|
function addClass(options: RoughDirectiveOptions, cls: string) {
|
|
15
15
|
options.class = [options.class, cls].filter(Boolean).join(' ')
|
package/modules/v-motion.ts
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import type { App, ObjectDirective } from 'vue'
|
|
2
2
|
import { watch } from 'vue'
|
|
3
3
|
import { MotionDirective } from '@vueuse/motion'
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
4
|
+
import type { ClicksInfo } from '@slidev/types'
|
|
5
|
+
import { injectionClicksContext, injectionCurrentPage, injectionRenderContext } from '../constants'
|
|
6
6
|
import { useNav } from '../composables/useNav'
|
|
7
7
|
import { makeId } from '../logic/utils'
|
|
8
8
|
import { directiveInject } from '../utils'
|
|
9
|
-
import
|
|
10
|
-
import { resolveClick } from './v-click'
|
|
11
|
-
|
|
12
|
-
export type MotionDirectiveValue = undefined | VClickValue | {
|
|
13
|
-
key?: string
|
|
14
|
-
at?: VClickValue
|
|
15
|
-
}
|
|
9
|
+
import { resolvedClickMap } from './v-click'
|
|
16
10
|
|
|
17
11
|
export function createVMotionDirectives() {
|
|
18
12
|
return {
|
|
@@ -22,6 +16,11 @@ export function createVMotionDirectives() {
|
|
|
22
16
|
// @ts-expect-error extra prop
|
|
23
17
|
name: 'v-motion',
|
|
24
18
|
mounted(el, binding, node, prevNode) {
|
|
19
|
+
const clicksContext = directiveInject(binding, injectionClicksContext)
|
|
20
|
+
const thisPage = directiveInject(binding, injectionCurrentPage)
|
|
21
|
+
const renderContext = directiveInject(binding, injectionRenderContext)
|
|
22
|
+
const { currentPage, clicks: currentClicks, isPrintMode } = useNav()
|
|
23
|
+
|
|
25
24
|
const props = node.props = { ...node.props }
|
|
26
25
|
|
|
27
26
|
const variantInitial = { ...props.initial, ...props.variants?.['slidev-initial'] }
|
|
@@ -36,7 +35,7 @@ export function createVMotionDirectives() {
|
|
|
36
35
|
id: string
|
|
37
36
|
at: number | [number, number]
|
|
38
37
|
variant: Record<string, unknown>
|
|
39
|
-
|
|
38
|
+
info: ClicksInfo | null | undefined
|
|
40
39
|
}[] = []
|
|
41
40
|
|
|
42
41
|
for (const k of Object.keys(props)) {
|
|
@@ -48,7 +47,7 @@ export function createVMotionDirectives() {
|
|
|
48
47
|
id,
|
|
49
48
|
at,
|
|
50
49
|
variant: { ...props[k] },
|
|
51
|
-
|
|
50
|
+
info: clicksContext?.value.calculate(at),
|
|
52
51
|
})
|
|
53
52
|
delete props[k]
|
|
54
53
|
}
|
|
@@ -59,11 +58,6 @@ export function createVMotionDirectives() {
|
|
|
59
58
|
original.created!(el, binding, node, prevNode)
|
|
60
59
|
original.mounted!(el, binding, node, prevNode)
|
|
61
60
|
|
|
62
|
-
const thisPage = directiveInject(binding, injectionCurrentPage)
|
|
63
|
-
const renderContext = directiveInject(binding, injectionRenderContext)
|
|
64
|
-
const clickVisibility = directiveInject(binding, injectionClickVisibility)
|
|
65
|
-
const clicksContext = directiveInject(binding, injectionClicksContext)
|
|
66
|
-
const { currentPage, clicks: currentClicks, isPrintMode } = useNav()
|
|
67
61
|
// @ts-expect-error extra prop
|
|
68
62
|
const motion = el.motionInstance
|
|
69
63
|
motion.clickIds = clicks.map(i => i.id)
|
|
@@ -71,7 +65,7 @@ export function createVMotionDirectives() {
|
|
|
71
65
|
motion.watchStopHandle = watch(
|
|
72
66
|
[thisPage, currentPage, currentClicks].filter(Boolean),
|
|
73
67
|
() => {
|
|
74
|
-
const visibility =
|
|
68
|
+
const visibility = resolvedClickMap.get(el)?.visibilityState.value ?? 'shown'
|
|
75
69
|
if (!clicksContext?.value || !['slide', 'presenter'].includes(renderContext?.value ?? '')) {
|
|
76
70
|
const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
|
|
77
71
|
for (const { variant } of clicks)
|
|
@@ -80,10 +74,10 @@ export function createVMotionDirectives() {
|
|
|
80
74
|
motion.set(mixedVariant)
|
|
81
75
|
}
|
|
82
76
|
else if (isPrintMode.value || thisPage?.value === currentPage.value) {
|
|
83
|
-
if (visibility ===
|
|
77
|
+
if (visibility === 'shown') {
|
|
84
78
|
const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
|
|
85
|
-
for (const { variant,
|
|
86
|
-
if (!
|
|
79
|
+
for (const { variant, info } of clicks) {
|
|
80
|
+
if (!info || info.isActive.value)
|
|
87
81
|
Object.assign(mixedVariant, variant)
|
|
88
82
|
}
|
|
89
83
|
if (isPrintMode.value)
|
|
@@ -104,14 +98,9 @@ export function createVMotionDirectives() {
|
|
|
104
98
|
},
|
|
105
99
|
)
|
|
106
100
|
},
|
|
107
|
-
unmounted(el
|
|
108
|
-
if (!directiveInject(dir, injectionClicksContext)?.value)
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
const ctx = directiveInject(dir, injectionClicksContext)?.value
|
|
101
|
+
unmounted(el) {
|
|
112
102
|
// @ts-expect-error extra prop
|
|
113
103
|
const motion = el.motionInstance
|
|
114
|
-
motion.clickIds.map((id: string) => ctx?.unregister(id))
|
|
115
104
|
motion.watchStopHandle()
|
|
116
105
|
},
|
|
117
106
|
})
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.49.0-beta.
|
|
4
|
+
"version": "0.49.0-beta.3",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"@shikijs/vitepress-twoslash": "^1.3.0",
|
|
37
37
|
"@slidev/rough-notation": "^0.1.0",
|
|
38
38
|
"@typescript/ata": "^0.9.4",
|
|
39
|
-
"@unhead/vue": "^1.9.
|
|
40
|
-
"@unocss/reset": "^0.59.
|
|
39
|
+
"@unhead/vue": "^1.9.5",
|
|
40
|
+
"@unocss/reset": "^0.59.2",
|
|
41
41
|
"@vueuse/core": "^10.9.0",
|
|
42
42
|
"@vueuse/math": "^10.9.0",
|
|
43
43
|
"@vueuse/motion": "^2.1.0",
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
"file-saver": "^2.0.5",
|
|
47
47
|
"floating-vue": "^5.2.2",
|
|
48
48
|
"fuse.js": "^7.0.0",
|
|
49
|
-
"js-yaml": "^4.1.0",
|
|
50
49
|
"katex": "^0.16.10",
|
|
51
50
|
"lz-string": "^1.5.0",
|
|
52
51
|
"mermaid": "^10.9.0",
|
|
@@ -55,13 +54,14 @@
|
|
|
55
54
|
"recordrtc": "^5.6.2",
|
|
56
55
|
"shiki": "^1.3.0",
|
|
57
56
|
"shiki-magic-move": "^0.3.5",
|
|
58
|
-
"typescript": "^5.4.
|
|
59
|
-
"unocss": "^0.59.
|
|
57
|
+
"typescript": "^5.4.5",
|
|
58
|
+
"unocss": "^0.59.2",
|
|
60
59
|
"vue": "^3.4.21",
|
|
61
60
|
"vue-demi": "^0.14.7",
|
|
62
61
|
"vue-router": "^4.3.0",
|
|
63
|
-
"
|
|
64
|
-
"@slidev/
|
|
62
|
+
"yaml": "^2.4.1",
|
|
63
|
+
"@slidev/parser": "0.49.0-beta.3",
|
|
64
|
+
"@slidev/types": "0.49.0-beta.3"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"vite": "^5.2.8"
|
package/pages/play.vue
CHANGED
|
@@ -9,6 +9,7 @@ import SlideContainer from '../internals/SlideContainer.vue'
|
|
|
9
9
|
import NavControls from '../internals/NavControls.vue'
|
|
10
10
|
import SlidesShow from '../internals/SlidesShow.vue'
|
|
11
11
|
import PrintStyle from '../internals/PrintStyle.vue'
|
|
12
|
+
import { onContextMenu } from '../logic/contextMenu'
|
|
12
13
|
import { useNav } from '../composables/useNav'
|
|
13
14
|
import { useDrawings } from '../composables/useDrawings'
|
|
14
15
|
|
|
@@ -22,9 +23,9 @@ function onClick(e: MouseEvent) {
|
|
|
22
23
|
if (showEditor.value)
|
|
23
24
|
return
|
|
24
25
|
|
|
25
|
-
if ((e.target as HTMLElement)?.id === 'slide-container') {
|
|
26
|
+
if (e.button === 0 && (e.target as HTMLElement)?.id === 'slide-container') {
|
|
26
27
|
// click right to next, left to previous
|
|
27
|
-
if ((e.
|
|
28
|
+
if ((e.pageX / window.innerWidth) > 0.6)
|
|
28
29
|
next()
|
|
29
30
|
else
|
|
30
31
|
prev()
|
|
@@ -57,6 +58,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
57
58
|
:scale="slideScale"
|
|
58
59
|
:is-main="true"
|
|
59
60
|
@pointerdown="onClick"
|
|
61
|
+
@contextmenu="onContextMenu"
|
|
60
62
|
>
|
|
61
63
|
<template #default>
|
|
62
64
|
<SlidesShow render-context="slide" />
|
package/pages/presenter.vue
CHANGED
|
@@ -7,6 +7,7 @@ import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout,
|
|
|
7
7
|
import { configs } from '../env'
|
|
8
8
|
import { sharedState } from '../state/shared'
|
|
9
9
|
import { registerShortcuts } from '../logic/shortcuts'
|
|
10
|
+
import { onContextMenu } from '../logic/contextMenu'
|
|
10
11
|
import { getSlideClass } from '../utils'
|
|
11
12
|
import { useTimer } from '../logic/utils'
|
|
12
13
|
import { createFixedClicks } from '../composables/useClicks'
|
|
@@ -21,6 +22,7 @@ import SlidesShow from '../internals/SlidesShow.vue'
|
|
|
21
22
|
import DrawingControls from '../internals/DrawingControls.vue'
|
|
22
23
|
import IconButton from '../internals/IconButton.vue'
|
|
23
24
|
import ClicksSlider from '../internals/ClicksSlider.vue'
|
|
25
|
+
import ContextMenu from '../internals/ContextMenu.vue'
|
|
24
26
|
import { useNav } from '../composables/useNav'
|
|
25
27
|
import { useDrawings } from '../composables/useDrawings'
|
|
26
28
|
|
|
@@ -112,6 +114,7 @@ onMounted(() => {
|
|
|
112
114
|
<SlideContainer
|
|
113
115
|
key="main"
|
|
114
116
|
class="h-full w-full p-2 lg:p-4 flex-auto"
|
|
117
|
+
@contextmenu="onContextMenu"
|
|
115
118
|
>
|
|
116
119
|
<template #default>
|
|
117
120
|
<SlidesShow render-context="presenter" />
|
|
@@ -209,6 +212,7 @@ onMounted(() => {
|
|
|
209
212
|
</div>
|
|
210
213
|
<Goto />
|
|
211
214
|
<QuickOverview v-model="showOverview" />
|
|
215
|
+
<ContextMenu />
|
|
212
216
|
</template>
|
|
213
217
|
|
|
214
218
|
<style scoped>
|
package/setup/code-runners.ts
CHANGED
|
@@ -26,9 +26,9 @@ export default createSingletonPromise(async () => {
|
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
const resolveId = async (specifier: string) => {
|
|
29
|
-
if (!/^(@[^\/:]+?\/)?[^\/:]+$/.test(specifier))
|
|
30
|
-
return specifier
|
|
31
|
-
const res = await fetch(`/@slidev/resolve-id
|
|
29
|
+
if (!'./'.includes(specifier[0]) && !/^(@[^\/:]+?\/)?[^\/:]+$/.test(specifier))
|
|
30
|
+
return specifier // this might be a url or something else
|
|
31
|
+
const res = await fetch(`/@slidev/resolve-id?specifier=${specifier}`)
|
|
32
32
|
if (!res.ok)
|
|
33
33
|
return null
|
|
34
34
|
const id = await res.text()
|
|
@@ -85,7 +85,10 @@ async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
|
|
|
85
85
|
replace.clear = () => allLogs.length = 0
|
|
86
86
|
const vmConsole = Object.assign({}, console, replace)
|
|
87
87
|
try {
|
|
88
|
-
const safeJS = `return async (console) => {
|
|
88
|
+
const safeJS = `return async (console) => {
|
|
89
|
+
window.console = console
|
|
90
|
+
${sanitizeJS(code)}
|
|
91
|
+
}`
|
|
89
92
|
// eslint-disable-next-line no-new-func
|
|
90
93
|
await (new Function(safeJS)())(vmConsole)
|
|
91
94
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/// <reference types="unplugin-icons/types/vue3" />
|
|
2
|
+
import type { ComputedRef } from 'vue'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import type { ContextMenuItem } from '@slidev/types'
|
|
5
|
+
import { useNav } from '../composables/useNav'
|
|
6
|
+
import { useDrawings } from '../composables/useDrawings'
|
|
7
|
+
import { fullscreen, showEditor, toggleOverview } from '../state'
|
|
8
|
+
import setups from '#slidev/setups/context-menu'
|
|
9
|
+
|
|
10
|
+
import IconArrowLeft from '~icons/carbon/arrow-left'
|
|
11
|
+
import IconArrowRight from '~icons/carbon/arrow-right'
|
|
12
|
+
import IconArrowUp from '~icons/carbon/arrow-up'
|
|
13
|
+
import IconArrowDown from '~icons/carbon/arrow-down'
|
|
14
|
+
import IconPen from '~icons/carbon/pen'
|
|
15
|
+
import IconTextNotationToggle from '~icons/carbon/text-annotation-toggle'
|
|
16
|
+
import IconApps from '~icons/carbon/apps'
|
|
17
|
+
import IconPresentationFile from '~icons/carbon/presentation-file'
|
|
18
|
+
import IconUserSpeaker from '~icons/carbon/user-speaker'
|
|
19
|
+
import IconMaximize from '~icons/carbon/maximize'
|
|
20
|
+
import IconMinimize from '~icons/carbon/minimize'
|
|
21
|
+
|
|
22
|
+
let items: ComputedRef<ContextMenuItem[]> | undefined
|
|
23
|
+
|
|
24
|
+
export default () => {
|
|
25
|
+
if (items)
|
|
26
|
+
return items
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
next,
|
|
30
|
+
nextSlide,
|
|
31
|
+
prev,
|
|
32
|
+
prevSlide,
|
|
33
|
+
hasNext,
|
|
34
|
+
hasPrev,
|
|
35
|
+
currentPage,
|
|
36
|
+
total,
|
|
37
|
+
isPresenter,
|
|
38
|
+
enterPresenter,
|
|
39
|
+
exitPresenter,
|
|
40
|
+
isEmbedded,
|
|
41
|
+
isPresenterAvailable,
|
|
42
|
+
} = useNav()
|
|
43
|
+
const { drawingEnabled } = useDrawings()
|
|
44
|
+
const {
|
|
45
|
+
isFullscreen,
|
|
46
|
+
toggle: toggleFullscreen,
|
|
47
|
+
} = fullscreen
|
|
48
|
+
|
|
49
|
+
return items = setups.reduce(
|
|
50
|
+
(items, fn) => fn(items),
|
|
51
|
+
computed(() => [
|
|
52
|
+
{
|
|
53
|
+
small: true,
|
|
54
|
+
icon: IconArrowLeft,
|
|
55
|
+
label: 'Previous Click',
|
|
56
|
+
action: prev,
|
|
57
|
+
disabled: !hasPrev.value,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
small: true,
|
|
61
|
+
icon: IconArrowRight,
|
|
62
|
+
label: 'Next Click',
|
|
63
|
+
action: next,
|
|
64
|
+
disabled: !hasNext.value,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
small: true,
|
|
68
|
+
icon: IconArrowUp,
|
|
69
|
+
label: 'Previous Slide',
|
|
70
|
+
action: prevSlide,
|
|
71
|
+
disabled: currentPage.value <= 1,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
small: true,
|
|
75
|
+
icon: IconArrowDown,
|
|
76
|
+
label: 'Next Slide',
|
|
77
|
+
action: nextSlide,
|
|
78
|
+
disabled: currentPage.value >= total.value,
|
|
79
|
+
},
|
|
80
|
+
'separator',
|
|
81
|
+
{
|
|
82
|
+
icon: IconTextNotationToggle,
|
|
83
|
+
label: showEditor.value ? 'Hide editor' : 'Show editor',
|
|
84
|
+
action: () => (showEditor.value = !showEditor.value),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
icon: IconPen,
|
|
88
|
+
label: drawingEnabled.value ? 'Hide drawing toolbar' : 'Show drawing toolbar',
|
|
89
|
+
action: () => (drawingEnabled.value = !drawingEnabled.value),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
icon: IconApps,
|
|
93
|
+
label: 'Show slide overview',
|
|
94
|
+
action: toggleOverview,
|
|
95
|
+
},
|
|
96
|
+
isPresenter.value && {
|
|
97
|
+
icon: IconPresentationFile,
|
|
98
|
+
label: 'Exit Presenter Mode',
|
|
99
|
+
action: exitPresenter,
|
|
100
|
+
},
|
|
101
|
+
__SLIDEV_FEATURE_PRESENTER__ && isPresenterAvailable.value && {
|
|
102
|
+
icon: IconUserSpeaker,
|
|
103
|
+
label: 'Enter Presenter Mode',
|
|
104
|
+
action: enterPresenter,
|
|
105
|
+
},
|
|
106
|
+
!isEmbedded.value && {
|
|
107
|
+
icon: isFullscreen.value ? IconMinimize : IconMaximize,
|
|
108
|
+
label: isFullscreen.value ? 'Close fullscreen' : 'Enter fullscreen',
|
|
109
|
+
action: toggleFullscreen,
|
|
110
|
+
},
|
|
111
|
+
].filter(Boolean) as ContextMenuItem[]),
|
|
112
|
+
)
|
|
113
|
+
}
|
package/setup/main.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { createVClickDirectives } from '../modules/v-click'
|
|
|
9
9
|
import { createVMarkDirective } from '../modules/v-mark'
|
|
10
10
|
import { createVDragDirective } from '../modules/v-drag'
|
|
11
11
|
import { createVMotionDirectives } from '../modules/v-motion'
|
|
12
|
-
import
|
|
12
|
+
import setupRoutes from '../setup/routes'
|
|
13
13
|
import setups from '#slidev/setups/main'
|
|
14
14
|
|
|
15
15
|
import '#slidev/styles'
|
|
@@ -28,7 +28,7 @@ export default async function setupMain(app: App) {
|
|
|
28
28
|
history: __SLIDEV_HASH_ROUTE__
|
|
29
29
|
? createWebHashHistory(import.meta.env.BASE_URL)
|
|
30
30
|
: createWebHistory(import.meta.env.BASE_URL),
|
|
31
|
-
routes,
|
|
31
|
+
routes: setupRoutes(),
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
app.use(router)
|
package/setup/monaco.ts
CHANGED
|
@@ -66,9 +66,6 @@ const setup = createSingletonPromise(async () => {
|
|
|
66
66
|
module: monaco.languages.typescript.ModuleKind.ESNext,
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
// Load types from server
|
|
70
|
-
import('#slidev/monaco-types')
|
|
71
|
-
|
|
72
69
|
const ata = configs.monacoTypesSource === 'cdn'
|
|
73
70
|
? setupTypeAcquisition({
|
|
74
71
|
projectName: 'TypeScript Playground',
|