@slidev/client 0.43.0-beta.3 → 0.43.0-beta.4

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.
@@ -16,6 +16,7 @@ import { computed, onMounted, ref } from 'vue'
16
16
  import { useEventListener } from '@vueuse/core'
17
17
  import { decode } from 'js-base64'
18
18
  import { nanoid } from 'nanoid'
19
+ import type * as monaco from 'monaco-editor'
19
20
  import { isDark } from '../logic/dark'
20
21
 
21
22
  const props = withDefaults(defineProps<{
@@ -25,6 +26,7 @@ const props = withDefaults(defineProps<{
25
26
  readonly?: boolean
26
27
  lineNumbers?: 'on' | 'off' | 'relative' | 'interval'
27
28
  height?: number | string
29
+ editorOptions?: monaco.editor.IEditorOptions
28
30
  }>(), {
29
31
  code: '',
30
32
  lang: 'typescript',
@@ -108,6 +110,7 @@ useEventListener(window, 'message', ({ data: payload }) => {
108
110
  lang: props.lang,
109
111
  readonly: props.readonly,
110
112
  lineNumbers: props.lineNumbers,
113
+ editorOptions: props.editorOptions,
111
114
  dark: isDark.value,
112
115
  style: Object.entries(getStyleObject(iframe.value)).map(([k, v]) => `${k}: ${v};`).join(''),
113
116
  })
@@ -0,0 +1,56 @@
1
+ import { ref } from 'vue'
2
+ import { useRouter } from 'vue-router'
3
+
4
+ export function useViewTransition() {
5
+ const router = useRouter()
6
+ const isViewTransition = ref(false)
7
+
8
+ let viewTransitionFinish: undefined | (() => void)
9
+ let viewTransitionAbort: undefined | (() => void)
10
+
11
+ const supportViewTransition = typeof document !== 'undefined' && 'startViewTransition' in document
12
+
13
+ router.beforeResolve((from, to) => {
14
+ if (!(from.meta.transition === 'view-transition' || to.meta.transition === 'view-transition')) {
15
+ isViewTransition.value = false
16
+ return
17
+ }
18
+
19
+ if (!supportViewTransition) {
20
+ isViewTransition.value = false
21
+ console.warn('View transition is not supported in your browser, fallback to normal transition.')
22
+ return
23
+ }
24
+
25
+ isViewTransition.value = true
26
+ const promise = new Promise<void>((resolve, reject) => {
27
+ viewTransitionFinish = resolve
28
+ viewTransitionAbort = reject
29
+ })
30
+
31
+ let changeRoute: () => void
32
+ const ready = new Promise<void>(resolve => (changeRoute = resolve))
33
+
34
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
35
+ // @ts-expect-error
36
+ const transition = document.startViewTransition(() => {
37
+ changeRoute()
38
+ return promise
39
+ })
40
+
41
+ transition.finished.then(() => {
42
+ viewTransitionAbort = undefined
43
+ viewTransitionFinish = undefined
44
+ })
45
+ return ready
46
+ })
47
+
48
+ if (supportViewTransition) {
49
+ router.afterEach(() => {
50
+ viewTransitionFinish?.()
51
+ viewTransitionAbort?.()
52
+ })
53
+ }
54
+
55
+ return isViewTransition
56
+ }
@@ -16,6 +16,7 @@ const props = {
16
16
  lineNumbers: url.searchParams.get('lineNumbers') ?? 'off',
17
17
  dark: false,
18
18
  style: '',
19
+ editorOptions: {},
19
20
  }
20
21
 
21
22
  const styleObject = document.createElement('style')
@@ -65,7 +66,7 @@ function post(data: any, type = 'slidev-monaco') {
65
66
  }
66
67
 
67
68
  async function start() {
68
- const { monaco, theme = {} } = await setupMonaco()
69
+ const { monaco, theme = {}, editorOptions = {} } = await setupMonaco()
69
70
 
70
71
  const style = getComputedStyle(document.documentElement)
71
72
  const container = document.getElementById('container')!
@@ -106,6 +107,7 @@ async function start() {
106
107
  enableSplitViewResizing: false,
107
108
  renderOverviewRuler: false,
108
109
  // renderSideBySide: false,
110
+ ...editorOptions,
109
111
  })
110
112
  monacoEditor.setModel({
111
113
  original: model,
@@ -146,6 +148,7 @@ async function start() {
146
148
  if (selection)
147
149
  originalEditor.setSelection(selection)
148
150
  }
151
+ originalEditor.updateOptions(props.editorOptions)
149
152
 
150
153
  if (modifiedEditor.getValue().toString() !== props.diff) {
151
154
  const selection = modifiedEditor.getSelection()
@@ -153,6 +156,7 @@ async function start() {
153
156
  if (selection)
154
157
  modifiedEditor.setSelection(selection)
155
158
  }
159
+ modifiedEditor.updateOptions(props.editorOptions)
156
160
  }
157
161
 
158
162
  diffModel.onDidChangeContent(() => {
@@ -191,6 +195,7 @@ async function start() {
191
195
  },
192
196
  overviewRulerLanes: 0,
193
197
  minimap: { enabled: false },
198
+ ...editorOptions,
194
199
  })
195
200
 
196
201
  format = async () => {
@@ -218,6 +223,7 @@ async function start() {
218
223
  if (selection)
219
224
  originalEditor.setSelection(selection)
220
225
  }
226
+ originalEditor.updateOptions(props.editorOptions)
221
227
  }
222
228
  }
223
229
 
@@ -6,6 +6,10 @@ import { useCodeMirror } from '../setup/codemirror'
6
6
  import { currentSlideId, openInEditor } from '../logic/nav'
7
7
  import { useDynamicSlideInfo } from '../logic/note'
8
8
 
9
+ const props = defineProps<{
10
+ resize: boolean
11
+ }>()
12
+
9
13
  const tab = ref<'content' | 'note'>('content')
10
14
  const content = ref('')
11
15
  const note = ref('')
@@ -103,16 +107,19 @@ function switchTab(newTab: typeof tab.value) {
103
107
  // @ts-expect-error force cast
104
108
  document.activeElement?.blur?.()
105
109
  }
106
- useEventListener('pointermove', (e) => {
107
- if (handlerDown.value)
108
- updateWidth(window.innerWidth - e.pageX)
109
- }, { passive: true })
110
- useEventListener('pointerup', () => {
111
- handlerDown.value = false
112
- })
113
- useEventListener('resize', () => {
114
- updateWidth(editorWidth.value)
115
- })
110
+
111
+ if (props.resize) {
112
+ useEventListener('pointermove', (e) => {
113
+ if (handlerDown.value)
114
+ updateWidth(window.innerWidth - e.pageX)
115
+ }, { passive: true })
116
+ useEventListener('pointerup', () => {
117
+ handlerDown.value = false
118
+ })
119
+ useEventListener('resize', () => {
120
+ updateWidth(editorWidth.value)
121
+ })
122
+ }
116
123
 
117
124
  throttledWatch(
118
125
  [content, note],
@@ -126,14 +133,16 @@ throttledWatch(
126
133
 
127
134
  <template>
128
135
  <div
136
+ v-if="resize"
129
137
  class="fixed h-full top-0 bottom-0 w-10px bg-gray-400 select-none opacity-0 hover:opacity-10 z-100"
130
138
  :class="{ '!opacity-30': handlerDown }"
131
139
  :style="{ right: `${editorWidth - 5}px`, cursor: 'col-resize' }"
132
140
  @pointerdown="onHandlerDown"
133
141
  />
134
142
  <div
135
- class="shadow bg-main p-4 grid grid-rows-[max-content_1fr] h-full overflow-hidden border-l border-gray-400 border-opacity-20"
136
- :style="{ width: `${editorWidth}px` }"
143
+ class="shadow bg-main p-4 grid grid-rows-[max-content_1fr] h-full overflow-hidden"
144
+ :class="resize ? 'border-l border-gray-400 border-opacity-20' : ''"
145
+ :style="resize ? { width: `${editorWidth}px` } : {}"
137
146
  >
138
147
  <div class="flex pb-2 text-xl -mt-1">
139
148
  <div class="mr-4 rounded flex">
@@ -118,7 +118,11 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
118
118
  <carbon:user-speaker />
119
119
  </RouterLink>
120
120
 
121
- <button v-if="__DEV__ && !isPresenter" class="slidev-icon-btn <md:hidden" @click="showEditor = !showEditor">
121
+ <button
122
+ v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__"
123
+ class="slidev-icon-btn <md:hidden"
124
+ @click="showEditor = !showEditor"
125
+ >
122
126
  <carbon:text-annotation-toggle />
123
127
  </button>
124
128
  </template>
@@ -32,7 +32,7 @@ useSwipeControls(root)
32
32
  const persistNav = computed(() => isScreenVertical.value || showEditor.value)
33
33
 
34
34
  const Editor = shallowRef<any>()
35
- if (__DEV__)
35
+ if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
36
36
  import('./Editor.vue').then(v => Editor.value = v.default)
37
37
 
38
38
  const DrawingControls = shallowRef<any>()
@@ -69,8 +69,8 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
69
69
  </template>
70
70
  </SlideContainer>
71
71
 
72
- <template v-if="__DEV__">
73
- <Editor v-if="Editor && showEditor" />
72
+ <template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && Editor && showEditor">
73
+ <Editor :resize="true" />
74
74
  </template>
75
75
  </div>
76
76
  <Controls />
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { useHead } from '@vueuse/head'
3
- import { computed, onMounted, reactive, ref, watch } from 'vue'
3
+ import { computed, onMounted, reactive, ref, shallowRef, watch } from 'vue'
4
4
  import { useMouse, useWindowFocus } from '@vueuse/core'
5
5
  import { clicks, clicksTotal, currentPage, currentRoute, hasNext, nextRoute, total, useSwipeControls } from '../logic/nav'
6
- import { showOverview, showPresenterCursor } from '../state'
6
+ import { showEditor, showOverview, showPresenterCursor } from '../state'
7
7
  import { configs, themeVars } from '../env'
8
8
  import { sharedState } from '../state/shared'
9
9
  import { registerShortcuts } from '../logic/shortcuts'
@@ -53,6 +53,10 @@ const nextSlide = computed(() => {
53
53
  }
54
54
  })
55
55
 
56
+ const Editor = shallowRef<any>()
57
+ if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
58
+ import('./Editor.vue').then(v => Editor.value = v.default)
59
+
56
60
  // sync presenter cursor
57
61
  onMounted(() => {
58
62
  const slidesContainer = main.value!.querySelector('#slide-content')!
@@ -132,7 +136,10 @@ onMounted(() => {
132
136
  </div>
133
137
  </div>
134
138
  <div class="grid-section note overflow-auto">
135
- <NoteEditor v-if="__DEV__" class="w-full max-w-full h-full overflow-auto p-2 lg:p-4" />
139
+ <template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && Editor && showEditor">
140
+ <Editor />
141
+ </template>
142
+ <NoteEditor v-else-if="__DEV__" class="w-full max-w-full h-full overflow-auto p-2 lg:p-4" />
136
143
  <NoteStatic v-else class="w-full max-w-full h-full overflow-auto p-2 lg:p-4" />
137
144
  </div>
138
145
  <div class="grid-section bottom">
@@ -57,61 +57,60 @@ watchEffect(() => {
57
57
  </script>
58
58
 
59
59
  <template>
60
- <div
61
- v-show="value"
62
- class="slides-overview bg-main !bg-opacity-75 p-16 overflow-y-auto"
60
+ <Transition
61
+ enter-active-class="duration-150 ease-out"
62
+ enter-from-class="opacity-0 scale-102 !backdrop-blur-0px"
63
+ leave-active-class="duration-200 ease-in"
64
+ leave-to-class="opacity-0 scale-102 !backdrop-blur-0px"
63
65
  >
64
66
  <div
65
- class="grid gap-y-4 gap-x-8 w-full"
66
- :style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
67
+ v-show="value"
68
+ class="bg-main !bg-opacity-75 p-16 overflow-y-auto backdrop-blur-5px fixed left-0 right-0 top-0 h-[calc(var(--vh,1vh)*100)]"
69
+ @click="close()"
67
70
  >
68
71
  <div
69
- v-for="(route, idx) of rawRoutes"
70
- :key="route.path"
71
- class="relative"
72
+ class="grid gap-y-4 gap-x-8 w-full"
73
+ :style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
72
74
  >
73
75
  <div
74
- class="inline-block border rounded border-opacity-50 overflow-hidden bg-main hover:border-$slidev-theme-primary"
75
- :class="{ 'border-$slidev-theme-primary': focus(idx + 1), 'border-gray-400': !focus(idx + 1) }"
76
- :style="themeVars"
77
- @click="go(+route.path)"
76
+ v-for="(route, idx) of rawRoutes"
77
+ :key="route.path"
78
+ class="relative"
78
79
  >
79
- <SlideContainer
80
- :key="route.path"
81
- :width="cardWidth"
82
- :clicks-disabled="true"
83
- class="pointer-events-none"
80
+ <div
81
+ class="inline-block border rounded border-opacity-50 overflow-hidden bg-main hover:border-$slidev-theme-primary transition"
82
+ :class="{ 'border-$slidev-theme-primary': focus(idx + 1), 'border-gray-400': !focus(idx + 1) }"
83
+ :style="themeVars"
84
+ @click="go(+route.path)"
84
85
  >
85
- <SlideWrapper
86
- :is="route.component"
87
- v-if="route?.component"
86
+ <SlideContainer
87
+ :key="route.path"
88
+ :width="cardWidth"
88
89
  :clicks-disabled="true"
89
- :class="getSlideClass(route)"
90
- :route="route"
91
- context="overview"
92
- />
93
- <DrawingPreview :page="+route.path" />
94
- </SlideContainer>
95
- </div>
96
- <div
97
- class="absolute top-0 opacity-50"
98
- :style="`left: ${cardWidth + 5}px`"
99
- >
100
- {{ idx + 1 }}
90
+ class="pointer-events-none"
91
+ >
92
+ <SlideWrapper
93
+ :is="route.component"
94
+ v-if="route?.component"
95
+ :clicks-disabled="true"
96
+ :class="getSlideClass(route)"
97
+ :route="route"
98
+ context="overview"
99
+ />
100
+ <DrawingPreview :page="+route.path" />
101
+ </SlideContainer>
102
+ </div>
103
+ <div
104
+ class="absolute top-0 opacity-50"
105
+ :style="`left: ${cardWidth + 5}px`"
106
+ >
107
+ {{ idx + 1 }}
108
+ </div>
101
109
  </div>
102
110
  </div>
103
111
  </div>
104
- </div>
112
+ </Transition>
105
113
  <button v-if="value" class="fixed text-2xl top-4 right-4 slidev-icon-btn text-gray-400" @click="close">
106
114
  <carbon:close />
107
115
  </button>
108
116
  </template>
109
-
110
- <style lang="postcss">
111
- .slides-overview {
112
- @apply fixed left-0 right-0 top-0;
113
- backdrop-filter: blur(5px);
114
- height: 100vh;
115
- height: calc(var(--vh, 1vh) * 100);
116
- }
117
- </style>
@@ -1,7 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { computed, shallowRef, watch } from 'vue'
2
+ import { TransitionGroup, computed, shallowRef, watch } from 'vue'
3
3
  import { clicks, currentRoute, isPresenter, nextRoute, rawRoutes, router, transition } from '../logic/nav'
4
4
  import { getSlideClass } from '../utils'
5
+ import { useViewTransition } from '../composables/useViewTransition'
5
6
  import SlideWrapper from './SlideWrapper'
6
7
 
7
8
  // @ts-expect-error virtual module
@@ -21,6 +22,8 @@ watch(currentRoute, () => {
21
22
  nextRoute.value.meta.__preloaded = true
22
23
  }, { immediate: true })
23
24
 
25
+ const hasViewTransition = useViewTransition()
26
+
24
27
  // preserve the clicks count for previous slide to avoid flash on transition
25
28
  let previousClicks: [string | undefined, number] = [] as any
26
29
  router.beforeEach(() => {
@@ -39,7 +42,11 @@ const loadedRoutes = computed(() => rawRoutes.filter(r => r.meta?.__preloaded ||
39
42
  <GlobalBottom />
40
43
 
41
44
  <!-- Slides -->
42
- <TransitionGroup v-bind="transition" id="slideshow" tag="div">
45
+ <component
46
+ :is="hasViewTransition ? 'div' : TransitionGroup"
47
+ v-bind="transition"
48
+ id="slideshow" tag="div"
49
+ >
43
50
  <template v-for="route of loadedRoutes" :key="route.path">
44
51
  <SlideWrapper
45
52
  :is="route?.component as any"
@@ -52,7 +59,7 @@ const loadedRoutes = computed(() => rawRoutes.filter(r => r.meta?.__preloaded ||
52
59
  :context="context"
53
60
  />
54
61
  </template>
55
- </TransitionGroup>
62
+ </component>
56
63
 
57
64
  <!-- Global Top -->
58
65
  <GlobalTop />
@@ -30,6 +30,9 @@ export default function createDirectives() {
30
30
  if (isClicksDisabled.value || dirInject(dir, injectionClicksDisabled)?.value)
31
31
  return
32
32
 
33
+ if (dir.value === false || dir.value === 'false')
34
+ return
35
+
33
36
  const elements = dirInject(dir, injectionClicksElements)
34
37
  const clicks = dirInject(dir, injectionClicks)
35
38
  const orderMap = dirInject(dir, injectionOrderMap)
@@ -37,19 +40,14 @@ export default function createDirectives() {
37
40
  const hide = dir.modifiers.hide !== false && dir.modifiers.hide != null
38
41
  const fade = dir.modifiers.fade !== false && dir.modifiers.fade != null
39
42
 
40
- const prev = elements?.value?.length || 0
41
-
42
43
  const CLASS_HIDE = fade ? CLASS_VCLICK_FADE : CLASS_VCLICK_HIDDEN
43
44
 
44
45
  if (elements && !elements?.value?.includes(el))
45
46
  elements.value.push(el)
46
47
 
47
- // Set default dir.value
48
- if (dir.value == null)
49
- dir.value = elements?.value?.length
50
- // Relative value starts with '+' o '-'
51
- if (typeof dir.value === 'string' && (dir.value.startsWith('+') || dir.value.startsWith('-')))
52
- dir.value = (elements?.value?.length || 0) + Number(dir.value)
48
+ const prev = elements?.value?.length || 0
49
+
50
+ resolveDirValue(dir, prev)
53
51
 
54
52
  // If orderMap didn't have dir.value aka the order key, then initialize it.
55
53
  // If key exists, then move current element to the first of order array to
@@ -121,18 +119,16 @@ export default function createDirectives() {
121
119
  if (isClicksDisabled.value || dirInject(dir, injectionClicksDisabled)?.value)
122
120
  return
123
121
 
122
+ if (dir.value === false || dir.value === 'false')
123
+ return
124
+
124
125
  const elements = dirInject(dir, injectionClicksElements)
125
126
  const clicks = dirInject(dir, injectionClicks)
126
127
  const orderMap = dirInject(dir, injectionOrderMap)
127
128
 
128
129
  const prev = elements?.value.length || 0
129
130
 
130
- // Set default dir.value
131
- if (dir.value == null)
132
- dir.value = elements?.value.length
133
- // Relative value starts with '+' o '-'
134
- if (typeof dir.value === 'string' && (dir.value.startsWith('+') || dir.value.startsWith('-')))
135
- dir.value = (elements?.value?.length || 0) + Number(dir.value)
131
+ resolveDirValue(dir, prev)
136
132
 
137
133
  // If a v-click order before v-after is lower than v-after, the order map will
138
134
  // not contain the key for v-after, so we need to set it first, then move v-after
@@ -216,3 +212,12 @@ export default function createDirectives() {
216
212
  },
217
213
  }
218
214
  }
215
+
216
+ function resolveDirValue(dir: DirectiveBinding<any>, prev: number) {
217
+ // Set default dir.value
218
+ if (dir.value == null || dir.value === true || dir.value === 'true')
219
+ dir.value = prev
220
+ // Relative value starts with '+' o '-'
221
+ if (typeof dir.value === 'string' && (dir.value.startsWith('+') || dir.value.startsWith('-')))
222
+ dir.value = prev + Number(dir.value)
223
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slidev/client",
3
- "version": "0.43.0-beta.3",
3
+ "version": "0.43.0-beta.4",
4
4
  "description": "Presentation slides for developers",
5
5
  "author": "antfu <anthonyfu117@hotmail.com>",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@antfu/utils": "^0.7.6",
19
- "@unocss/reset": "^0.55.4",
19
+ "@unocss/reset": "^0.55.5",
20
20
  "@vueuse/core": "^10.4.1",
21
21
  "@vueuse/head": "^1.3.1",
22
22
  "@vueuse/math": "^10.4.1",
@@ -35,14 +35,14 @@
35
35
  "prettier": "^3.0.3",
36
36
  "recordrtc": "^5.6.2",
37
37
  "resolve": "^1.22.4",
38
- "unocss": "^0.55.4",
38
+ "unocss": "^0.55.5",
39
39
  "vite-plugin-windicss": "^1.9.1",
40
40
  "vue": "^3.3.4",
41
41
  "vue-router": "^4.2.4",
42
42
  "vue-starport": "^0.3.0",
43
43
  "windicss": "^3.5.6",
44
- "@slidev/parser": "0.43.0-beta.3",
45
- "@slidev/types": "0.43.0-beta.3"
44
+ "@slidev/parser": "0.43.0-beta.4",
45
+ "@slidev/types": "0.43.0-beta.4"
46
46
  },
47
47
  "devDependencies": {
48
48
  "vite": "^4.4.9"