vitepress-theme-element-plus 1.3.2 → 1.4.0
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.
|
@@ -4,11 +4,14 @@ import { computed } from 'vue'
|
|
|
4
4
|
import { useSidebar } from '../hooks/useSidebar'
|
|
5
5
|
import VPDocAside from './DocAside.vue'
|
|
6
6
|
import VPDocFooter from './DocFooter.vue'
|
|
7
|
+
import MobilePreviewFrame from './MobilePreviewFrame.vue'
|
|
7
8
|
|
|
8
|
-
const { theme } = useData()
|
|
9
|
+
const { theme, frontmatter } = useData()
|
|
9
10
|
|
|
10
11
|
const route = useRoute()
|
|
11
12
|
const { hasAside, leftAside, hasSidebar } = useSidebar()
|
|
13
|
+
const hasMobilePreview = computed(() => typeof frontmatter.value.mobileDemo === 'string' && frontmatter.value.mobileDemo.trim().length > 0)
|
|
14
|
+
const showAside = computed(() => hasAside.value)
|
|
12
15
|
|
|
13
16
|
const pageName = computed(() =>
|
|
14
17
|
route.path.replace(/[./]+/g, '_').replace(/_html$/, ''),
|
|
@@ -16,10 +19,16 @@ const pageName = computed(() =>
|
|
|
16
19
|
</script>
|
|
17
20
|
|
|
18
21
|
<template>
|
|
19
|
-
<div class="VPDoc" :class="{ 'has-sidebar': hasSidebar, 'has-aside':
|
|
22
|
+
<div class="VPDoc" :class="{ 'has-sidebar': hasSidebar, 'has-aside': showAside, 'has-mobile-preview': hasMobilePreview }">
|
|
20
23
|
<slot name="doc-top" />
|
|
21
24
|
<div class="container">
|
|
22
|
-
<div v-if="
|
|
25
|
+
<div v-if="hasMobilePreview" class="preview">
|
|
26
|
+
<div class="preview-container">
|
|
27
|
+
<MobilePreviewFrame />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div v-if="showAside" class="aside" :class="{ 'left-aside': leftAside }">
|
|
23
32
|
<div class="aside-container">
|
|
24
33
|
<div class="aside-content">
|
|
25
34
|
<VPDocAside>
|
|
@@ -112,9 +121,17 @@ const pageName = computed(() =>
|
|
|
112
121
|
justify-content: center;
|
|
113
122
|
}
|
|
114
123
|
|
|
124
|
+
.VPDoc.has-mobile-preview .container {
|
|
125
|
+
gap: 32px;
|
|
126
|
+
}
|
|
127
|
+
|
|
115
128
|
.VPDoc .aside {
|
|
116
129
|
display: block;
|
|
117
130
|
}
|
|
131
|
+
|
|
132
|
+
.VPDoc.has-aside.has-mobile-preview .aside {
|
|
133
|
+
display: none;
|
|
134
|
+
}
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
@media (min-width: 1440px) {
|
|
@@ -132,6 +149,17 @@ const pageName = computed(() =>
|
|
|
132
149
|
width: 100%;
|
|
133
150
|
}
|
|
134
151
|
|
|
152
|
+
.preview {
|
|
153
|
+
margin-bottom: 24px;
|
|
154
|
+
order: 2;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.preview-container {
|
|
158
|
+
width: 100%;
|
|
159
|
+
position: sticky;
|
|
160
|
+
top: calc(var(--vp-nav-height) + 32px);
|
|
161
|
+
}
|
|
162
|
+
|
|
135
163
|
.aside {
|
|
136
164
|
position: relative;
|
|
137
165
|
display: none;
|
|
@@ -179,6 +207,12 @@ const pageName = computed(() =>
|
|
|
179
207
|
}
|
|
180
208
|
}
|
|
181
209
|
|
|
210
|
+
@media (min-width: 1680px) {
|
|
211
|
+
.VPDoc.has-aside.has-mobile-preview .aside {
|
|
212
|
+
display: block;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
182
216
|
.content-container {
|
|
183
217
|
margin: 0 auto;
|
|
184
218
|
}
|
|
@@ -8,10 +8,12 @@ import { computed, provide, useSlots, watch } from 'vue'
|
|
|
8
8
|
import { useCloseSidebarOnEscape } from '../hooks/useSidebar'
|
|
9
9
|
import Content from './Content.vue'
|
|
10
10
|
import LocalNav from './LocalNav.vue'
|
|
11
|
+
import MobilePreviewLayout from './MobilePreviewLayout.vue'
|
|
11
12
|
import Nav from './Nav.vue'
|
|
12
13
|
import Sidebar from './Sidebar.vue'
|
|
13
14
|
|
|
14
15
|
const { frontmatter } = useData()
|
|
16
|
+
const isStandaloneMobilePreview = computed(() => frontmatter.value.layout === 'mobile-preview')
|
|
15
17
|
|
|
16
18
|
useCloseSidebarOnEscape()
|
|
17
19
|
const {
|
|
@@ -32,7 +34,8 @@ provide(layoutInfoInjectionKey, heroImageSlotExists)
|
|
|
32
34
|
</script>
|
|
33
35
|
|
|
34
36
|
<template>
|
|
35
|
-
<
|
|
37
|
+
<MobilePreviewLayout v-if="isStandaloneMobilePreview" />
|
|
38
|
+
<div v-else-if="frontmatter.layout !== false" class="Layout VMLayout" :class="frontmatter.pageClass">
|
|
36
39
|
<slot name="layout-top" />
|
|
37
40
|
<VPSkipLink />
|
|
38
41
|
<VPBackdrop class="backdrop" :show="isSidebarOpen" @click="closeSidebar()" />
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useData, useRoute } from 'vitepress'
|
|
3
|
+
import { computed, ref, watch } from 'vue'
|
|
4
|
+
import { resolveMobilePreviewId } from '../mobile-preview'
|
|
5
|
+
|
|
6
|
+
const { frontmatter, isDark, theme } = useData()
|
|
7
|
+
const route = useRoute()
|
|
8
|
+
const frameRef = ref<HTMLIFrameElement>()
|
|
9
|
+
const previewConfig = computed(() => theme.value.mobilePreview ?? {})
|
|
10
|
+
|
|
11
|
+
const demoId = computed(() => {
|
|
12
|
+
const value = frontmatter.value.mobileDemo
|
|
13
|
+
return typeof value === 'string'
|
|
14
|
+
? resolveMobilePreviewId(value, previewConfig.value.demoRoot)
|
|
15
|
+
: ''
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const frameWidth = computed(() => `${previewConfig.value.deviceWidth ?? 390}px`)
|
|
19
|
+
const viewportHeight = computed(() => `${previewConfig.value.deviceHeight ?? 760}px`)
|
|
20
|
+
|
|
21
|
+
const previewHref = computed(() => {
|
|
22
|
+
const previewPath = normalizePreviewPath(previewConfig.value.previewPath)
|
|
23
|
+
const search = new URLSearchParams({
|
|
24
|
+
demo: demoId.value,
|
|
25
|
+
theme: isDark.value ? 'dark' : 'light',
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
return `${resolveLocalePreviewPath(route.path, previewPath)}?${search.toString()}`
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const frameStyle = computed(() => ({
|
|
32
|
+
'--vp-mobile-preview-width': frameWidth.value,
|
|
33
|
+
'--vp-mobile-preview-height': viewportHeight.value,
|
|
34
|
+
}))
|
|
35
|
+
|
|
36
|
+
function normalizePreviewPath(value: unknown): string {
|
|
37
|
+
if (typeof value !== 'string' || !value.trim())
|
|
38
|
+
return 'preview/'
|
|
39
|
+
|
|
40
|
+
return value
|
|
41
|
+
.trim()
|
|
42
|
+
.replace(/^\/+/, '')
|
|
43
|
+
.replace(/\/?$/, '/')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolveLocalePreviewPath(path: string, previewPath: string): string {
|
|
47
|
+
const matchedLocale = path.match(/^\/([^/]+)\//)
|
|
48
|
+
const localePrefix = matchedLocale ? `/${matchedLocale[1]}/` : '/'
|
|
49
|
+
|
|
50
|
+
return `${localePrefix}${previewPath}`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function syncTheme(): void {
|
|
54
|
+
frameRef.value?.contentWindow?.postMessage({
|
|
55
|
+
type: 'vp-mobile-preview-theme',
|
|
56
|
+
value: isDark.value ? 'dark' : 'light',
|
|
57
|
+
}, '*')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
watch(isDark, syncTheme)
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<section v-if="demoId" class="VPMobilePreviewFrame" :style="frameStyle">
|
|
65
|
+
<div class="preview-actions">
|
|
66
|
+
<span class="preview-actions__label">Mobile Preview</span>
|
|
67
|
+
<a class="preview-actions__link" :href="previewHref" target="_blank" rel="noreferrer">
|
|
68
|
+
Open
|
|
69
|
+
</a>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="preview-phone">
|
|
73
|
+
<div class="preview-phone__camera" />
|
|
74
|
+
<iframe
|
|
75
|
+
ref="frameRef"
|
|
76
|
+
class="preview-phone__viewport"
|
|
77
|
+
:src="previewHref"
|
|
78
|
+
title="Mobile demo preview"
|
|
79
|
+
loading="lazy"
|
|
80
|
+
@load="syncTheme"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<a class="preview-mobile-link" :href="previewHref" target="_blank" rel="noreferrer">
|
|
85
|
+
Open mobile preview
|
|
86
|
+
</a>
|
|
87
|
+
</section>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<style scoped lang="scss">
|
|
91
|
+
.preview-actions {
|
|
92
|
+
display: none;
|
|
93
|
+
align-items: center;
|
|
94
|
+
justify-content: space-between;
|
|
95
|
+
margin-bottom: 12px;
|
|
96
|
+
padding: 0 4px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.preview-actions__label {
|
|
100
|
+
color: var(--vp-c-text-2);
|
|
101
|
+
font-size: 12px;
|
|
102
|
+
font-weight: 600;
|
|
103
|
+
letter-spacing: 0.08em;
|
|
104
|
+
text-transform: uppercase;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.preview-actions__link,
|
|
108
|
+
.preview-mobile-link {
|
|
109
|
+
color: var(--vp-c-brand-1);
|
|
110
|
+
font-size: 13px;
|
|
111
|
+
font-weight: 600;
|
|
112
|
+
text-decoration: none;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.preview-phone {
|
|
116
|
+
display: none;
|
|
117
|
+
position: relative;
|
|
118
|
+
width: var(--vp-mobile-preview-width);
|
|
119
|
+
padding: 14px 10px;
|
|
120
|
+
background: linear-gradient(180deg, rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.64)), rgba(255, 255, 255, 0.7);
|
|
121
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
122
|
+
border-radius: 32px;
|
|
123
|
+
box-shadow:
|
|
124
|
+
0 24px 80px rgba(15, 23, 42, 0.12),
|
|
125
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
|
126
|
+
backdrop-filter: blur(20px);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
:global(.dark) .preview-phone {
|
|
130
|
+
background: linear-gradient(180deg, rgba(24, 24, 27, 0.92), rgba(24, 24, 27, 0.8)), rgba(24, 24, 27, 0.86);
|
|
131
|
+
border-color: rgba(255, 255, 255, 0.12);
|
|
132
|
+
box-shadow:
|
|
133
|
+
0 24px 80px rgba(0, 0, 0, 0.45),
|
|
134
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.preview-phone__camera {
|
|
138
|
+
position: absolute;
|
|
139
|
+
top: 8px;
|
|
140
|
+
left: 50%;
|
|
141
|
+
width: 104px;
|
|
142
|
+
height: 18px;
|
|
143
|
+
border-radius: 999px;
|
|
144
|
+
background: rgba(15, 23, 42, 0.92);
|
|
145
|
+
transform: translateX(-50%);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.preview-phone__viewport {
|
|
149
|
+
display: block;
|
|
150
|
+
width: 100%;
|
|
151
|
+
height: var(--vp-mobile-preview-height);
|
|
152
|
+
overflow: hidden;
|
|
153
|
+
background: var(--vp-c-bg);
|
|
154
|
+
border: 0;
|
|
155
|
+
border-radius: 24px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.preview-mobile-link {
|
|
159
|
+
display: inline-flex;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@media (min-width: 1440px) {
|
|
163
|
+
.preview-actions {
|
|
164
|
+
display: flex;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.preview-phone {
|
|
168
|
+
display: block;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.preview-mobile-link {
|
|
172
|
+
display: none;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
</style>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Component } from 'vue'
|
|
3
|
+
import { useData } from 'vitepress'
|
|
4
|
+
import { computed, inject, markRaw, onBeforeUnmount, onMounted, ref, shallowRef } from 'vue'
|
|
5
|
+
import { mobilePreviewRegistryKey, resolveMobilePreviewId } from '../mobile-preview'
|
|
6
|
+
|
|
7
|
+
const { frontmatter, theme } = useData()
|
|
8
|
+
const registry = inject(mobilePreviewRegistryKey)
|
|
9
|
+
const demoId = ref('')
|
|
10
|
+
const errorMessage = ref('')
|
|
11
|
+
const isLoading = ref(true)
|
|
12
|
+
const demoComponent = shallowRef<Component>()
|
|
13
|
+
const previewConfig = computed(() => theme.value.mobilePreview ?? {})
|
|
14
|
+
|
|
15
|
+
const statusMessage = computed(() => {
|
|
16
|
+
if (errorMessage.value)
|
|
17
|
+
return errorMessage.value
|
|
18
|
+
|
|
19
|
+
return isLoading.value ? 'Loading mobile preview...' : 'No preview loaded.'
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
function applyTheme(theme: string | null): void {
|
|
23
|
+
if (typeof document === 'undefined')
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
document.documentElement.classList.toggle('dark', theme === 'dark')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function loadDemo(value: string | null): Promise<void> {
|
|
30
|
+
demoId.value = resolveMobilePreviewId(value ?? '', previewConfig.value.demoRoot)
|
|
31
|
+
demoComponent.value = undefined
|
|
32
|
+
errorMessage.value = ''
|
|
33
|
+
isLoading.value = true
|
|
34
|
+
|
|
35
|
+
if (!demoId.value) {
|
|
36
|
+
errorMessage.value = 'Missing demo id.'
|
|
37
|
+
isLoading.value = false
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!registry) {
|
|
42
|
+
errorMessage.value = 'No mobile preview registry was provided.'
|
|
43
|
+
isLoading.value = false
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const loader = registry[demoId.value]
|
|
48
|
+
if (!loader) {
|
|
49
|
+
errorMessage.value = `Unknown demo: ${demoId.value}`
|
|
50
|
+
isLoading.value = false
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const module = await loader()
|
|
56
|
+
const resolvedComponent = (module as { default?: Component }).default ?? module
|
|
57
|
+
demoComponent.value = markRaw(resolvedComponent as Component)
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
errorMessage.value = error instanceof Error
|
|
61
|
+
? error.message
|
|
62
|
+
: 'Failed to load the requested demo.'
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
isLoading.value = false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function syncFromLocation(): void {
|
|
70
|
+
if (typeof window === 'undefined')
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
const search = new URLSearchParams(window.location.search)
|
|
74
|
+
const fallbackDemo = typeof frontmatter.value.mobileDemo === 'string'
|
|
75
|
+
? frontmatter.value.mobileDemo
|
|
76
|
+
: null
|
|
77
|
+
applyTheme(search.get('theme'))
|
|
78
|
+
void loadDemo(search.get('demo') ?? fallbackDemo)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function handleMessage(event: MessageEvent): void {
|
|
82
|
+
if (event.data?.type !== 'vp-mobile-preview-theme')
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
applyTheme(event.data.value)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onMounted(() => {
|
|
89
|
+
syncFromLocation()
|
|
90
|
+
window.addEventListener('message', handleMessage)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
onBeforeUnmount(() => {
|
|
94
|
+
window.removeEventListener('message', handleMessage)
|
|
95
|
+
})
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<template>
|
|
99
|
+
<main class="VPMobilePreviewLayout">
|
|
100
|
+
<component :is="demoComponent" v-if="demoComponent" />
|
|
101
|
+
<div v-else class="preview-status">
|
|
102
|
+
<p class="preview-status__title">
|
|
103
|
+
Mobile Preview
|
|
104
|
+
</p>
|
|
105
|
+
<p class="preview-status__body">
|
|
106
|
+
{{ statusMessage }}
|
|
107
|
+
</p>
|
|
108
|
+
</div>
|
|
109
|
+
</main>
|
|
110
|
+
</template>
|
|
111
|
+
|
|
112
|
+
<style scoped lang="scss">
|
|
113
|
+
.VPMobilePreviewLayout {
|
|
114
|
+
min-height: 100%;
|
|
115
|
+
height: 100vh;
|
|
116
|
+
overflow-x: hidden;
|
|
117
|
+
overflow-y: auto;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.preview-status {
|
|
121
|
+
display: grid;
|
|
122
|
+
place-items: center;
|
|
123
|
+
align-content: center;
|
|
124
|
+
min-height: 100vh;
|
|
125
|
+
padding: 24px;
|
|
126
|
+
color: var(--vp-c-text-2);
|
|
127
|
+
text-align: center;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.preview-status__title {
|
|
131
|
+
margin: 0 0 12px;
|
|
132
|
+
color: var(--vp-c-text-1);
|
|
133
|
+
font-weight: 600;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.preview-status__body {
|
|
137
|
+
margin: 0;
|
|
138
|
+
font-size: 14px;
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { InjectionKey } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type MobilePreviewModule = () => Promise<unknown>
|
|
4
|
+
|
|
5
|
+
export type MobilePreviewRegistry = Record<string, MobilePreviewModule>
|
|
6
|
+
|
|
7
|
+
export interface EPMobilePreviewConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Locale-relative path of the standalone preview page.
|
|
10
|
+
*
|
|
11
|
+
* @default '/preview/'
|
|
12
|
+
*/
|
|
13
|
+
previewPath?: string
|
|
14
|
+
/**
|
|
15
|
+
* Outer device frame width used in the doc layout.
|
|
16
|
+
*
|
|
17
|
+
* @default 390
|
|
18
|
+
*/
|
|
19
|
+
deviceWidth?: number
|
|
20
|
+
/**
|
|
21
|
+
* Inner viewport height used in the doc layout.
|
|
22
|
+
*
|
|
23
|
+
* @default 760
|
|
24
|
+
*/
|
|
25
|
+
deviceHeight?: number
|
|
26
|
+
/**
|
|
27
|
+
* Root directory used to resolve the `mobileDemo` frontmatter field.
|
|
28
|
+
*
|
|
29
|
+
* @default 'demo/'
|
|
30
|
+
*/
|
|
31
|
+
demoRoot?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const mobilePreviewRegistryKey: InjectionKey<MobilePreviewRegistry> = Symbol('vitepress-theme-element-plus.mobile-preview-registry')
|
|
35
|
+
|
|
36
|
+
export function normalizeMobilePreviewId(value: string): string {
|
|
37
|
+
return value
|
|
38
|
+
.trim()
|
|
39
|
+
.replace(/\\/g, '/')
|
|
40
|
+
.replace(/^\.?\//, '')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function normalizeMobilePreviewRoot(value: string | undefined): string {
|
|
44
|
+
if (!value?.trim())
|
|
45
|
+
return 'demo/'
|
|
46
|
+
|
|
47
|
+
return normalizeMobilePreviewId(value)
|
|
48
|
+
.replace(/\/?$/, '/')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function resolveMobilePreviewId(value: string | undefined, root: string | undefined): string {
|
|
52
|
+
const normalizedId = normalizeMobilePreviewId(value ?? '')
|
|
53
|
+
if (!normalizedId)
|
|
54
|
+
return ''
|
|
55
|
+
|
|
56
|
+
const normalizedRoot = normalizeMobilePreviewRoot(root)
|
|
57
|
+
return normalizedId.startsWith(normalizedRoot)
|
|
58
|
+
? normalizedId
|
|
59
|
+
: `${normalizedRoot}${normalizedId}`
|
|
60
|
+
}
|
package/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DefaultTheme, Theme } from 'vitepress'
|
|
2
|
+
import type { EPMobilePreviewConfig, MobilePreviewModule, MobilePreviewRegistry } from './client/mobile-preview'
|
|
2
3
|
import VPTheme from 'vitepress/theme'
|
|
3
4
|
import Layout from './client/components/Layout.vue'
|
|
4
5
|
import 'element-plus/theme-chalk/base.css'
|
|
@@ -37,9 +38,20 @@ export interface EPThemeConfig extends DefaultTheme.Config {
|
|
|
37
38
|
* 文档版本号
|
|
38
39
|
*/
|
|
39
40
|
version?: string
|
|
41
|
+
/**
|
|
42
|
+
* 移动端预览配置
|
|
43
|
+
*/
|
|
44
|
+
mobilePreview?: EPMobilePreviewConfig
|
|
40
45
|
footer?: EPThemeFooter
|
|
41
46
|
}
|
|
42
47
|
// #endregion snippet
|
|
43
48
|
|
|
44
49
|
export { Layout }
|
|
50
|
+
export {
|
|
51
|
+
mobilePreviewRegistryKey,
|
|
52
|
+
normalizeMobilePreviewId,
|
|
53
|
+
normalizeMobilePreviewRoot,
|
|
54
|
+
resolveMobilePreviewId,
|
|
55
|
+
} from './client/mobile-preview'
|
|
56
|
+
export type { EPMobilePreviewConfig, MobilePreviewModule, MobilePreviewRegistry }
|
|
45
57
|
export default EPTheme
|