返回 slidev
root.ts
根目录 / packages / client / setup / root.ts
1 import { useHead } from '@unhead/vue'
2 import { computed, getCurrentInstance, reactive, ref, shallowRef, watch } from 'vue'
3 import { useRouter } from 'vue-router'
4 import setups from '#slidev/setups/root'
5 import { createFixedClicks } from '../composables/useClicks'
6 import { useEmbeddedControl } from '../composables/useEmbeddedCtrl'
7 import { useNav } from '../composables/useNav'
8 import { usePrintStyles } from '../composables/usePrintStyles'
9 import { injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionSlidevContext, TRUST_ORIGINS } from '../constants'
10 import { configs, slidesTitle } from '../env'
11 import { getSlidePath } from '../logic/slides'
12 import { makeId } from '../logic/utils'
13 import { hmrSkipTransition, syncDirections } from '../state'
14 import { initDrawingState } from '../state/drawings'
15 import { initSharedState, onPatch, patch } from '../state/shared'
16
17 export default function setupRoot() {
18 const app = getCurrentInstance()!.appContext.app
19
20 const context = reactive({
21 nav: useNav(),
22 configs,
23 themeConfigs: computed(() => configs.themeConfig),
24 })
25 app.provide(injectionRenderContext, ref('none' as const))
26 app.provide(injectionSlidevContext, context)
27 app.provide(injectionCurrentPage, computed(() => context.nav.currentSlideNo))
28 app.provide(injectionClicksContext, shallowRef(createFixedClicks()))
29
30 // allows controls from postMessages
31 if (__DEV__) {
32 // @ts-expect-error expose global
33 window.__slidev__ = context
34 useEmbeddedControl()
35 }
36
37 // User Setups
38 for (const setup of setups)
39 setup()
40
41 const {
42 clicksContext,
43 currentSlideNo,
44 hasPrimarySlide,
45 isNotesViewer,
46 isPresenter,
47 isPrintMode,
48 } = useNav()
49
50 useHead({
51 title: slidesTitle,
52 htmlAttrs: configs.htmlAttrs,
53 })
54
55 usePrintStyles()
56
57 initSharedState(`${slidesTitle} - shared`)
58 initDrawingState(`${slidesTitle} - drawings`)
59
60 const id = `${location.origin}_${makeId()}`
61 const syncType = computed(() => isPresenter.value ? 'presenter' : 'viewer')
62
63 // update shared state
64 function updateSharedState() {
65 const shouldSend = isPresenter.value
66 ? syncDirections.value.presenterSend
67 : syncDirections.value.viewerSend
68
69 if (!shouldSend)
70 return
71 if (isNotesViewer.value || isPrintMode.value)
72 return
73 // we allow Presenter mode, or Viewer mode from trusted origins to update the shared state
74 if (!isPresenter.value && !TRUST_ORIGINS.includes(location.host.split(':')[0]))
75 return
76
77 patch('page', +currentSlideNo.value)
78 patch('clicks', clicksContext.value.current)
79 patch('clicksTotal', clicksContext.value.total)
80 patch('lastUpdate', {
81 id,
82 type: syncType.value,
83 time: Date.now(),
84 })
85 }
86 const router = useRouter()
87 router.afterEach(updateSharedState)
88 watch(clicksContext, updateSharedState)
89
90 onPatch((state) => {
91 const shouldReceive = isPresenter.value
92 ? syncDirections.value.presenterReceive
93 : syncDirections.value.viewerReceive
94 if (!shouldReceive)
95 return
96 if (!hasPrimarySlide.value || isPrintMode.value)
97 return
98 if (state.lastUpdate?.type === syncType.value)
99 return
100 if ((+state.page === +currentSlideNo.value && +clicksContext.value.current === +state.clicks))
101 return
102 // if (state.lastUpdate?.type === 'presenter') {
103 hmrSkipTransition.value = false
104 router.replace({
105 path: getSlidePath(state.page, isPresenter.value),
106 query: {
107 ...router.currentRoute.value.query,
108 clicks: state.clicks || 0,
109 },
110 })
111 // }
112 })
113 }
114
114 lines TYPESCRIPT