@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.
@@ -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,
@@ -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, directiveProvide } from '../utils'
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, VClickValue>('click', {
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, true)
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
- const clicks = Array.isArray(resolved.clicks) ? resolved.clicks : [resolved.clicks, undefined]
34
- el.dataset.slidevClicksStart = String(clicks[0])
35
- if (clicks[1] != null)
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, VClickValue>('after', {
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, dir.value, true, true)
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, VClickValue>('click-hide', {
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, false, 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
- function isClickActive(thisClick: number | [number, number], clicks: number) {
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 isClickCurrent(thisClick: number | [number, number], clicks: number) {
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
- if (value === false || value === 'false')
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
- if (clickAfter)
146
- value = '+0'
147
- else if (value == null || value === true || value === 'true')
148
- value = '+1'
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
- const isActive = computed(() => isClickActive(thisClick, ctx.current))
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 resolved: ResolvedClicksInfo = {
168
- max: maxClick,
169
- clicks: thisClick,
170
- delta,
171
- isActive,
172
- isCurrent,
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
- 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
-
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 { VClickValue } from './v-click'
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: VClickValue
9
+ at: RawAtValue
10
10
  }
11
11
 
12
- export type RoughDirectiveValue = VClickValue | RoughDirectiveOptions
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(' ')
@@ -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 { ResolvedClicksInfo } from '@slidev/types'
5
- import { injectionClickVisibility, injectionClicksContext, injectionCurrentPage, injectionRenderContext } from '../constants'
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 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
- }
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
- resolved: ResolvedClicksInfo | null
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
- resolved: resolveClick(id, binding, at),
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 = clickVisibility?.value ?? true
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 === true) {
77
+ if (visibility === 'shown') {
84
78
  const mixedVariant: Record<string, unknown> = { ...variantInitial, ...variantEnter }
85
- for (const { variant, resolved: resolvedClick } of clicks) {
86
- if (!resolvedClick || resolvedClick.isActive.value)
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, dir) {
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.1",
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.4",
40
- "@unocss/reset": "^0.59.0",
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.4",
59
- "unocss": "^0.59.0",
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
- "@slidev/parser": "0.49.0-beta.1",
64
- "@slidev/types": "0.49.0-beta.1"
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.screenX / window.innerWidth) > 0.6)
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" />
@@ -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>
@@ -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/${specifier}`)
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) => {${sanitizeJS(code)}}`
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 { routes } from '../routes'
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',