@slidev/client 0.48.8 → 0.48.9

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.
@@ -45,43 +45,45 @@ onMounted(() => {
45
45
  // Calculate the step and rangeStr based on the current click count
46
46
  const clickCount = clicks.current - start
47
47
  let step = steps.length - 1
48
- let _currentClickSum = 0
48
+ let currentClickSum = 0
49
49
  let rangeStr = 'all'
50
50
  for (let i = 0; i < ranges.value.length; i++) {
51
51
  const current = ranges.value[i]
52
- if (clickCount < _currentClickSum + current.length - 1) {
52
+ if (clickCount < currentClickSum + current.length - 1) {
53
53
  step = i
54
- rangeStr = current[clickCount - _currentClickSum + 1]
54
+ rangeStr = current[clickCount - currentClickSum + 1]
55
55
  break
56
56
  }
57
- _currentClickSum += current.length || 1
57
+ currentClickSum += current.length || 1
58
58
  }
59
59
  stepIndex.value = step
60
60
 
61
- const pre = container.value?.querySelector('.shiki') as HTMLElement
62
- if (!pre)
63
- return
61
+ setTimeout(() => {
62
+ const pre = container.value?.querySelector('.shiki') as HTMLElement
63
+ if (!pre)
64
+ return
64
65
 
65
- const children = (Array.from(pre.children) as HTMLElement[])
66
- .slice(1) // Remove the first anchor
67
- .filter(i => !i.className.includes('shiki-magic-move-leave')) // Filter the leaving elements
66
+ const children = (Array.from(pre.children) as HTMLElement[])
67
+ .slice(1) // Remove the first anchor
68
+ .filter(i => !i.className.includes('shiki-magic-move-leave')) // Filter the leaving elements
68
69
 
69
- // Group to lines between `<br>`
70
- const lines = children.reduce((acc, el) => {
71
- if (el.tagName === 'BR')
72
- acc.push([])
73
- else
74
- acc[acc.length - 1].push(el)
75
- return acc
76
- }, [[]] as HTMLElement[][])
70
+ // Group to lines between `<br>`
71
+ const lines = children.reduce((acc, el) => {
72
+ if (el.tagName === 'BR')
73
+ acc.push([])
74
+ else
75
+ acc[acc.length - 1].push(el)
76
+ return acc
77
+ }, [[]] as HTMLElement[][])
77
78
 
78
- // Update highlight range
79
- updateCodeHighlightRange(
80
- rangeStr,
81
- lines.length,
82
- 1,
83
- no => lines[no],
84
- )
79
+ // Update highlight range
80
+ updateCodeHighlightRange(
81
+ rangeStr,
82
+ lines.length,
83
+ 1,
84
+ no => lines[no],
85
+ )
86
+ })
85
87
  },
86
88
  { immediate: true },
87
89
  )
@@ -104,3 +106,10 @@ onMounted(() => {
104
106
  />
105
107
  </div>
106
108
  </template>
109
+
110
+ <style>
111
+ .slidev-code-magic-move .shiki-magic-move-enter-from,
112
+ .slidev-code-magic-move .shiki-magic-move-leave-to {
113
+ opacity: 0;
114
+ }
115
+ </style>
package/constants.ts CHANGED
@@ -13,6 +13,7 @@ export const injectionRenderContext = '$$slidev-render-context' as unknown as In
13
13
  export const injectionActive = '$$slidev-active' as unknown as InjectionKey<Ref<boolean>>
14
14
  export const injectionFrontmatter = '$$slidev-fontmatter' as unknown as InjectionKey<Record<string, any>>
15
15
  export const injectionSlideZoom = '$$slidev-slide-zoom' as unknown as InjectionKey<ComputedRef<number>>
16
+ export const injectionClickVisibility = '$$slidev-click-visibility' as unknown as InjectionKey<ComputedRef<true | 'before' | 'after'>>
16
17
 
17
18
  export const CLASS_VCLICK_TARGET = 'slidev-vclick-target'
18
19
  export const CLASS_VCLICK_HIDDEN = 'slidev-vclick-hidden'
@@ -76,7 +76,6 @@ function onAfterLeave() {
76
76
  :class="getSlideClass(route)"
77
77
  :route="route"
78
78
  :render-context="renderContext"
79
- class="overflow-hidden"
80
79
  />
81
80
  </div>
82
81
  </component>
@@ -1,5 +1,5 @@
1
1
  import type { ResolvedClicksInfo } from '@slidev/types'
2
- import type { App, DirectiveBinding, InjectionKey } from 'vue'
2
+ import type { App, DirectiveBinding } from 'vue'
3
3
  import { computed, watchEffect } from 'vue'
4
4
  import {
5
5
  CLASS_VCLICK_CURRENT,
@@ -8,14 +8,12 @@ import {
8
8
  CLASS_VCLICK_HIDDEN_EXP,
9
9
  CLASS_VCLICK_PRIOR,
10
10
  CLASS_VCLICK_TARGET,
11
+ injectionClickVisibility,
11
12
  injectionClicksContext,
12
13
  } from '../constants'
14
+ import { directiveInject, directiveProvide } from '../utils'
13
15
 
14
- export type VClickValue = string | [string | number, string | number] | boolean
15
-
16
- export function dirInject<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, defaultValue?: T): T | undefined {
17
- return (dir.instance?.$ as any).provides[key as any] ?? defaultValue
18
- }
16
+ export type VClickValue = undefined | string | number | [string | number, string | number] | boolean
19
17
 
20
18
  export function createVClickDirectives() {
21
19
  return {
@@ -25,7 +23,7 @@ export function createVClickDirectives() {
25
23
  name: 'v-click',
26
24
 
27
25
  mounted(el, dir) {
28
- const resolved = resolveClick(el, dir, dir.value)
26
+ const resolved = resolveClick(el, dir, dir.value, true)
29
27
  if (resolved == null)
30
28
  return
31
29
 
@@ -37,7 +35,8 @@ export function createVClickDirectives() {
37
35
  if (clicks[1] != null)
38
36
  el.dataset.slidevClicksEnd = String(clicks[1])
39
37
 
40
- watchEffect(() => {
38
+ // @ts-expect-error extra prop
39
+ el.watchStopHandle = watchEffect(() => {
41
40
  const active = resolved.isActive.value
42
41
  const current = resolved.isCurrent.value
43
42
  const prior = active && !current
@@ -62,13 +61,14 @@ export function createVClickDirectives() {
62
61
  name: 'v-after',
63
62
 
64
63
  mounted(el, dir) {
65
- const resolved = resolveClick(el, dir, dir.value, true)
64
+ const resolved = resolveClick(el, dir, dir.value, true, true)
66
65
  if (resolved == null)
67
66
  return
68
67
 
69
68
  el.classList.toggle(CLASS_VCLICK_TARGET, true)
70
69
 
71
- watchEffect(() => {
70
+ // @ts-expect-error extra prop
71
+ el.watchStopHandle = watchEffect(() => {
72
72
  const active = resolved.isActive.value
73
73
  const current = resolved.isCurrent.value
74
74
  const prior = active && !current
@@ -93,13 +93,14 @@ export function createVClickDirectives() {
93
93
  name: 'v-click-hide',
94
94
 
95
95
  mounted(el, dir) {
96
- const resolved = resolveClick(el, dir, dir.value, false, true)
96
+ const resolved = resolveClick(el, dir, dir.value, true, false, true)
97
97
  if (resolved == null)
98
98
  return
99
99
 
100
100
  el.classList.toggle(CLASS_VCLICK_TARGET, true)
101
101
 
102
- watchEffect(() => {
102
+ // @ts-expect-error extra prop
103
+ el.watchStopHandle = watchEffect(() => {
103
104
  const active = resolved.isActive.value
104
105
  const current = resolved.isCurrent.value
105
106
  const prior = active && !current
@@ -117,20 +118,20 @@ export function createVClickDirectives() {
117
118
  }
118
119
  }
119
120
 
120
- function isActive(thisClick: number | [number, number], clicks: number) {
121
+ function isClickActive(thisClick: number | [number, number], clicks: number) {
121
122
  return Array.isArray(thisClick)
122
123
  ? thisClick[0] <= clicks && clicks < thisClick[1]
123
124
  : thisClick <= clicks
124
125
  }
125
126
 
126
- function isCurrent(thisClick: number | [number, number], clicks: number) {
127
+ function isClickCurrent(thisClick: number | [number, number], clicks: number) {
127
128
  return Array.isArray(thisClick)
128
129
  ? thisClick[0] === clicks
129
130
  : thisClick === clicks
130
131
  }
131
132
 
132
- export function resolveClick(el: Element, dir: DirectiveBinding<any>, value: VClickValue, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
133
- const ctx = dirInject(dir, injectionClicksContext)?.value
133
+ export function resolveClick(el: Element | string, dir: DirectiveBinding<any>, value: VClickValue, provideVisibility = false, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
134
+ const ctx = directiveInject(dir, injectionClicksContext)?.value
134
135
 
135
136
  if (!el || !ctx)
136
137
  return null
@@ -152,29 +153,47 @@ export function resolveClick(el: Element, dir: DirectiveBinding<any>, value: VCl
152
153
  if (Array.isArray(value)) {
153
154
  // range (absolute)
154
155
  delta = 0
155
- thisClick = value as [number, number]
156
+ thisClick = [+value[0], +value[1]]
156
157
  maxClick = +value[1]
157
158
  }
158
159
  else {
159
160
  ({ start: thisClick, end: maxClick, delta } = ctx.resolve(value))
160
161
  }
161
162
 
163
+ const isActive = computed(() => isClickActive(thisClick, ctx.current))
164
+ const isCurrent = computed(() => isClickCurrent(thisClick, ctx.current))
165
+ const isShown = computed(() => flagHide ? !isActive.value : isActive.value)
166
+
162
167
  const resolved: ResolvedClicksInfo = {
163
168
  max: maxClick,
164
169
  clicks: thisClick,
165
170
  delta,
166
- isActive: computed(() => isActive(thisClick, ctx.current)),
167
- isCurrent: computed(() => isCurrent(thisClick, ctx.current)),
168
- isShown: computed(() => flagHide ? !isActive(thisClick, ctx.current) : isActive(thisClick, ctx.current)),
171
+ isActive,
172
+ isCurrent,
173
+ isShown,
169
174
  flagFade,
170
175
  flagHide,
171
176
  }
172
177
  ctx.register(el, resolved)
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
+
173
190
  return resolved
174
191
  }
175
192
 
176
193
  function unmounted(el: HTMLElement, dir: DirectiveBinding<any>) {
177
194
  el.classList.toggle(CLASS_VCLICK_TARGET, false)
178
- const ctx = dirInject(dir, injectionClicksContext)?.value
195
+ const ctx = directiveInject(dir, injectionClicksContext)?.value
179
196
  ctx?.unregister(el)
197
+ // @ts-expect-error extra prop
198
+ el.watchStopHandle?.()
180
199
  }
package/modules/v-mark.ts CHANGED
@@ -121,7 +121,8 @@ export function createVMarkDirective() {
121
121
  return
122
122
  }
123
123
 
124
- watchEffect(() => {
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.48.8",
4
+ "version": "0.48.9",
5
5
  "description": "Presentation slides for developers",
6
6
  "author": "antfu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -32,12 +32,12 @@
32
32
  "@iconify-json/carbon": "^1.1.31",
33
33
  "@iconify-json/ph": "^1.1.11",
34
34
  "@iconify-json/svg-spinners": "^1.1.2",
35
- "@shikijs/monaco": "^1.2.1",
36
- "@shikijs/vitepress-twoslash": "^1.2.1",
35
+ "@shikijs/monaco": "^1.2.3",
36
+ "@shikijs/vitepress-twoslash": "^1.2.3",
37
37
  "@slidev/rough-notation": "^0.1.0",
38
38
  "@typescript/ata": "^0.9.4",
39
- "@unhead/vue": "^1.9.3",
40
- "@unocss/reset": "^0.58.8",
39
+ "@unhead/vue": "^1.9.4",
40
+ "@unocss/reset": "^0.58.9",
41
41
  "@vueuse/core": "^10.9.0",
42
42
  "@vueuse/math": "^10.9.0",
43
43
  "@vueuse/motion": "^2.1.0",
@@ -53,15 +53,15 @@
53
53
  "monaco-editor": "^0.47.0",
54
54
  "prettier": "^3.2.5",
55
55
  "recordrtc": "^5.6.2",
56
- "shiki": "^1.2.1",
57
- "shiki-magic-move": "^0.3.4",
56
+ "shiki": "^1.2.3",
57
+ "shiki-magic-move": "^0.3.5",
58
58
  "typescript": "^5.4.3",
59
- "unocss": "^0.58.8",
59
+ "unocss": "^0.58.9",
60
60
  "vue": "^3.4.21",
61
61
  "vue-demi": "^0.14.7",
62
62
  "vue-router": "^4.3.0",
63
- "@slidev/parser": "0.48.8",
64
- "@slidev/types": "0.48.8"
63
+ "@slidev/parser": "0.48.9",
64
+ "@slidev/types": "0.48.9"
65
65
  },
66
66
  "devDependencies": {
67
67
  "vite": "^5.2.7"
@@ -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: runJavaScript,
10
- js: runJavaScript,
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
- export async function runJavaScript(code: string): Promise<CodeRunnerOutputs> {
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
- const { transpile } = tsModule ??= await import('typescript')
163
- code = transpile(code, {
164
- module: tsModule.ModuleKind.ESNext,
165
- target: tsModule.ScriptTarget.ES2022,
166
- })
167
- return await context.run(code, 'javascript')
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,7 @@ 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 { createVMotionDirectives } from '../modules/v-motion'
11
11
  import { routes } from '../routes'
12
12
  import setups from '#slidev/setups/main'
13
13
 
@@ -34,7 +34,7 @@ export default async function setupMain(app: App) {
34
34
  app.use(createHead())
35
35
  app.use(createVClickDirectives())
36
36
  app.use(createVMarkDirective())
37
- app.use(MotionPlugin)
37
+ app.use(createVMotionDirectives())
38
38
  app.use(TwoSlashFloatingVue as any, { container: '#twoslash-container' })
39
39
 
40
40
  const context: AppContext = {
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
+ }