107 lines
3.3 KiB
TypeScript
Raw Permalink Normal View History

2026-05-31 13:21:13 +02:00
import setups from '#slidev/setups/root'
import { useHead } from '@unhead/vue'
import { computed, getCurrentInstance, reactive, ref, shallowRef, watch } from 'vue'
import { useRouter } from 'vue-router'
import { createFixedClicks } from '../composables/useClicks'
import { useEmbeddedControl } from '../composables/useEmbeddedCtrl'
import { useNav } from '../composables/useNav'
import { usePrintStyles } from '../composables/usePrintStyles'
import { injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionSlidevContext, TRUST_ORIGINS } from '../constants'
import { configs, slidesTitle } from '../env'
import { skipTransition } from '../logic/hmr'
import { getSlidePath } from '../logic/slides'
import { makeId } from '../logic/utils'
import { initDrawingState } from '../state/drawings'
import { initSharedState, onPatch, patch } from '../state/shared'
export default function setupRoot() {
const app = getCurrentInstance()!.appContext.app
const context = reactive({
nav: useNav(),
configs,
themeConfigs: computed(() => configs.themeConfig),
})
app.provide(injectionRenderContext, ref('none' as const))
app.provide(injectionSlidevContext, context)
app.provide(injectionCurrentPage, computed(() => context.nav.currentSlideNo))
app.provide(injectionClicksContext, shallowRef(createFixedClicks()))
// allows controls from postMessages
if (__DEV__) {
// @ts-expect-error expose global
window.__slidev__ = context
useEmbeddedControl()
}
// User Setups
for (const setup of setups)
setup()
const {
clicksContext,
currentSlideNo,
hasPrimarySlide,
isNotesViewer,
isPresenter,
isPrintMode,
} = useNav()
useHead({
title: slidesTitle,
htmlAttrs: configs.htmlAttrs,
})
usePrintStyles()
initSharedState(`${slidesTitle} - shared`)
initDrawingState(`${slidesTitle} - drawings`)
const id = `${location.origin}_${makeId()}`
// update shared state
function updateSharedState() {
if (isNotesViewer.value || isPrintMode.value)
return
// we allow Presenter mode, or Viewer mode from trusted origins to update the shared state
if (!isPresenter.value && !TRUST_ORIGINS.includes(location.host.split(':')[0]))
return
if (isPresenter.value) {
patch('page', +currentSlideNo.value)
patch('clicks', clicksContext.value.current)
patch('clicksTotal', clicksContext.value.total)
}
else {
patch('viewerPage', +currentSlideNo.value)
patch('viewerClicks', clicksContext.value.current)
patch('viewerClicksTotal', clicksContext.value.total)
}
patch('lastUpdate', {
id,
type: isPresenter.value ? 'presenter' : 'viewer',
time: new Date().getTime(),
})
}
const router = useRouter()
router.afterEach(updateSharedState)
watch(clicksContext, updateSharedState)
onPatch((state) => {
if (!hasPrimarySlide.value || isPrintMode.value)
return
if (state.lastUpdate?.type === 'presenter' && (+state.page !== +currentSlideNo.value || +clicksContext.value.current !== +state.clicks)) {
skipTransition.value = false
router.replace({
path: getSlidePath(state.page, isPresenter.value),
query: {
...router.currentRoute.value.query,
clicks: state.clicks || 0,
},
})
}
})
}