@slidev/client 0.48.0-beta.21 → 0.48.0-beta.23

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.
@@ -12,13 +12,12 @@ Learn more: https://sli.dev/guide/syntax.html#line-highlighting
12
12
  -->
13
13
 
14
14
  <script setup lang="ts">
15
- import { parseRangeString } from '@slidev/parser/core'
16
15
  import { useClipboard } from '@vueuse/core'
17
16
  import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
18
17
  import type { PropType } from 'vue'
19
18
  import { configs } from '../env'
20
- import { makeId } from '../logic/utils'
21
- import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET } from '../constants'
19
+ import { makeId, updateCodeHighlightRange } from '../logic/utils'
20
+ import { CLASS_VCLICK_HIDDEN } from '../constants'
22
21
  import { useSlideContext } from '../context'
23
22
 
24
23
  const props = defineProps({
@@ -87,28 +86,27 @@ onMounted(() => {
87
86
  if (hide)
88
87
  rangeStr = props.ranges[index.value + 1] ?? finallyRange.value
89
88
 
90
- const isDuoTone = el.value.querySelector('.shiki-dark')
91
- const targets = isDuoTone ? Array.from(el.value.querySelectorAll('.shiki')) : [el.value]
92
- const startLine = props.startLine
93
- for (const target of targets) {
94
- const lines = Array.from(target.querySelectorAll('code > .line'))
95
- const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr)
96
- lines.forEach((line, idx) => {
97
- const highlighted = highlights.includes(idx + startLine)
98
- line.classList.toggle(CLASS_VCLICK_TARGET, true)
99
- line.classList.toggle('highlighted', highlighted)
100
- line.classList.toggle('dishonored', !highlighted)
101
- })
102
- if (props.maxHeight) {
103
- const highlightedEls = Array.from(target.querySelectorAll('.line.highlighted')) as HTMLElement[]
104
- const height = highlightedEls.reduce((acc, el) => el.offsetHeight + acc, 0)
105
- if (height > el.value.offsetHeight) {
106
- highlightedEls[0].scrollIntoView({ behavior: 'smooth', block: 'start' })
107
- }
108
- else if (highlightedEls.length > 0) {
109
- const middleEl = highlightedEls[Math.round((highlightedEls.length - 1) / 2)]
110
- middleEl.scrollIntoView({ behavior: 'smooth', block: 'center' })
111
- }
89
+ const pre = el.value.querySelector('.shiki')!
90
+ const lines = Array.from(pre.querySelectorAll('code > .line'))
91
+ const linesCount = lines.length
92
+
93
+ updateCodeHighlightRange(
94
+ rangeStr,
95
+ linesCount,
96
+ props.startLine,
97
+ no => [lines[no]],
98
+ )
99
+
100
+ // Scroll to the highlighted line if `maxHeight` is set
101
+ if (props.maxHeight) {
102
+ const highlightedEls = Array.from(pre.querySelectorAll('.line.highlighted')) as HTMLElement[]
103
+ const height = highlightedEls.reduce((acc, el) => el.offsetHeight + acc, 0)
104
+ if (height > el.value.offsetHeight) {
105
+ highlightedEls[0].scrollIntoView({ behavior: 'smooth', block: 'start' })
106
+ }
107
+ else if (highlightedEls.length > 0) {
108
+ const middleEl = highlightedEls[Math.round((highlightedEls.length - 1) / 2)]
109
+ middleEl.scrollIntoView({ behavior: 'smooth', block: 'center' })
112
110
  }
113
111
  }
114
112
  })
@@ -12,11 +12,12 @@ Learn more: https://sli.dev/guide/syntax.html#monaco-editor
12
12
  -->
13
13
 
14
14
  <script setup lang="ts">
15
- import type * as monaco from 'monaco-editor'
16
- import { computed, nextTick, onMounted, ref } from 'vue'
17
15
  import { debounce } from '@antfu/utils'
18
16
  import lz from 'lz-string'
17
+ import type * as monaco from 'monaco-editor'
18
+ import { computed, nextTick, onMounted, ref } from 'vue'
19
19
  import { makeId } from '../logic/utils'
20
+ import CodeRunner from '../internals/CodeRunner.vue'
20
21
 
21
22
  const props = withDefaults(defineProps<{
22
23
  codeLz: string
@@ -27,6 +28,11 @@ const props = withDefaults(defineProps<{
27
28
  height?: number | string // Posible values: 'initial', 'auto', '100%', '200px', etc.
28
29
  editorOptions?: monaco.editor.IEditorOptions
29
30
  ata?: boolean
31
+ runnable?: boolean
32
+ autorun?: boolean | 'once'
33
+ outputHeight?: string
34
+ highlightOutput?: boolean
35
+ runnerOptions?: Record<string, unknown>
30
36
  }>(), {
31
37
  codeLz: '',
32
38
  lang: 'typescript',
@@ -34,10 +40,13 @@ const props = withDefaults(defineProps<{
34
40
  lineNumbers: 'off',
35
41
  height: 'initial',
36
42
  ata: true,
43
+ runnable: false,
44
+ autorun: true,
45
+ highlightOutput: true,
37
46
  })
38
47
 
39
- const code = lz.decompressFromBase64(props.codeLz).trimEnd()
40
- const diff = props.diffLz && lz.decompressFromBase64(props.diffLz).trimEnd()
48
+ const code = ref(lz.decompressFromBase64(props.codeLz).trimEnd())
49
+ const diff = props.diffLz && ref(lz.decompressFromBase64(props.diffLz).trimEnd())
41
50
 
42
51
  const langMap: Record<string, string> = {
43
52
  ts: 'typescript',
@@ -69,7 +78,8 @@ onMounted(async () => {
69
78
  // Lazy load monaco, so it will be bundled in async chunk
70
79
  const { default: setup } = await import('../setup/monaco')
71
80
  const { ata, monaco } = await setup()
72
- const model = monaco.editor.createModel(code, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
81
+ const model = monaco.editor.createModel(code.value, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
82
+ model.onDidChangeContent(() => code.value = model.getValue())
73
83
  const commonOptions = {
74
84
  automaticLayout: true,
75
85
  readOnly: props.readonly,
@@ -89,7 +99,8 @@ onMounted(async () => {
89
99
 
90
100
  let editableEditor: monaco.editor.IStandaloneCodeEditor
91
101
  if (diff) {
92
- const diffModel = monaco.editor.createModel(diff, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
102
+ const diffModel = monaco.editor.createModel(diff.value, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
103
+ diffModel.onDidChangeContent(() => code.value = model.getValue())
93
104
  const editor = monaco.editor.createDiffEditor(container.value!, {
94
105
  renderOverviewRuler: false,
95
106
  ...commonOptions,
@@ -140,11 +151,56 @@ onMounted(async () => {
140
151
  : /* BELOW */ `` // reset
141
152
  }
142
153
  }
154
+ nextTick(() => monaco.editor.remeasureFonts())
143
155
  })
144
156
  </script>
145
157
 
146
158
  <template>
147
- <div ref="outer" class="slidev-monaco-container" :style="{ height }">
148
- <div ref="container" class="absolute inset-0.5" />
159
+ <div class="relative slidev-monaco-container">
160
+ <div ref="outer" class="relative slidev-monaco-container-inner" :style="{ height }">
161
+ <div ref="container" class="absolute inset-0.5" />
162
+ </div>
163
+ <CodeRunner
164
+ v-if="props.runnable"
165
+ v-model="code"
166
+ :lang="lang"
167
+ :autorun="props.autorun"
168
+ :height="props.outputHeight"
169
+ :highlight-output="props.highlightOutput"
170
+ :runner-options="props.runnerOptions"
171
+ />
149
172
  </div>
150
173
  </template>
174
+
175
+ <style>
176
+ div[widgetid='messageoverlay'] {
177
+ transform: translateY(calc(100% * (var(--slidev-slide-scale) - 1)));
178
+ }
179
+
180
+ .slidev-monaco-container {
181
+ position: relative;
182
+ margin: var(--slidev-code-margin);
183
+ line-height: var(--slidev-code-line-height);
184
+ border-radius: var(--slidev-code-radius);
185
+ background: var(--slidev-code-background);
186
+ }
187
+
188
+ .slidev-monaco-container-inner {
189
+ padding: var(--slidev-code-padding);
190
+ }
191
+
192
+ .slidev-monaco-container .monaco-editor {
193
+ --monaco-monospace-font: var(--slidev-code-font-family);
194
+ --vscode-editor-background: var(--slidev-code-background);
195
+ --vscode-editorGutter-background: var(--slidev-code-background);
196
+ }
197
+
198
+ /** Revert styles */
199
+ .slidev-monaco-container .monaco-editor a {
200
+ border-bottom: none;
201
+ }
202
+
203
+ .slidev-monaco-container .monaco-editor a:hover {
204
+ border-bottom: none;
205
+ }
206
+ </style>
@@ -1,22 +1,26 @@
1
1
  <script setup lang="ts">
2
2
  import { ShikiMagicMovePrecompiled } from 'shiki-magic-move/vue'
3
3
  import type { KeyedTokensInfo } from 'shiki-magic-move/types'
4
- import { onMounted, onUnmounted, ref, watchEffect } from 'vue'
4
+ import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
5
5
  import lz from 'lz-string'
6
6
  import { useSlideContext } from '../context'
7
- import { makeId } from '../logic/utils'
8
-
9
- import 'shiki-magic-move/style.css'
7
+ import { makeId, updateCodeHighlightRange } from '../logic/utils'
10
8
 
11
9
  const props = defineProps<{
12
- stepsLz: string
13
10
  at?: string | number
11
+ stepsLz: string
12
+ stepRanges: string[][]
14
13
  }>()
15
14
 
16
15
  const steps = JSON.parse(lz.decompressFromBase64(props.stepsLz)) as KeyedTokensInfo[]
17
16
  const { $clicksContext: clicks, $scale: scale } = useSlideContext()
18
17
  const id = makeId()
19
- const index = ref(0)
18
+
19
+ const stepIndex = ref(0)
20
+ const container = ref<HTMLElement>()
21
+
22
+ // Normalized the ranges, to at least have one range
23
+ const ranges = computed(() => props.stepRanges.map(i => i.length ? i : ['all']))
20
24
 
21
25
  onUnmounted(() => {
22
26
  clicks!.unregister(id)
@@ -26,25 +30,74 @@ onMounted(() => {
26
30
  if (!clicks || clicks.disabled)
27
31
  return
28
32
 
29
- const { start, end, delta } = clicks.resolve(props.at || '+1', steps.length - 1)
33
+ if (ranges.value.length !== steps.length)
34
+ throw new Error('[slidev] The length of stepRanges does not match the length of steps, this is an internal error.')
35
+
36
+ const clickCounts = ranges.value.map(s => s.length).reduce((a, b) => a + b, 0)
37
+ const { start, end, delta } = clicks.resolve(props.at ?? '+1', clickCounts - 1)
30
38
  clicks.register(id, { max: end, delta })
31
39
 
32
- watchEffect(() => {
33
- if (clicks.disabled)
34
- index.value = steps.length - 1
35
- else
36
- index.value = Math.min(Math.max(0, clicks.current - start + 1), steps.length - 1)
37
- })
40
+ watch(
41
+ () => clicks.current,
42
+ () => {
43
+ // Calculate the step and rangeStr based on the current click count
44
+ const clickCount = clicks.current - start
45
+ let step = steps.length - 1
46
+ let _currentClickSum = 0
47
+ let rangeStr = 'all'
48
+ for (let i = 0; i < ranges.value.length; i++) {
49
+ const current = ranges.value[i]
50
+ if (clickCount < _currentClickSum + current.length - 1) {
51
+ step = i
52
+ rangeStr = current[clickCount - _currentClickSum + 1]
53
+ break
54
+ }
55
+ _currentClickSum += current.length || 1
56
+ }
57
+ stepIndex.value = step
58
+
59
+ const pre = container.value?.querySelector('.shiki') as HTMLElement
60
+ if (!pre)
61
+ return
62
+
63
+ const children = (Array.from(pre.children) as HTMLElement[])
64
+ .slice(1) // Remove the first anchor
65
+ .filter(i => !i.className.includes('shiki-magic-move-leave')) // Filter the leaving elements
66
+
67
+ // Group to lines between `<br>`
68
+ const lines = children.reduce((acc, el) => {
69
+ if (el.tagName === 'BR')
70
+ acc.push([])
71
+ else
72
+ acc[acc.length - 1].push(el)
73
+ return acc
74
+ }, [[]] as HTMLElement[][])
75
+
76
+ // Update highlight range
77
+ updateCodeHighlightRange(
78
+ rangeStr,
79
+ lines.length,
80
+ 1,
81
+ no => lines[no],
82
+ )
83
+ },
84
+ { immediate: true },
85
+ )
38
86
  })
39
87
  </script>
40
88
 
41
89
  <template>
42
- <div class="slidev-code-wrapper slidev-code-magic-move">
90
+ <div ref="container" class="slidev-code-wrapper slidev-code-magic-move relative">
43
91
  <ShikiMagicMovePrecompiled
44
92
  class="slidev-code relative shiki overflow-visible"
45
93
  :steps="steps"
46
- :step="index"
47
- :options="{ globalScale: scale }"
94
+ :step="stepIndex"
95
+ :options="{
96
+ globalScale: scale,
97
+ // TODO: make this configurable later
98
+ duration: 800,
99
+ stagger: 1,
100
+ }"
48
101
  />
49
102
  </div>
50
103
  </template>
package/builtin/Tweet.vue CHANGED
@@ -22,7 +22,14 @@ const tweet = ref<HTMLElement | null>()
22
22
  const loaded = ref(false)
23
23
  const tweetNotFound = ref(false)
24
24
 
25
- onMounted(async () => {
25
+ async function create(retries = 10) {
26
+ // @ts-expect-error global
27
+ if (!window.twttr?.widgets?.createTweet) {
28
+ if (retries <= 0)
29
+ return console.error('Failed to load Twitter widget after 10 retries.')
30
+ setTimeout(() => create(retries - 1), 1000)
31
+ return
32
+ }
26
33
  // @ts-expect-error global
27
34
  const element = await window.twttr.widgets.createTweet(
28
35
  props.id.toString(),
@@ -36,6 +43,10 @@ onMounted(async () => {
36
43
  loaded.value = true
37
44
  if (element === undefined)
38
45
  tweetNotFound.value = true
46
+ }
47
+
48
+ onMounted(() => {
49
+ create()
39
50
  })
40
51
  </script>
41
52
 
@@ -23,6 +23,7 @@ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): C
23
23
  },
24
24
  relativeOffsets,
25
25
  map,
26
+ onMounted() {},
26
27
  resolve(at, size = 1) {
27
28
  const [isRelative, value] = normalizeAtProp(at)
28
29
  if (isRelative) {
@@ -57,8 +58,7 @@ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): C
57
58
  get total() {
58
59
  // eslint-disable-next-line no-unused-expressions
59
60
  routeForceRefresh.value
60
- return clicksOverrides
61
- ?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
61
+ return clicksOverrides ?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
62
62
  },
63
63
  }
64
64
  }
@@ -68,6 +68,7 @@ const queryClicksRaw = useRouteQuery('clicks', '0')
68
68
  export function usePrimaryClicks(route: SlideRoute): ClicksContext {
69
69
  if (route?.meta?.__clicksContext)
70
70
  return route.meta.__clicksContext
71
+
71
72
  const thisNo = route.no
72
73
  const current = computed({
73
74
  get() {
@@ -92,6 +93,13 @@ export function usePrimaryClicks(route: SlideRoute): ClicksContext {
92
93
  current,
93
94
  route?.meta?.clicks,
94
95
  )
96
+
97
+ // On slide mounted, make sure the query is not greater than the total
98
+ context.onMounted = () => {
99
+ if (queryClicksRaw.value)
100
+ queryClicksRaw.value = Math.min(queryClicksRaw.value, context.total)
101
+ }
102
+
95
103
  if (route?.meta)
96
104
  route.meta.__clicksContext = context
97
105
  return context
@@ -3,7 +3,8 @@ import type { ComputedRef, Ref, TransitionGroupProps } from 'vue'
3
3
  import { computed, ref, watch } from 'vue'
4
4
  import type { Router } from 'vue-router'
5
5
  import { getCurrentTransition } from '../logic/transition'
6
- import { getSlidePath } from '../logic/slides'
6
+ import { getSlide, getSlidePath } from '../logic/slides'
7
+ import { CLICKS_MAX } from '../constants'
7
8
  import { useTocTree } from './useTocTree'
8
9
  import { skipTransition } from './hmr'
9
10
  import { slides } from '#slidev/slides'
@@ -121,12 +122,12 @@ export function useNavBase(
121
122
  async function prevSlide(lastClicks = true) {
122
123
  clicksDirection.value = -1
123
124
  const next = Math.max(1, currentSlideNo.value - 1)
124
- await go(next)
125
- if (lastClicks && clicksTotal.value) {
126
- router?.replace({
127
- query: { ...router.currentRoute.value.query, clicks: clicksTotal.value },
128
- })
129
- }
125
+ await go(
126
+ next,
127
+ lastClicks
128
+ ? getSlide(next)?.meta.__clicksContext?.total ?? CLICKS_MAX
129
+ : undefined,
130
+ )
130
131
  }
131
132
 
132
133
  function goFirst() {
@@ -141,7 +142,10 @@ export function useNavBase(
141
142
  skipTransition.value = false
142
143
  await router?.push({
143
144
  path: getSlidePath(page),
144
- query: { ...router.currentRoute.value.query, clicks },
145
+ query: {
146
+ ...router.currentRoute.value.query,
147
+ clicks: clicks || undefined,
148
+ },
145
149
  })
146
150
  }
147
151
 
@@ -9,14 +9,14 @@ export interface UseSlideInfo {
9
9
  update: (data: SlidePatch) => Promise<SlideInfo | void>
10
10
  }
11
11
 
12
- export function useSlideInfo(id: number | undefined): UseSlideInfo {
13
- if (id == null) {
12
+ export function useSlideInfo(no: number): UseSlideInfo {
13
+ if (no == null) {
14
14
  return {
15
15
  info: ref() as Ref<SlideInfo | undefined>,
16
16
  update: async () => {},
17
17
  }
18
18
  }
19
- const url = `/@slidev/slide/${id}.json`
19
+ const url = `/@slidev/slide/${no}.json`
20
20
  const { data: info, execute } = useFetch(url).json().get()
21
21
 
22
22
  execute()
@@ -37,11 +37,11 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
37
37
 
38
38
  if (__DEV__) {
39
39
  import.meta.hot?.on('slidev:update-slide', (payload) => {
40
- if (payload.id === id)
40
+ if (payload.no === no)
41
41
  info.value = payload.data
42
42
  })
43
43
  import.meta.hot?.on('slidev:update-note', (payload) => {
44
- if (payload.id === id && info.value.note?.trim() !== payload.note?.trim())
44
+ if (payload.no === no && info.value.note?.trim() !== payload.note?.trim())
45
45
  info.value = { ...info.value, ...payload }
46
46
  })
47
47
  }
@@ -52,20 +52,17 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
52
52
  }
53
53
  }
54
54
 
55
- const map: Record<string, UseSlideInfo> = {}
55
+ const map: Record<number, UseSlideInfo> = {}
56
56
 
57
- export function useDynamicSlideInfo(id: MaybeRef<number | undefined>) {
58
- function get(id: number | undefined) {
59
- const i = `${id}`
60
- if (!map[i])
61
- map[i] = useSlideInfo(id)
62
- return map[i]
57
+ export function useDynamicSlideInfo(no: MaybeRef<number>) {
58
+ function get(no: number) {
59
+ return map[no] ??= useSlideInfo(no)
63
60
  }
64
61
 
65
62
  return {
66
- info: computed(() => get(unref(id)).info.value),
63
+ info: computed(() => get(unref(no)).info.value),
67
64
  update: async (data: SlidePatch, newId?: number) => {
68
- const info = get(newId ?? unref(id))
65
+ const info = get(newId ?? unref(no))
69
66
  const newData = await info.update(data)
70
67
  if (newData)
71
68
  info.info.value = newData
@@ -1,5 +1,6 @@
1
1
  import { ref } from 'vue'
2
2
  import { useRouter } from 'vue-router'
3
+ import { getSlide } from '../logic/nav'
3
4
 
4
5
  export function useViewTransition() {
5
6
  const router = useRouter()
@@ -11,13 +12,15 @@ export function useViewTransition() {
11
12
  const supportViewTransition = typeof document !== 'undefined' && 'startViewTransition' in document
12
13
 
13
14
  router.beforeResolve((to, from) => {
14
- const fromNo = from.meta.slide?.no
15
- const toNo = to.meta.slide?.no
15
+ const fromMeta = getSlide(from.params.no as string)?.meta
16
+ const toMeta = getSlide(to.params.no as string)?.meta
17
+ const fromNo = fromMeta?.slide?.no
18
+ const toNo = toMeta?.slide?.no
16
19
  if (
17
20
  !(
18
21
  fromNo !== undefined && toNo !== undefined && (
19
- (from.meta.transition === 'view-transition' && fromNo < toNo)
20
- || (to.meta.transition === 'view-transition' && toNo < fromNo)
22
+ (fromMeta?.transition === 'view-transition' && fromNo < toNo)
23
+ || (toMeta?.transition === 'view-transition' && toNo < fromNo)
21
24
  )
22
25
  )
23
26
  ) {
package/context.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { ref, shallowRef, toRef } from 'vue'
1
+ import { ref, toRef } from 'vue'
2
2
  import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
3
- import { useFixedClicks } from './composables/useClicks'
4
3
  import {
5
4
  FRONTMATTER_FIELDS,
6
5
  HEADMATTER_FIELDS,
@@ -13,15 +12,13 @@ import {
13
12
  injectionSlidevContext,
14
13
  } from './constants'
15
14
 
16
- const clicksContextFallback = shallowRef(useFixedClicks())
17
-
18
15
  /**
19
16
  * Get the current slide context, should be called inside the setup function of a component inside slide
20
17
  */
21
18
  export function useSlideContext() {
22
19
  const $slidev = injectLocal(injectionSlidevContext)!
23
20
  const $nav = toRef($slidev, 'nav')
24
- const $clicksContext = injectLocal(injectionClicksContext, clicksContextFallback)!.value
21
+ const $clicksContext = injectLocal(injectionClicksContext)!.value
25
22
  const $clicks = toRef($clicksContext, 'current')
26
23
  const $page = injectLocal(injectionCurrentPage)!
27
24
  const $renderContext = injectLocal(injectionRenderContext)!
@@ -42,6 +39,8 @@ export function useSlideContext() {
42
39
  }
43
40
  }
44
41
 
42
+ export type SlideContext = ReturnType<typeof useSlideContext>
43
+
45
44
  export function provideFrontmatter(frontmatter: Record<string, any>) {
46
45
  provideLocal(injectionFrontmatter, frontmatter)
47
46
 
package/env.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { computed } from 'vue'
1
+ import { computed, ref } from 'vue'
2
2
  import { objectMap } from '@antfu/utils'
3
3
  import configs from '#slidev/configs'
4
4
 
5
5
  export { configs }
6
6
 
7
- export const slideAspect = configs.aspectRatio ?? (16 / 9)
8
- export const slideWidth = configs.canvasWidth ?? 980
7
+ export const slideAspect = ref(configs.aspectRatio ?? (16 / 9))
8
+ export const slideWidth = ref(configs.canvasWidth ?? 980)
9
9
 
10
10
  // To honor the aspect ratio more as possible, we need to approximate the height to the next integer.
11
11
  // Doing this, we will prevent on print, to create an additional empty white page after each page.
12
- export const slideHeight = Math.ceil(slideWidth / slideAspect)
12
+ export const slideHeight = computed(() => Math.ceil(slideWidth.value / slideAspect.value))
13
13
 
14
14
  export const themeVars = computed(() => {
15
15
  return objectMap(configs.themeConfig || {}, (k, v) => [`--slidev-theme-${k}`, v])
package/index.html CHANGED
@@ -8,6 +8,7 @@
8
8
  <body>
9
9
  <div id="app"></div>
10
10
  <script type="module" src="__ENTRY__"></script>
11
+ <div id="mermaid-rendering-container"></div>
11
12
  <!-- body -->
12
13
  </body>
13
14
  </html>