@slidev/client 0.32.3 → 0.33.1
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.
- package/builtin/Mermaid.vue +1 -1
- package/builtin/Monaco.vue +0 -2
- package/builtin/RenderWhen.vue +29 -0
- package/constants.ts +2 -0
- package/internals/NavControls.vue +1 -1
- package/internals/NoteEditor.vue +8 -16
- package/internals/NoteStatic.vue +20 -0
- package/internals/NoteViewer.vue +26 -0
- package/internals/Play.vue +2 -2
- package/internals/Presenter.vue +34 -8
- package/internals/ShadowRoot.vue +7 -1
- package/internals/SlideWrapper.ts +6 -1
- package/internals/SlidesOverview.vue +1 -0
- package/internals/SlidesShow.vue +3 -0
- package/internals/VerticalDivider.vue +1 -1
- package/package.json +20 -20
- package/routes.ts +1 -0
- package/setup/shortcuts.ts +2 -1
- package/shim.d.ts +1 -3
- package/styles/index.css +7 -1
package/builtin/Mermaid.vue
CHANGED
package/builtin/Monaco.vue
CHANGED
|
@@ -74,11 +74,9 @@ onMounted(() => {
|
|
|
74
74
|
'allow-top-navigation-by-user-activation',
|
|
75
75
|
].join(' '))
|
|
76
76
|
|
|
77
|
-
/* eslint-disable no-undef */
|
|
78
77
|
frame.src = __DEV__
|
|
79
78
|
? `${location.origin}${__SLIDEV_CLIENT_ROOT__}/iframes/monaco/index.html`
|
|
80
79
|
: `${import.meta.env.BASE_URL}iframes/monaco/index.html`
|
|
81
|
-
/* eslint-enable no-undef */
|
|
82
80
|
|
|
83
81
|
frame.style.backgroundColor = 'transparent'
|
|
84
82
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, inject } from 'vue'
|
|
3
|
+
import type { RenderContext } from '@slidev/types'
|
|
4
|
+
|
|
5
|
+
import { injectionSlideContext } from '../constants'
|
|
6
|
+
|
|
7
|
+
type Context = 'main' | RenderContext
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
context: Context | Context[]
|
|
11
|
+
}>()
|
|
12
|
+
const { context } = props
|
|
13
|
+
|
|
14
|
+
const currentContext = inject(injectionSlideContext)
|
|
15
|
+
|
|
16
|
+
const shouldRender = computed(() => Array.isArray(context) ? context.some(contextMatch) : contextMatch(context))
|
|
17
|
+
|
|
18
|
+
function contextMatch(context: Context) {
|
|
19
|
+
if (context === currentContext)
|
|
20
|
+
return true
|
|
21
|
+
if (context === 'main' && (currentContext === 'slide' || currentContext === 'presenter'))
|
|
22
|
+
return true
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<slot v-if="shouldRender" />
|
|
29
|
+
</template>
|
package/constants.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ComputedRef, InjectionKey, Ref } from 'vue'
|
|
2
2
|
import type { RouteRecordRaw } from 'vue-router'
|
|
3
|
+
import type { RenderContext } from '@slidev/types'
|
|
3
4
|
import type { SlidevContext } from './modules/context'
|
|
4
5
|
|
|
5
6
|
export const injectionClicks: InjectionKey<Ref<number>> = Symbol('v-click-clicks')
|
|
@@ -9,6 +10,7 @@ export const injectionClicksDisabled: InjectionKey<Ref<boolean>> = Symbol('v-cli
|
|
|
9
10
|
export const injectionSlideScale: InjectionKey<ComputedRef<number>> = Symbol('slidev-slide-scale')
|
|
10
11
|
export const injectionSlidevContext: InjectionKey<SlidevContext> = Symbol('slidev-slidev-context')
|
|
11
12
|
export const injectionRoute: InjectionKey<RouteRecordRaw> = Symbol('slidev-route')
|
|
13
|
+
export const injectionSlideContext: InjectionKey<RenderContext> = Symbol('slidev-slide-context')
|
|
12
14
|
|
|
13
15
|
export const CLASS_VCLICK_TARGET = 'slidev-vclick-target'
|
|
14
16
|
export const CLASS_VCLICK_HIDDEN = 'slidev-vclick-hidden'
|
|
@@ -47,7 +47,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
47
47
|
<template>
|
|
48
48
|
<nav ref="root" class="flex flex-col">
|
|
49
49
|
<div
|
|
50
|
-
class="flex flex-wrap-reverse text-xl p-
|
|
50
|
+
class="flex flex-wrap-reverse text-xl gap-0.5 p-1 lg:(gap-1 p-2)"
|
|
51
51
|
:class="barStyle"
|
|
52
52
|
@mouseleave="onMouseLeave"
|
|
53
53
|
>
|
package/internals/NoteEditor.vue
CHANGED
|
@@ -3,6 +3,7 @@ import { ignorableWatch, onClickOutside } from '@vueuse/core'
|
|
|
3
3
|
import { nextTick, ref, watch } from 'vue'
|
|
4
4
|
import { currentSlideId } from '../logic/nav'
|
|
5
5
|
import { useDynamicSlideInfo } from '../logic/note'
|
|
6
|
+
import NoteViewer from './NoteViewer.vue'
|
|
6
7
|
|
|
7
8
|
const props = defineProps({
|
|
8
9
|
class: {
|
|
@@ -59,22 +60,13 @@ onClickOutside(input, () => {
|
|
|
59
60
|
</script>
|
|
60
61
|
|
|
61
62
|
<template>
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/>
|
|
70
|
-
<div
|
|
71
|
-
v-else
|
|
72
|
-
class="prose overflow-auto outline-none"
|
|
73
|
-
:class="props.class"
|
|
74
|
-
@click="switchNoteEdit"
|
|
75
|
-
v-text="note"
|
|
76
|
-
/>
|
|
77
|
-
</template>
|
|
63
|
+
<NoteViewer
|
|
64
|
+
v-if="!editing && note"
|
|
65
|
+
:class="props.class"
|
|
66
|
+
:note="note"
|
|
67
|
+
:note-html="info?.notesHTML"
|
|
68
|
+
@click="switchNoteEdit"
|
|
69
|
+
/>
|
|
78
70
|
<textarea
|
|
79
71
|
v-else
|
|
80
72
|
ref="input"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import { currentRoute } from '../logic/nav'
|
|
4
|
+
import NoteViewer from './NoteViewer.vue'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
class?: string
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const note = computed(() => currentRoute.value?.meta?.slide?.note)
|
|
11
|
+
const noteHtml = computed(() => currentRoute.value?.meta?.slide?.notesHTML)
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<NoteViewer
|
|
16
|
+
:class="props.class"
|
|
17
|
+
:note="note"
|
|
18
|
+
:note-html="noteHtml"
|
|
19
|
+
/>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const props = defineProps<{
|
|
3
|
+
class?: string
|
|
4
|
+
noteHtml?: string
|
|
5
|
+
note?: string
|
|
6
|
+
}>()
|
|
7
|
+
|
|
8
|
+
defineEmits(['click'])
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<div
|
|
13
|
+
v-if="noteHtml"
|
|
14
|
+
class="prose overflow-auto outline-none"
|
|
15
|
+
:class="props.class"
|
|
16
|
+
@click="$emit('click')"
|
|
17
|
+
v-html="noteHtml"
|
|
18
|
+
/>
|
|
19
|
+
<div
|
|
20
|
+
v-else
|
|
21
|
+
class="prose overflow-auto outline-none"
|
|
22
|
+
:class="props.class"
|
|
23
|
+
@click="$emit('click')"
|
|
24
|
+
v-text="note"
|
|
25
|
+
/>
|
|
26
|
+
</template>
|
package/internals/Play.vue
CHANGED
package/internals/Presenter.vue
CHANGED
|
@@ -14,6 +14,7 @@ import SlideContainer from './SlideContainer.vue'
|
|
|
14
14
|
import NavControls from './NavControls.vue'
|
|
15
15
|
import SlidesOverview from './SlidesOverview.vue'
|
|
16
16
|
import NoteEditor from './NoteEditor.vue'
|
|
17
|
+
import NoteStatic from './NoteStatic.vue'
|
|
17
18
|
import Goto from './Goto.vue'
|
|
18
19
|
import SlidesShow from './SlidesShow.vue'
|
|
19
20
|
import SlideWrapper from './SlideWrapper'
|
|
@@ -52,7 +53,7 @@ const nextSlide = computed(() => {
|
|
|
52
53
|
}
|
|
53
54
|
})
|
|
54
55
|
|
|
55
|
-
// sync presenter
|
|
56
|
+
// sync presenter cursor
|
|
56
57
|
onMounted(() => {
|
|
57
58
|
const slidesContainer = main.value!.querySelector('#slide-content')!
|
|
58
59
|
const mouse = reactive(useMouse())
|
|
@@ -83,7 +84,7 @@ onMounted(() => {
|
|
|
83
84
|
<div class="bg-main h-full slidev-presenter">
|
|
84
85
|
<div class="grid-container">
|
|
85
86
|
<div class="grid-section top flex">
|
|
86
|
-
<img src="../assets/logo-title-horizontal.png" class="h-
|
|
87
|
+
<img src="../assets/logo-title-horizontal.png" class="ml-2 my-auto h-10 py-1 lg:(h-14 py-2)">
|
|
87
88
|
<div class="flex-auto" />
|
|
88
89
|
<div
|
|
89
90
|
class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
|
|
@@ -97,17 +98,20 @@ onMounted(() => {
|
|
|
97
98
|
{{ timer }}
|
|
98
99
|
</div>
|
|
99
100
|
</div>
|
|
100
|
-
<div ref="main" class="grid-section main flex flex-col p-4" :style="themeVars">
|
|
101
|
+
<div ref="main" class="relative grid-section main flex flex-col p-2 lg:(p-4)" :style="themeVars">
|
|
101
102
|
<SlideContainer
|
|
102
103
|
key="main"
|
|
103
104
|
class="h-full w-full"
|
|
104
105
|
>
|
|
105
|
-
<template
|
|
106
|
-
<SlidesShow />
|
|
106
|
+
<template #default>
|
|
107
|
+
<SlidesShow context="presenter" />
|
|
107
108
|
</template>
|
|
108
109
|
</SlideContainer>
|
|
110
|
+
<div class="context">
|
|
111
|
+
current
|
|
112
|
+
</div>
|
|
109
113
|
</div>
|
|
110
|
-
<div class="grid-section next flex flex-col p-4">
|
|
114
|
+
<div class="relative grid-section next flex flex-col p-2 lg:(p-4)">
|
|
111
115
|
<SlideContainer
|
|
112
116
|
v-if="nextSlide"
|
|
113
117
|
key="next"
|
|
@@ -120,11 +124,16 @@ onMounted(() => {
|
|
|
120
124
|
:clicks-disabled="false"
|
|
121
125
|
:class="getSlideClass(nextSlide.route)"
|
|
122
126
|
:route="nextSlide.route"
|
|
127
|
+
context="previewNext"
|
|
123
128
|
/>
|
|
124
129
|
</SlideContainer>
|
|
130
|
+
<div class="context">
|
|
131
|
+
next
|
|
132
|
+
</div>
|
|
125
133
|
</div>
|
|
126
134
|
<div class="grid-section note overflow-auto">
|
|
127
|
-
<NoteEditor class="w-full h-full p-
|
|
135
|
+
<NoteEditor v-if="__DEV__" class="w-full h-full overflow-auto p-2 lg:(p-4)" />
|
|
136
|
+
<NoteStatic v-else class="w-full h-full overflow-auto p-2 lg:(p-4)" />
|
|
128
137
|
</div>
|
|
129
138
|
<div class="grid-section bottom">
|
|
130
139
|
<NavControls :persist="true" />
|
|
@@ -173,7 +182,20 @@ onMounted(() => {
|
|
|
173
182
|
"bottom bottom";
|
|
174
183
|
}
|
|
175
184
|
|
|
176
|
-
@
|
|
185
|
+
@media (max-aspect-ratio: 3/5) {
|
|
186
|
+
.grid-container {
|
|
187
|
+
grid-template-columns: 1fr;
|
|
188
|
+
grid-template-rows: min-content 1fr 1fr 1fr min-content;
|
|
189
|
+
grid-template-areas:
|
|
190
|
+
"top"
|
|
191
|
+
"main"
|
|
192
|
+
"note"
|
|
193
|
+
"next"
|
|
194
|
+
"bottom";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@media (min-aspect-ratio: 1/1) {
|
|
177
199
|
.grid-container {
|
|
178
200
|
grid-template-columns: 1fr 1.1fr 0.9fr;
|
|
179
201
|
grid-template-rows: min-content 1fr 2fr min-content;
|
|
@@ -208,4 +230,8 @@ onMounted(() => {
|
|
|
208
230
|
grid-area: bottom;
|
|
209
231
|
}
|
|
210
232
|
}
|
|
233
|
+
|
|
234
|
+
.context {
|
|
235
|
+
@apply absolute top-0 left-0 px-1 text-xs bg-gray-400 bg-opacity-50 opacity-75 rounded-br-md;
|
|
236
|
+
}
|
|
211
237
|
</style>
|
package/internals/ShadowRoot.vue
CHANGED
|
@@ -5,11 +5,17 @@ const props = defineProps<{
|
|
|
5
5
|
innerHtml: string
|
|
6
6
|
}>()
|
|
7
7
|
|
|
8
|
+
const emit = defineEmits<{
|
|
9
|
+
(event: 'shadow', div: ShadowRoot): void
|
|
10
|
+
}>()
|
|
11
|
+
|
|
8
12
|
const el = ref<HTMLDivElement>()
|
|
9
13
|
const shadow = computed(() => el.value ? (el.value.shadowRoot || el.value.attachShadow({ mode: 'open' })) : null)
|
|
10
14
|
watchEffect(() => {
|
|
11
|
-
if (shadow.value)
|
|
15
|
+
if (shadow.value) {
|
|
16
|
+
emit('shadow', shadow.value)
|
|
12
17
|
shadow.value.innerHTML = props.innerHtml
|
|
18
|
+
}
|
|
13
19
|
})
|
|
14
20
|
</script>
|
|
15
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useVModel } from '@vueuse/core'
|
|
2
2
|
import { defineComponent, h, provide } from 'vue'
|
|
3
|
-
import { injectionClicks, injectionClicksDisabled, injectionClicksElements, injectionOrderMap, injectionRoute } from '../constants'
|
|
3
|
+
import { injectionClicks, injectionClicksDisabled, injectionClicksElements, injectionOrderMap, injectionRoute, injectionSlideContext } from '../constants'
|
|
4
4
|
|
|
5
5
|
export default defineComponent({
|
|
6
6
|
props: {
|
|
@@ -20,6 +20,10 @@ export default defineComponent({
|
|
|
20
20
|
type: Boolean,
|
|
21
21
|
default: false,
|
|
22
22
|
},
|
|
23
|
+
context: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: 'main',
|
|
26
|
+
},
|
|
23
27
|
is: {
|
|
24
28
|
type: Object,
|
|
25
29
|
default: undefined,
|
|
@@ -38,6 +42,7 @@ export default defineComponent({
|
|
|
38
42
|
clicksElements.value.length = 0
|
|
39
43
|
|
|
40
44
|
provide(injectionRoute, props.route)
|
|
45
|
+
provide(injectionSlideContext, props.context)
|
|
41
46
|
provide(injectionClicks, clicks)
|
|
42
47
|
provide(injectionClicksDisabled, clicksDisabled)
|
|
43
48
|
provide(injectionClicksElements, clicksElements)
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -9,6 +9,8 @@ import GlobalTop from '/@slidev/global-components/top'
|
|
|
9
9
|
import GlobalBottom from '/@slidev/global-components/bottom'
|
|
10
10
|
import PresenterMouse from './PresenterMouse.vue'
|
|
11
11
|
|
|
12
|
+
defineProps<{ context: 'slide' | 'presenter' }>()
|
|
13
|
+
|
|
12
14
|
// preload next route
|
|
13
15
|
watch(currentRoute, () => {
|
|
14
16
|
if (currentRoute.value?.meta && currentRoute.value.meta.preload !== false)
|
|
@@ -37,6 +39,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
|
|
|
37
39
|
:clicks-disabled="false"
|
|
38
40
|
:class="getSlideClass(route)"
|
|
39
41
|
:route="route"
|
|
42
|
+
:context="context"
|
|
40
43
|
/>
|
|
41
44
|
</template>
|
|
42
45
|
|
package/package.json
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.1",
|
|
4
4
|
"description": "Presentation slides for developers",
|
|
5
|
-
"homepage": "https://sli.dev",
|
|
6
|
-
"bugs": "https://github.com/slidevjs/slidev/issues",
|
|
7
|
-
"license": "MIT",
|
|
8
5
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"funding": "https://github.com/sponsors/antfu",
|
|
8
|
+
"homepage": "https://sli.dev",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "https://github.com/slidevjs/slidev"
|
|
12
12
|
},
|
|
13
|
-
"
|
|
13
|
+
"bugs": "https://github.com/slidevjs/slidev/issues",
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=14.0.0"
|
|
16
|
+
},
|
|
14
17
|
"dependencies": {
|
|
15
18
|
"@antfu/utils": "^0.5.2",
|
|
16
|
-
"@slidev/parser": "0.
|
|
17
|
-
"@slidev/types": "0.
|
|
18
|
-
"@vueuse/core": "^8.
|
|
19
|
+
"@slidev/parser": "0.33.1",
|
|
20
|
+
"@slidev/types": "0.33.1",
|
|
21
|
+
"@vueuse/core": "^8.7.4",
|
|
19
22
|
"@vueuse/head": "^0.7.6",
|
|
20
23
|
"@vueuse/motion": "^2.0.0-beta.18",
|
|
21
|
-
"codemirror": "^5.65.
|
|
24
|
+
"codemirror": "^5.65.5",
|
|
22
25
|
"defu": "^6.0.0",
|
|
23
26
|
"drauu": "^0.3.0",
|
|
24
27
|
"file-saver": "^2.0.5",
|
|
25
28
|
"js-base64": "^3.7.2",
|
|
26
29
|
"js-yaml": "^4.1.0",
|
|
27
|
-
"katex": "^0.
|
|
28
|
-
"mermaid": "^9.1.
|
|
30
|
+
"katex": "^0.16.0",
|
|
31
|
+
"mermaid": "^9.1.2",
|
|
29
32
|
"monaco-editor": "^0.33.0",
|
|
30
|
-
"nanoid": "^
|
|
31
|
-
"prettier": "^2.
|
|
33
|
+
"nanoid": "^4.0.0",
|
|
34
|
+
"prettier": "^2.7.1",
|
|
32
35
|
"recordrtc": "^5.6.2",
|
|
33
|
-
"resolve": "^1.22.
|
|
36
|
+
"resolve": "^1.22.1",
|
|
34
37
|
"vite-plugin-windicss": "^1.8.4",
|
|
35
|
-
"vue": "^3.2.
|
|
36
|
-
"vue-router": "^4.0.
|
|
37
|
-
"vue-starport": "^0.
|
|
38
|
+
"vue": "^3.2.36",
|
|
39
|
+
"vue-router": "^4.0.16",
|
|
40
|
+
"vue-starport": "^0.3.0",
|
|
38
41
|
"windicss": "^3.5.4"
|
|
39
|
-
},
|
|
40
|
-
"engines": {
|
|
41
|
-
"node": ">=14.0.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/routes.ts
CHANGED
package/setup/shortcuts.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* __imports__ */
|
|
2
2
|
|
|
3
3
|
import type { NavOperations, ShortcutOptions } from '@slidev/types'
|
|
4
|
-
import { downloadPDF, next, nextSlide, prev, prevSlide } from '../logic/nav'
|
|
4
|
+
import { downloadPDF, go, next, nextSlide, prev, prevSlide } from '../logic/nav'
|
|
5
5
|
import { toggleDark } from '../logic/dark'
|
|
6
6
|
import { showGotoDialog, showOverview, toggleOverview } from '../state'
|
|
7
7
|
import { drawingEnabled } from '../logic/drawings'
|
|
@@ -14,6 +14,7 @@ export default function setupShortcuts() {
|
|
|
14
14
|
prev,
|
|
15
15
|
nextSlide,
|
|
16
16
|
prevSlide,
|
|
17
|
+
go,
|
|
17
18
|
downloadPDF,
|
|
18
19
|
toggleDark,
|
|
19
20
|
toggleOverview,
|
package/shim.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/* eslint-disable import/no-duplicates */
|
|
2
|
-
|
|
3
1
|
declare interface Window {
|
|
4
2
|
// extend the window
|
|
5
3
|
}
|
|
6
4
|
|
|
7
|
-
// with vite-plugin-
|
|
5
|
+
// with vite-plugin-vue-markdown, markdowns can be treat as Vue components
|
|
8
6
|
declare module '*.md' {
|
|
9
7
|
import type { ComponentOptions } from 'vue'
|
|
10
8
|
const component: ComponentOptions
|
package/styles/index.css
CHANGED
|
@@ -16,10 +16,16 @@ html {
|
|
|
16
16
|
|
|
17
17
|
.icon-btn {
|
|
18
18
|
@apply inline-block cursor-pointer select-none !outline-none;
|
|
19
|
-
@apply opacity-75 transition duration-200 ease-in-out align-middle rounded p-
|
|
19
|
+
@apply opacity-75 transition duration-200 ease-in-out align-middle rounded p-1;
|
|
20
20
|
@apply hover:(opacity-100 bg-gray-400 bg-opacity-10);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
@screen md {
|
|
24
|
+
.icon-btn {
|
|
25
|
+
@apply p-2;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
.icon-btn.shallow {
|
|
24
30
|
@apply opacity-30
|
|
25
31
|
}
|