@slidev/client 0.48.8 → 0.49.0-beta.1
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/ShikiMagicMove.vue +34 -25
- package/builtin/SlidevVideo.vue +54 -62
- package/builtin/VDrag.vue +27 -0
- package/composables/useDragElements.ts +282 -0
- package/composables/useSlideBounds.ts +30 -0
- package/composables/useSlideInfo.ts +12 -5
- package/constants.ts +3 -0
- package/internals/DragControl.vue +396 -0
- package/internals/SlideContainer.vue +10 -8
- package/internals/SlideWrapper.vue +15 -6
- package/internals/SlidesShow.vue +10 -18
- package/modules/v-click.ts +40 -21
- package/modules/v-drag.ts +44 -0
- package/modules/v-mark.ts +7 -1
- package/modules/v-motion.ts +120 -0
- package/package.json +13 -13
- package/setup/code-runners.ts +94 -10
- package/setup/main.ts +4 -2
- package/setup/shortcuts.ts +7 -5
- package/state/index.ts +4 -1
- package/utils.ts +16 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { App } from 'vue'
|
|
2
|
+
import { watch } from 'vue'
|
|
3
|
+
import type { DragElementState } from '../composables/useDragElements'
|
|
4
|
+
import { useDragElement } from '../composables/useDragElements'
|
|
5
|
+
|
|
6
|
+
export function createVDragDirective() {
|
|
7
|
+
return {
|
|
8
|
+
install(app: App) {
|
|
9
|
+
app.directive<HTMLElement & { draggingState: DragElementState }>('drag', {
|
|
10
|
+
// @ts-expect-error extra prop
|
|
11
|
+
name: 'v-drag',
|
|
12
|
+
|
|
13
|
+
created(el, binding, vnode) {
|
|
14
|
+
const state = useDragElement(binding, binding.value, vnode.props?.markdownSource)
|
|
15
|
+
if (vnode.props) {
|
|
16
|
+
vnode.props = { ...vnode.props }
|
|
17
|
+
delete vnode.props.markdownSource
|
|
18
|
+
}
|
|
19
|
+
state.container.value = el
|
|
20
|
+
el.draggingState = state
|
|
21
|
+
el.dataset.dragId = state.id
|
|
22
|
+
state.watchStopHandles.push(
|
|
23
|
+
watch(state.containerStyle, (style) => {
|
|
24
|
+
for (const [k, v] of Object.entries(style)) {
|
|
25
|
+
if (v)
|
|
26
|
+
el.style[k as any] = v
|
|
27
|
+
}
|
|
28
|
+
}, { immediate: true }),
|
|
29
|
+
)
|
|
30
|
+
el.addEventListener('dblclick', state.startDragging)
|
|
31
|
+
},
|
|
32
|
+
mounted(el) {
|
|
33
|
+
el.draggingState.mounted()
|
|
34
|
+
},
|
|
35
|
+
unmounted(el) {
|
|
36
|
+
const state = el.draggingState
|
|
37
|
+
state.unmounted()
|
|
38
|
+
el.removeEventListener('dblclick', state.startDragging)
|
|
39
|
+
state.watchStopHandles.forEach(fn => fn())
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
}
|
package/modules/v-mark.ts
CHANGED
|
@@ -121,7 +121,8 @@ export function createVMarkDirective() {
|
|
|
121
121
|
return
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
// @ts-expect-error extra prop
|
|
125
|
+
el.watchStopHandle = watchEffect(() => {
|
|
125
126
|
let shouldShow: boolean | undefined
|
|
126
127
|
|
|
127
128
|
if (options.value.class)
|
|
@@ -147,6 +148,11 @@ export function createVMarkDirective() {
|
|
|
147
148
|
annotation.hide()
|
|
148
149
|
})
|
|
149
150
|
},
|
|
151
|
+
|
|
152
|
+
unmounted: (el) => {
|
|
153
|
+
// @ts-expect-error extra prop
|
|
154
|
+
el.watchStopHandle?.()
|
|
155
|
+
},
|
|
150
156
|
})
|
|
151
157
|
},
|
|
152
158
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { App, ObjectDirective } from 'vue'
|
|
2
|
+
import { watch } from 'vue'
|
|
3
|
+
import { MotionDirective } from '@vueuse/motion'
|
|
4
|
+
import type { ResolvedClicksInfo } from '@slidev/types'
|
|
5
|
+
import { injectionClickVisibility, injectionClicksContext, injectionCurrentPage, injectionRenderContext } from '../constants'
|
|
6
|
+
import { useNav } from '../composables/useNav'
|
|
7
|
+
import { makeId } from '../logic/utils'
|
|
8
|
+
import { directiveInject } from '../utils'
|
|
9
|
+
import type { VClickValue } from './v-click'
|
|
10
|
+
import { resolveClick } from './v-click'
|
|
11
|
+
|
|
12
|
+
export type MotionDirectiveValue = undefined | VClickValue | {
|
|
13
|
+
key?: string
|
|
14
|
+
at?: VClickValue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createVMotionDirectives() {
|
|
18
|
+
return {
|
|
19
|
+
install(app: App) {
|
|
20
|
+
const original = MotionDirective() as ObjectDirective
|
|
21
|
+
app.directive<HTMLElement | SVGElement, string>('motion', {
|
|
22
|
+
// @ts-expect-error extra prop
|
|
23
|
+
name: 'v-motion',
|
|
24
|
+
mounted(el, binding, node, prevNode) {
|
|
25
|
+
const props = node.props = { ...node.props }
|
|
26
|
+
|
|
27
|
+
const variantInitial = { ...props.initial, ...props.variants?.['slidev-initial'] }
|
|
28
|
+
const variantEnter = { ...props.enter, ...props.variants?.['slidev-enter'] }
|
|
29
|
+
const variantLeave = { ...props.leave, ...props.variants?.['slidev-leave'] }
|
|
30
|
+
delete props.initial
|
|
31
|
+
delete props.enter
|
|
32
|
+
delete props.leave
|
|
33
|
+
|
|
34
|
+
const idPrefix = `${makeId()}-`
|
|
35
|
+
const clicks: {
|
|
36
|
+
id: string
|
|
37
|
+
at: number | [number, number]
|
|
38
|
+
variant: Record<string, unknown>
|
|
39
|
+
resolved: ResolvedClicksInfo | null
|
|
40
|
+
}[] = []
|
|
41
|
+
|
|
42
|
+
for (const k of Object.keys(props)) {
|
|
43
|
+
if (k.startsWith('click-')) {
|
|
44
|
+
const s = k.slice(6)
|
|
45
|
+
const at = s.includes('-') ? s.split('-').map(Number) as [number, number] : +s
|
|
46
|
+
const id = idPrefix + s
|
|
47
|
+
clicks.push({
|
|
48
|
+
id,
|
|
49
|
+
at,
|
|
50
|
+
variant: { ...props[k] },
|
|
51
|
+
resolved: resolveClick(id, binding, at),
|
|
52
|
+
})
|
|
53
|
+
delete props[k]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
clicks.sort((a, b) => (Array.isArray(a.at) ? a.at[0] : a.at) - (Array.isArray(b.at) ? b.at[0] : b.at))
|
|
58
|
+
|
|
59
|
+
original.created!(el, binding, node, prevNode)
|
|
60
|
+
original.mounted!(el, binding, node, prevNode)
|
|
61
|
+
|
|
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
|
+
// @ts-expect-error extra prop
|
|
68
|
+
const motion = el.motionInstance
|
|
69
|
+
motion.clickIds = clicks.map(i => i.id)
|
|
70
|
+
motion.set(variantInitial)
|
|
71
|
+
motion.watchStopHandle = watch(
|
|
72
|
+
[thisPage, currentPage, currentClicks].filter(Boolean),
|
|
73
|
+
() => {
|
|
74
|
+
const visibility = clickVisibility?.value ?? true
|
|
75
|
+
if (!clicksContext?.value || !['slide', 'presenter'].includes(renderContext?.value ?? '')) {
|
|
76
|
+
const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
|
|
77
|
+
for (const { variant } of clicks)
|
|
78
|
+
Object.assign(mixedVariant, variant)
|
|
79
|
+
|
|
80
|
+
motion.set(mixedVariant)
|
|
81
|
+
}
|
|
82
|
+
else if (isPrintMode.value || thisPage?.value === currentPage.value) {
|
|
83
|
+
if (visibility === true) {
|
|
84
|
+
const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
|
|
85
|
+
for (const { variant, resolved: resolvedClick } of clicks) {
|
|
86
|
+
if (!resolvedClick || resolvedClick.isActive.value)
|
|
87
|
+
Object.assign(mixedVariant, variant)
|
|
88
|
+
}
|
|
89
|
+
if (isPrintMode.value)
|
|
90
|
+
motion.set(mixedVariant) // print with clicks
|
|
91
|
+
else
|
|
92
|
+
motion.apply(mixedVariant)
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
motion.apply(visibility === 'before' ? variantInitial : variantLeave)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
motion.apply((thisPage?.value ?? -1) > currentPage.value ? variantInitial : variantLeave)
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
immediate: true,
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
},
|
|
107
|
+
unmounted(el, dir) {
|
|
108
|
+
if (!directiveInject(dir, injectionClicksContext)?.value)
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
const ctx = directiveInject(dir, injectionClicksContext)?.value
|
|
112
|
+
// @ts-expect-error extra prop
|
|
113
|
+
const motion = el.motionInstance
|
|
114
|
+
motion.clickIds.map((id: string) => ctx?.unregister(id))
|
|
115
|
+
motion.watchStopHandle()
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.49.0-beta.1",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@antfu/utils": "^0.7.7",
|
|
32
32
|
"@iconify-json/carbon": "^1.1.31",
|
|
33
|
-
"@iconify-json/ph": "^1.1.
|
|
33
|
+
"@iconify-json/ph": "^1.1.12",
|
|
34
34
|
"@iconify-json/svg-spinners": "^1.1.2",
|
|
35
|
-
"@shikijs/monaco": "^1.
|
|
36
|
-
"@shikijs/vitepress-twoslash": "^1.
|
|
35
|
+
"@shikijs/monaco": "^1.3.0",
|
|
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.
|
|
39
|
+
"@unhead/vue": "^1.9.4",
|
|
40
|
+
"@unocss/reset": "^0.59.0",
|
|
41
41
|
"@vueuse/core": "^10.9.0",
|
|
42
42
|
"@vueuse/math": "^10.9.0",
|
|
43
43
|
"@vueuse/motion": "^2.1.0",
|
|
@@ -53,17 +53,17 @@
|
|
|
53
53
|
"monaco-editor": "^0.47.0",
|
|
54
54
|
"prettier": "^3.2.5",
|
|
55
55
|
"recordrtc": "^5.6.2",
|
|
56
|
-
"shiki": "^1.
|
|
57
|
-
"shiki-magic-move": "^0.3.
|
|
58
|
-
"typescript": "^5.4.
|
|
59
|
-
"unocss": "^0.
|
|
56
|
+
"shiki": "^1.3.0",
|
|
57
|
+
"shiki-magic-move": "^0.3.5",
|
|
58
|
+
"typescript": "^5.4.4",
|
|
59
|
+
"unocss": "^0.59.0",
|
|
60
60
|
"vue": "^3.4.21",
|
|
61
61
|
"vue-demi": "^0.14.7",
|
|
62
62
|
"vue-router": "^4.3.0",
|
|
63
|
-
"@slidev/parser": "0.
|
|
64
|
-
"@slidev/types": "0.
|
|
63
|
+
"@slidev/parser": "0.49.0-beta.1",
|
|
64
|
+
"@slidev/types": "0.49.0-beta.1"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"vite": "^5.2.
|
|
67
|
+
"vite": "^5.2.8"
|
|
68
68
|
}
|
|
69
69
|
}
|
package/setup/code-runners.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { createSingletonPromise } from '@antfu/utils'
|
|
1
|
+
import { createSingletonPromise, ensurePrefix, slash } from '@antfu/utils'
|
|
2
2
|
import type { CodeRunner, CodeRunnerContext, CodeRunnerOutput, CodeRunnerOutputText, CodeRunnerOutputs } from '@slidev/types'
|
|
3
3
|
import type { CodeToHastOptions } from 'shiki'
|
|
4
|
+
import type ts from 'typescript'
|
|
4
5
|
import { isDark } from '../logic/dark'
|
|
5
6
|
import setups from '#slidev/setups/code-runners'
|
|
6
7
|
|
|
7
8
|
export default createSingletonPromise(async () => {
|
|
8
9
|
const runners: Record<string, CodeRunner> = {
|
|
9
|
-
javascript:
|
|
10
|
-
js:
|
|
10
|
+
javascript: runTypeScript,
|
|
11
|
+
js: runTypeScript,
|
|
11
12
|
typescript: runTypeScript,
|
|
12
13
|
ts: runTypeScript,
|
|
13
14
|
}
|
|
@@ -24,6 +25,18 @@ export default createSingletonPromise(async () => {
|
|
|
24
25
|
...options,
|
|
25
26
|
})
|
|
26
27
|
|
|
28
|
+
const resolveId = async (specifier: string) => {
|
|
29
|
+
if (!/^(@[^\/:]+?\/)?[^\/:]+$/.test(specifier))
|
|
30
|
+
return specifier
|
|
31
|
+
const res = await fetch(`/@slidev/resolve-id/${specifier}`)
|
|
32
|
+
if (!res.ok)
|
|
33
|
+
return null
|
|
34
|
+
const id = await res.text()
|
|
35
|
+
if (!id)
|
|
36
|
+
return null
|
|
37
|
+
return `/@fs${ensurePrefix('/', slash(id))}`
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
const run = async (code: string, lang: string, options: Record<string, unknown>): Promise<CodeRunnerOutputs> => {
|
|
28
41
|
try {
|
|
29
42
|
const runner = runners[lang]
|
|
@@ -34,6 +47,7 @@ export default createSingletonPromise(async () => {
|
|
|
34
47
|
{
|
|
35
48
|
options,
|
|
36
49
|
highlight,
|
|
50
|
+
resolveId,
|
|
37
51
|
run: async (code, lang) => {
|
|
38
52
|
return await run(code, lang, options)
|
|
39
53
|
},
|
|
@@ -60,7 +74,7 @@ export default createSingletonPromise(async () => {
|
|
|
60
74
|
})
|
|
61
75
|
|
|
62
76
|
// Ported from https://github.com/microsoft/TypeScript-Website/blob/v2/packages/playground/src/sidebar/runtime.ts
|
|
63
|
-
|
|
77
|
+
async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
|
|
64
78
|
const allLogs: CodeRunnerOutput[] = []
|
|
65
79
|
|
|
66
80
|
const replace = {} as any
|
|
@@ -159,10 +173,80 @@ export async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
|
|
|
159
173
|
let tsModule: typeof import('typescript') | undefined
|
|
160
174
|
|
|
161
175
|
export async function runTypeScript(code: string, context: CodeRunnerContext) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
176
|
+
tsModule ??= await import('typescript')
|
|
177
|
+
|
|
178
|
+
code = tsModule.transpileModule(code, {
|
|
179
|
+
compilerOptions: {
|
|
180
|
+
module: tsModule.ModuleKind.ESNext,
|
|
181
|
+
target: tsModule.ScriptTarget.ES2022,
|
|
182
|
+
},
|
|
183
|
+
transformers: {
|
|
184
|
+
after: [transformImports],
|
|
185
|
+
},
|
|
186
|
+
}).outputText
|
|
187
|
+
|
|
188
|
+
const importRegex = /import\s*\(\s*(['"])(.+?)['"]\s*\)/g
|
|
189
|
+
const idMap: Record<string, string> = {}
|
|
190
|
+
for (const [,,specifier] of code.matchAll(importRegex)!)
|
|
191
|
+
idMap[specifier] = await context.resolveId(specifier) ?? specifier
|
|
192
|
+
code = code.replace(importRegex, (_full, quote, specifier) => `import(${quote}${idMap[specifier] ?? specifier}${quote})`)
|
|
193
|
+
|
|
194
|
+
return await runJavaScript(code)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Transform import statements to dynamic imports
|
|
199
|
+
*/
|
|
200
|
+
function transformImports(context: ts.TransformationContext): ts.Transformer<ts.SourceFile> {
|
|
201
|
+
const { factory } = context
|
|
202
|
+
const { isImportDeclaration, isNamedImports, NodeFlags } = tsModule!
|
|
203
|
+
return (sourceFile: ts.SourceFile) => {
|
|
204
|
+
const statements = [...sourceFile.statements]
|
|
205
|
+
for (let i = 0; i < statements.length; i++) {
|
|
206
|
+
const statement = statements[i]
|
|
207
|
+
if (!isImportDeclaration(statement))
|
|
208
|
+
continue
|
|
209
|
+
let bindingPattern: ts.ObjectBindingPattern | ts.Identifier
|
|
210
|
+
const namedBindings = statement.importClause?.namedBindings
|
|
211
|
+
const bindings: ts.BindingElement[] = []
|
|
212
|
+
if (statement.importClause?.name)
|
|
213
|
+
bindings.push(factory.createBindingElement(undefined, factory.createIdentifier('default'), statement.importClause.name))
|
|
214
|
+
if (namedBindings) {
|
|
215
|
+
if (isNamedImports(namedBindings)) {
|
|
216
|
+
for (const specifier of namedBindings.elements)
|
|
217
|
+
bindings.push(factory.createBindingElement(undefined, specifier.propertyName, specifier.name))
|
|
218
|
+
bindingPattern = factory.createObjectBindingPattern(bindings)
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
bindingPattern = factory.createIdentifier(namedBindings.name.text)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
bindingPattern = factory.createObjectBindingPattern(bindings)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const newStatement = factory.createVariableStatement(
|
|
229
|
+
undefined,
|
|
230
|
+
factory.createVariableDeclarationList(
|
|
231
|
+
[
|
|
232
|
+
factory.createVariableDeclaration(
|
|
233
|
+
bindingPattern,
|
|
234
|
+
undefined,
|
|
235
|
+
undefined,
|
|
236
|
+
factory.createAwaitExpression(
|
|
237
|
+
factory.createCallExpression(
|
|
238
|
+
factory.createIdentifier('import'),
|
|
239
|
+
undefined,
|
|
240
|
+
[statement.moduleSpecifier],
|
|
241
|
+
),
|
|
242
|
+
),
|
|
243
|
+
),
|
|
244
|
+
],
|
|
245
|
+
NodeFlags.Const,
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
statements[i] = newStatement
|
|
249
|
+
}
|
|
250
|
+
return factory.updateSourceFile(sourceFile, statements)
|
|
251
|
+
}
|
|
168
252
|
}
|
package/setup/main.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AppContext } from '@slidev/types'
|
|
2
|
-
import { MotionPlugin } from '@vueuse/motion'
|
|
3
2
|
import TwoSlashFloatingVue from '@shikijs/vitepress-twoslash/client'
|
|
4
3
|
import type { App } from 'vue'
|
|
5
4
|
import { nextTick } from 'vue'
|
|
@@ -8,6 +7,8 @@ import { createHead } from '@unhead/vue'
|
|
|
8
7
|
import { routeForceRefresh } from '../logic/route'
|
|
9
8
|
import { createVClickDirectives } from '../modules/v-click'
|
|
10
9
|
import { createVMarkDirective } from '../modules/v-mark'
|
|
10
|
+
import { createVDragDirective } from '../modules/v-drag'
|
|
11
|
+
import { createVMotionDirectives } from '../modules/v-motion'
|
|
11
12
|
import { routes } from '../routes'
|
|
12
13
|
import setups from '#slidev/setups/main'
|
|
13
14
|
|
|
@@ -34,7 +35,8 @@ export default async function setupMain(app: App) {
|
|
|
34
35
|
app.use(createHead())
|
|
35
36
|
app.use(createVClickDirectives())
|
|
36
37
|
app.use(createVMarkDirective())
|
|
37
|
-
app.use(
|
|
38
|
+
app.use(createVDragDirective())
|
|
39
|
+
app.use(createVMotionDirectives())
|
|
38
40
|
app.use(TwoSlashFloatingVue as any, { container: '#twoslash-container' })
|
|
39
41
|
|
|
40
42
|
const context: AppContext = {
|
package/setup/shortcuts.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { and, not, or } from '@vueuse/math'
|
|
|
2
2
|
import type { NavOperations, ShortcutOptions } from '@slidev/types'
|
|
3
3
|
import { downloadPDF } from '../utils'
|
|
4
4
|
import { toggleDark } from '../logic/dark'
|
|
5
|
-
import { magicKeys, showGotoDialog, showOverview, toggleOverview } from '../state'
|
|
5
|
+
import { activeDragElement, magicKeys, showGotoDialog, showOverview, toggleOverview } from '../state'
|
|
6
6
|
import { useNav } from '../composables/useNav'
|
|
7
7
|
import { useDrawings } from '../composables/useDrawings'
|
|
8
8
|
import { currentOverviewPage, downOverviewPage, nextOverviewPage, prevOverviewPage, upOverviewPage } from './../logic/overview'
|
|
@@ -29,15 +29,17 @@ export default function setupShortcuts() {
|
|
|
29
29
|
showGotoDialog: () => showGotoDialog.value = !showGotoDialog.value,
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const navViaArrowKeys = and(not(showOverview), not(activeDragElement))
|
|
33
|
+
|
|
32
34
|
let shortcuts: ShortcutOptions[] = [
|
|
33
35
|
{ name: 'next_space', key: and(space, not(shift)), fn: next, autoRepeat: true },
|
|
34
36
|
{ name: 'prev_space', key: and(space, shift), fn: prev, autoRepeat: true },
|
|
35
|
-
{ name: 'next_right', key: and(right, not(shift),
|
|
36
|
-
{ name: 'prev_left', key: and(left, not(shift),
|
|
37
|
+
{ name: 'next_right', key: and(right, not(shift), navViaArrowKeys), fn: next, autoRepeat: true },
|
|
38
|
+
{ name: 'prev_left', key: and(left, not(shift), navViaArrowKeys), fn: prev, autoRepeat: true },
|
|
37
39
|
{ name: 'next_page_key', key: 'pageDown', fn: next, autoRepeat: true },
|
|
38
40
|
{ name: 'prev_page_key', key: 'pageUp', fn: prev, autoRepeat: true },
|
|
39
|
-
{ name: 'next_down', key: and(down,
|
|
40
|
-
{ name: 'prev_up', key: and(up,
|
|
41
|
+
{ name: 'next_down', key: and(down, navViaArrowKeys), fn: nextSlide, autoRepeat: true },
|
|
42
|
+
{ name: 'prev_up', key: and(up, navViaArrowKeys), fn: () => prevSlide(false), autoRepeat: true },
|
|
41
43
|
{ name: 'next_shift', key: and(right, shift), fn: nextSlide, autoRepeat: true },
|
|
42
44
|
{ name: 'prev_shift', key: and(left, shift), fn: () => prevSlide(false), autoRepeat: true },
|
|
43
45
|
{ name: 'toggle_dark', key: and(d, not(drawingEnabled)), fn: toggleDark },
|
package/state/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { breakpointsTailwind, isClient, useActiveElement, useBreakpoints, useFullscreen, useLocalStorage, useMagicKeys, useToggle, useWindowSize } from '@vueuse/core'
|
|
2
|
-
import { computed, ref } from 'vue'
|
|
2
|
+
import { computed, ref, shallowRef } from 'vue'
|
|
3
3
|
import { slideAspect } from '../env'
|
|
4
|
+
import type { DragElementState } from '../composables/useDragElements'
|
|
4
5
|
|
|
5
6
|
export const showRecordingDialog = ref(false)
|
|
6
7
|
export const showInfoDialog = ref(false)
|
|
@@ -31,6 +32,8 @@ export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false,
|
|
|
31
32
|
export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 318, { listenToStorageChanges: false })
|
|
32
33
|
export const editorHeight = useLocalStorage('slidev-editor-height', isClient ? window.innerHeight * 0.4 : 300, { listenToStorageChanges: false })
|
|
33
34
|
|
|
35
|
+
export const activeDragElement = shallowRef<DragElementState | null>(null)
|
|
36
|
+
|
|
34
37
|
export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1, { listenToStorageChanges: false })
|
|
35
38
|
export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1, { listenToStorageChanges: false })
|
|
36
39
|
|
package/utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SlideRoute } from '@slidev/types'
|
|
2
|
+
import type { DirectiveBinding, InjectionKey } from 'vue'
|
|
2
3
|
import { configs } from './env'
|
|
3
4
|
|
|
4
5
|
export function getSlideClass(route?: SlideRoute, extra = '') {
|
|
@@ -22,3 +23,18 @@ export async function downloadPDF() {
|
|
|
22
23
|
`${configs.title}.pdf`,
|
|
23
24
|
)
|
|
24
25
|
}
|
|
26
|
+
|
|
27
|
+
export function directiveInject<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, defaultValue?: T): T | undefined {
|
|
28
|
+
return (dir.instance?.$ as any).provides[key as any] ?? defaultValue
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function directiveProvide<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, value?: T) {
|
|
32
|
+
const instance = dir.instance?.$ as any
|
|
33
|
+
if (instance) {
|
|
34
|
+
let provides = instance.provides
|
|
35
|
+
const parentProvides = instance.parent?.provides
|
|
36
|
+
if (provides === parentProvides)
|
|
37
|
+
provides = instance.provides = Object.create(parentProvides)
|
|
38
|
+
provides[key as any] = value
|
|
39
|
+
}
|
|
40
|
+
}
|