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

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.
@@ -1,23 +1,21 @@
1
1
  <script setup lang="ts">
2
- import type { RouteRecordRaw } from 'vue-router'
3
2
  import { computed, reactive, shallowRef } from 'vue'
4
- import type { ClicksContext } from '@slidev/types'
5
3
  import { provideLocal } from '@vueuse/core'
6
4
  import { injectionSlidevContext } from '../constants'
7
5
  import { configs, slideHeight, slideWidth } from '../env'
8
6
  import { getSlideClass } from '../utils'
9
- import type { SlidevContextNav } from '../modules/context'
7
+ import type { SlidevContextNav } from '../composables/useNav'
10
8
  import SlideWrapper from './SlideWrapper'
11
9
 
12
10
  import GlobalTop from '#slidev/global-components/top'
13
11
  import GlobalBottom from '#slidev/global-components/bottom'
14
12
 
15
- const props = defineProps<{
16
- clicksContext: ClicksContext
13
+ const { nav } = defineProps<{
17
14
  nav: SlidevContextNav
18
- route: RouteRecordRaw
19
15
  }>()
20
16
 
17
+ const route = computed(() => nav.currentSlideRoute.value)
18
+
21
19
  const style = computed(() => ({
22
20
  height: `${slideHeight}px`,
23
21
  width: `${slideWidth}px`,
@@ -28,11 +26,11 @@ if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
28
26
  import('./DrawingPreview.vue').then(v => (DrawingPreview.value = v.default))
29
27
 
30
28
  const id = computed(() =>
31
- `${props.route.path.toString().padStart(3, '0')}-${(props.nav.clicks.value + 1).toString().padStart(2, '0')}`,
29
+ `${route.value.no.toString().padStart(3, '0')}-${(nav.clicks.value + 1).toString().padStart(2, '0')}`,
32
30
  )
33
31
 
34
32
  provideLocal(injectionSlidevContext, reactive({
35
- nav: props.nav,
33
+ nav,
36
34
  configs,
37
35
  themeConfigs: computed(() => configs.themeConfig),
38
36
  }))
@@ -43,8 +41,8 @@ provideLocal(injectionSlidevContext, reactive({
43
41
  <GlobalBottom />
44
42
 
45
43
  <SlideWrapper
46
- :is="route?.component!"
47
- :clicks-context="clicksContext"
44
+ :is="route.component!"
45
+ :clicks-context="nav.clicksContext.value"
48
46
  :class="getSlideClass(route)"
49
47
  :route="route"
50
48
  />
@@ -55,7 +53,7 @@ provideLocal(injectionSlidevContext, reactive({
55
53
  && DrawingPreview
56
54
  "
57
55
  >
58
- <DrawingPreview :page="+route.path" />
56
+ <DrawingPreview :page="route.no" />
59
57
  </template>
60
58
 
61
59
  <GlobalTop />
@@ -2,7 +2,7 @@
2
2
  import { useEventListener, useVModel } from '@vueuse/core'
3
3
  import { computed, ref, watchEffect } from 'vue'
4
4
  import { breakpoints, showOverview, windowSize } from '../state'
5
- import { currentPage, go as goSlide, rawRoutes } from '../logic/nav'
5
+ import { currentSlideNo, go as goSlide, slides } from '../logic/nav'
6
6
  import { currentOverviewPage, overviewRowCount } from '../logic/overview'
7
7
  import { useFixedClicks } from '../composables/useClicks'
8
8
  import { getSlideClass } from '../utils'
@@ -78,17 +78,17 @@ useEventListener('keypress', (e) => {
78
78
  keyboardBuffer.value += String(num)
79
79
 
80
80
  // beyond the number of slides, reset
81
- if (+keyboardBuffer.value >= rawRoutes.length) {
81
+ if (+keyboardBuffer.value >= slides.value.length) {
82
82
  keyboardBuffer.value = ''
83
83
  return
84
84
  }
85
85
 
86
- const extactMatch = rawRoutes.findIndex(i => i.path === keyboardBuffer.value)
86
+ const extactMatch = slides.value.findIndex(i => `/${i.no}` === keyboardBuffer.value)
87
87
  if (extactMatch !== -1)
88
88
  currentOverviewPage.value = extactMatch + 1
89
89
 
90
90
  // When the input number is the largest at the number of digits, we go to that page directly.
91
- if (+keyboardBuffer.value * 10 > rawRoutes.length) {
91
+ if (+keyboardBuffer.value * 10 > slides.value.length) {
92
92
  go(+keyboardBuffer.value)
93
93
  keyboardBuffer.value = ''
94
94
  }
@@ -97,7 +97,7 @@ useEventListener('keypress', (e) => {
97
97
  watchEffect(() => {
98
98
  // Watch currentPage, make sure every time we open overview,
99
99
  // we focus on the right page.
100
- currentOverviewPage.value = currentPage.value
100
+ currentOverviewPage.value = currentSlideNo.value
101
101
  // Watch rowCount, make sure up and down shortcut work correctly.
102
102
  overviewRowCount.value = rowCount.value
103
103
  })
@@ -120,17 +120,17 @@ watchEffect(() => {
120
120
  :style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
121
121
  >
122
122
  <div
123
- v-for="(route, idx) of rawRoutes"
124
- :key="route.path"
123
+ v-for="(route, idx) of slides"
124
+ :key="route.no"
125
125
  class="relative"
126
126
  >
127
127
  <div
128
128
  class="inline-block border rounded overflow-hidden bg-main hover:border-primary transition"
129
129
  :class="(focus(idx + 1) || currentOverviewPage === idx + 1) ? 'border-primary' : 'border-main'"
130
- @click="go(+route.path)"
130
+ @click="go(route.no)"
131
131
  >
132
132
  <SlideContainer
133
- :key="route.path"
133
+ :key="route.no"
134
134
  :width="cardWidth"
135
135
  :clicks-disabled="true"
136
136
  class="pointer-events-none"
@@ -143,7 +143,7 @@ watchEffect(() => {
143
143
  :route="route"
144
144
  render-context="overview"
145
145
  />
146
- <DrawingPreview :page="+route.path" />
146
+ <DrawingPreview :page="route.no" />
147
147
  </SlideContainer>
148
148
  </div>
149
149
  <div
@@ -3,7 +3,7 @@ import { throttledWatch, useEventListener } from '@vueuse/core'
3
3
  import { computed, nextTick, onMounted, ref, watch } from 'vue'
4
4
  import { activeElement, editorHeight, editorWidth, isInputting, showEditor, isEditorVertical as vertical } from '../state'
5
5
  import { useCodeMirror } from '../setup/codemirror'
6
- import { currentSlideId, openInEditor } from '../logic/nav'
6
+ import { currentSlideNo, openInEditor } from '../logic/nav'
7
7
  import { useDynamicSlideInfo } from '../logic/note'
8
8
  import IconButton from './IconButton.vue'
9
9
 
@@ -19,7 +19,7 @@ const frontmatter = ref<any>({})
19
19
  const contentInput = ref<HTMLTextAreaElement>()
20
20
  const noteInput = ref<HTMLTextAreaElement>()
21
21
 
22
- const { info, update } = useDynamicSlideInfo(currentSlideId)
22
+ const { info, update } = useDynamicSlideInfo(currentSlideNo)
23
23
 
24
24
  watch(
25
25
  info,
@@ -103,7 +103,7 @@ onMounted(async () => {
103
103
  })
104
104
  })
105
105
 
106
- watch(currentSlideId, () => {
106
+ watch(currentSlideNo, () => {
107
107
  contentEditor.clearHistory()
108
108
  noteEditor.clearHistory()
109
109
  }, { flush: 'post' })
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, ref } from 'vue'
3
+
4
+ const timeout = ref(false)
5
+ onMounted(() => {
6
+ setTimeout(() => {
7
+ timeout.value = true
8
+ }, 200)
9
+ })
10
+ </script>
11
+
12
+ <template>
13
+ <div class="h-full w-full flex items-center justify-center gap-2">
14
+ <template v-if="timeout">
15
+ <div class="i-svg-spinners-90-ring-with-bg text-xl" />
16
+ <div>Loading slide...</div>
17
+ </template>
18
+ </div>
19
+ </template>
@@ -1,9 +1,9 @@
1
- import { computed, defineComponent, h, ref, toRef } from 'vue'
1
+ import { computed, defineAsyncComponent, defineComponent, h, ref, toRef } from 'vue'
2
2
  import type { PropType } from 'vue'
3
3
  import { provideLocal } from '@vueuse/core'
4
- import type { ClicksContext, RenderContext } from '@slidev/types'
5
- import type { RouteRecordRaw } from 'vue-router'
4
+ import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
6
5
  import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
6
+ import SlideLoading from './SlideLoading.vue'
7
7
 
8
8
  export default defineComponent({
9
9
  name: 'SlideWrapper',
@@ -21,17 +21,16 @@ export default defineComponent({
21
21
  default: false,
22
22
  },
23
23
  is: {
24
- type: Object,
25
24
  required: true,
26
25
  },
27
26
  route: {
28
- type: Object as PropType<RouteRecordRaw>,
27
+ type: Object as PropType<SlideRoute>,
29
28
  required: true,
30
29
  },
31
30
  },
32
31
  setup(props) {
33
32
  provideLocal(injectionRoute, props.route)
34
- provideLocal(injectionCurrentPage, ref(+props.route.path))
33
+ provideLocal(injectionCurrentPage, ref(props.route.no))
35
34
  provideLocal(injectionRenderContext, ref(props.renderContext as RenderContext))
36
35
  provideLocal(injectionActive, toRef(props, 'active'))
37
36
  provideLocal(injectionClicksContext, toRef(props, 'clicksContext'))
@@ -48,11 +47,12 @@ export default defineComponent({
48
47
  }
49
48
  })
50
49
 
51
- return {
52
- style,
53
- }
54
- },
55
- render() {
56
- return h(this.$props.is, { style: this.style })
50
+ const SlideComponent = defineAsyncComponent({
51
+ loader: (props.is as any),
52
+ delay: 300,
53
+ loadingComponent: SlideLoading,
54
+ })
55
+
56
+ return () => h(SlideComponent, { style: style.value })
57
57
  },
58
58
  })
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { TransitionGroup, computed, shallowRef, watch } from 'vue'
3
- import { currentRoute, isPresenter, nextRoute, rawRoutes, transition } from '../logic/nav'
3
+ import { currentSlideRoute, currentTransition, isPresenter, nextRoute, slides } from '../logic/nav'
4
4
  import { getSlideClass } from '../utils'
5
5
  import { useViewTransition } from '../composables/useViewTransition'
6
6
  import { skipTransition } from '../composables/hmr'
@@ -16,9 +16,9 @@ defineProps<{
16
16
  }>()
17
17
 
18
18
  // preload next route
19
- watch(currentRoute, () => {
20
- if (currentRoute.value?.meta && currentRoute.value.meta.preload !== false)
21
- currentRoute.value.meta.__preloaded = true
19
+ watch(currentSlideRoute, () => {
20
+ if (currentSlideRoute.value?.meta && currentSlideRoute.value.meta.preload !== false)
21
+ currentSlideRoute.value.meta.__preloaded = true
22
22
  if (nextRoute.value?.meta && nextRoute.value.meta.preload !== false)
23
23
  nextRoute.value.meta.__preloaded = true
24
24
  }, { immediate: true })
@@ -29,7 +29,7 @@ const DrawingLayer = shallowRef<any>()
29
29
  if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
30
30
  import('./DrawingLayer.vue').then(v => DrawingLayer.value = v.default)
31
31
 
32
- const loadedRoutes = computed(() => rawRoutes.filter(r => r.meta?.__preloaded || r === currentRoute.value))
32
+ const loadedRoutes = computed(() => slides.value.filter(r => r.meta?.__preloaded || r === currentSlideRoute.value))
33
33
 
34
34
  function onAfterLeave() {
35
35
  // After transition, we disable it so HMR won't trigger it again
@@ -45,22 +45,25 @@ function onAfterLeave() {
45
45
  <!-- Slides -->
46
46
  <component
47
47
  :is="hasViewTransition ? 'div' : TransitionGroup"
48
- v-bind="skipTransition ? {} : transition"
48
+ v-bind="skipTransition ? {} : currentTransition"
49
49
  id="slideshow"
50
50
  tag="div"
51
51
  @after-leave="onAfterLeave"
52
52
  >
53
- <template v-for="route of loadedRoutes" :key="route.path">
53
+ <div
54
+ v-for="route of loadedRoutes"
55
+ v-show="route === currentSlideRoute"
56
+ :key="route.no"
57
+ >
54
58
  <SlideWrapper
55
- :is="route?.component as any"
56
- v-show="route === currentRoute"
59
+ :is="route.component!"
57
60
  :clicks-context="usePrimaryClicks(route)"
58
61
  :class="getSlideClass(route)"
59
62
  :route="route"
60
63
  :render-context="renderContext"
61
64
  class="overflow-hidden"
62
65
  />
63
- </template>
66
+ </div>
64
67
  </component>
65
68
 
66
69
  <!-- Global Top -->
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="px-4 py-10 text-center text-red-700 dark:text-red-500 font-bold font-mono">
3
+ An error occurred on this slide. Check the terminal for more information.
4
+ </div>
5
+ </template>
package/logic/drawings.ts CHANGED
@@ -5,7 +5,7 @@ import { toReactive, useLocalStorage } from '@vueuse/core'
5
5
  import { drawingState, onPatch, patch } from '../state/drawings'
6
6
  import { configs } from '../env'
7
7
  import { isInputting } from '../state'
8
- import { currentPage, isPresenter } from './nav'
8
+ import { currentSlideNo, isPresenter } from './nav'
9
9
 
10
10
  export const brushColors = [
11
11
  '#ff595e',
@@ -63,7 +63,7 @@ export const drauu = markRaw(createDrauu(drauuOptions))
63
63
  export function clearDrauu() {
64
64
  drauu.clear()
65
65
  if (syncUp.value)
66
- patch(currentPage.value, '')
66
+ patch(currentSlideNo.value, '')
67
67
  }
68
68
 
69
69
  export function updateState() {
@@ -74,7 +74,7 @@ export function updateState() {
74
74
 
75
75
  export function loadCanvas(page?: number) {
76
76
  disableDump = true
77
- const data = drawingState[page || currentPage.value]
77
+ const data = drawingState[page || currentSlideNo.value]
78
78
  if (data != null)
79
79
  drauu.load(data)
80
80
  else
@@ -87,7 +87,7 @@ drauu.on('changed', () => {
87
87
  updateState()
88
88
  if (!disableDump) {
89
89
  const dump = drauu.dump()
90
- const key = currentPage.value
90
+ const key = currentSlideNo.value
91
91
  if ((drawingState[key] || '') !== dump && syncUp.value)
92
92
  patch(key, drauu.dump())
93
93
  }
@@ -95,14 +95,14 @@ drauu.on('changed', () => {
95
95
 
96
96
  onPatch((state) => {
97
97
  disableDump = true
98
- if (state[currentPage.value] != null)
99
- drauu.load(state[currentPage.value] || '')
98
+ if (state[currentSlideNo.value] != null)
99
+ drauu.load(state[currentSlideNo.value] || '')
100
100
  disableDump = false
101
101
  updateState()
102
102
  })
103
103
 
104
104
  nextTick(() => {
105
- watch(currentPage, () => {
105
+ watch(currentSlideNo, () => {
106
106
  if (!drauu.mounted)
107
107
  return
108
108
  loadCanvas()
@@ -0,0 +1,20 @@
1
+ import { computed } from 'vue'
2
+ import { logicOr } from '@vueuse/math'
3
+ import { configs } from '../env'
4
+ import { router } from '../routes'
5
+ import { getSlide, slides } from './slides'
6
+
7
+ export const currentRoute = computed(() => router.currentRoute.value)
8
+
9
+ export const isPrintMode = computed(() => currentRoute.value.query.print !== undefined)
10
+ export const isPrintWithClicks = computed(() => currentRoute.value.query.print === 'clicks')
11
+ export const isEmbedded = computed(() => currentRoute.value.query.embedded !== undefined)
12
+ export const isPlaying = computed(() => currentRoute.value.name === 'play')
13
+ export const isPresenter = computed(() => currentRoute.value.name === 'presenter')
14
+ export const isNotesViewer = computed(() => currentRoute.value.name === 'notes')
15
+ export const isPresenterAvailable = computed(() => !isPresenter.value && (!configs.remote || currentRoute.value.query.password === configs.remote))
16
+
17
+ export const hasPrimarySlide = logicOr(isPlaying, isPresenter)
18
+
19
+ export const currentSlideNo = computed(() => hasPrimarySlide.value ? getSlide(currentRoute.value.params.no as string)?.no ?? 1 : 1)
20
+ export const currentSlideRoute = computed(() => slides.value[currentSlideNo.value - 1])