@slidev/client 0.48.0-beta.13 → 0.48.0-beta.14
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/composables/useClicks.ts +4 -2
- package/internals/Controls.vue +2 -2
- package/internals/IconButton.vue +3 -2
- package/internals/NavControls.vue +2 -2
- package/internals/NoteDisplay.vue +12 -2
- package/internals/NoteEditor.vue +5 -7
- package/internals/{SlidesOverview.vue → QuickOverview.vue} +10 -9
- package/internals/RecordingControls.vue +1 -1
- package/package.json +3 -3
- package/pages/notes.vue +1 -0
- package/pages/overview.vue +3 -2
- package/pages/presenter/print.vue +4 -1
- package/pages/presenter.vue +35 -41
package/composables/useClicks.ts
CHANGED
|
@@ -69,7 +69,9 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
|
|
|
69
69
|
const thisPath = +(route?.path ?? CLICKS_MAX)
|
|
70
70
|
const current = computed({
|
|
71
71
|
get() {
|
|
72
|
-
const currentPath = +(currentRoute.value?.path ??
|
|
72
|
+
const currentPath = +(currentRoute.value?.path ?? Number.NaN)
|
|
73
|
+
if (!currentPath || Number.isNaN(currentPath))
|
|
74
|
+
return 0
|
|
73
75
|
if (currentPath === thisPath)
|
|
74
76
|
return queryClicks.value
|
|
75
77
|
else if (currentPath > thisPath)
|
|
@@ -78,7 +80,7 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
|
|
|
78
80
|
return 0
|
|
79
81
|
},
|
|
80
82
|
set(v) {
|
|
81
|
-
const currentPath = +(currentRoute.value?.path ??
|
|
83
|
+
const currentPath = +(currentRoute.value?.path ?? Number.NaN)
|
|
82
84
|
if (currentPath === thisPath)
|
|
83
85
|
queryClicks.value = v
|
|
84
86
|
},
|
package/internals/Controls.vue
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { shallowRef } from 'vue'
|
|
3
3
|
import { showInfoDialog, showOverview, showRecordingDialog } from '../state'
|
|
4
4
|
import { configs } from '../env'
|
|
5
|
-
import
|
|
5
|
+
import QuickOverview from './QuickOverview.vue'
|
|
6
6
|
import InfoDialog from './InfoDialog.vue'
|
|
7
7
|
import Goto from './Goto.vue'
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ if (__SLIDEV_FEATURE_RECORD__) {
|
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
|
-
<
|
|
18
|
+
<QuickOverview v-model="showOverview" />
|
|
19
19
|
<Goto />
|
|
20
20
|
<WebCamera v-if="WebCamera" />
|
|
21
21
|
<RecordingDialog v-if="RecordingDialog" v-model="showRecordingDialog" />
|
package/internals/IconButton.vue
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
defineProps<{
|
|
3
3
|
title: string
|
|
4
4
|
icon?: string
|
|
5
|
+
as?: string
|
|
5
6
|
}>()
|
|
6
7
|
</script>
|
|
7
8
|
|
|
8
9
|
<template>
|
|
9
|
-
<button class="slidev-icon-btn" :title="title" v-bind="$attrs">
|
|
10
|
+
<component :is="as || 'button'" class="slidev-icon-btn" :title="title" v-bind="$attrs">
|
|
10
11
|
<span class="sr-only">{{ title }}</span>
|
|
11
12
|
<slot>
|
|
12
13
|
<div :class="icon" />
|
|
13
14
|
</slot>
|
|
14
|
-
</
|
|
15
|
+
</component>
|
|
15
16
|
</template>
|
|
@@ -114,13 +114,13 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
114
114
|
<IconButton
|
|
115
115
|
v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__"
|
|
116
116
|
:title="showEditor ? 'Hide editor' : 'Show editor'"
|
|
117
|
-
class="
|
|
117
|
+
class="lt-md:hidden"
|
|
118
118
|
@click="showEditor = !showEditor"
|
|
119
119
|
>
|
|
120
120
|
<carbon:text-annotation-toggle />
|
|
121
121
|
</IconButton>
|
|
122
122
|
|
|
123
|
-
<IconButton v-if="isPresenter" title="Toggle Presenter Layout" @click="togglePresenterLayout">
|
|
123
|
+
<IconButton v-if="isPresenter" title="Toggle Presenter Layout" class="aspect-ratio-initial" @click="togglePresenterLayout">
|
|
124
124
|
<carbon:template />
|
|
125
125
|
{{ presenterLayout }}
|
|
126
126
|
</IconButton>
|
|
@@ -9,6 +9,7 @@ const props = defineProps<{
|
|
|
9
9
|
note?: string
|
|
10
10
|
placeholder?: string
|
|
11
11
|
clicksContext?: ClicksContext
|
|
12
|
+
autoScroll?: boolean
|
|
12
13
|
}>()
|
|
13
14
|
|
|
14
15
|
defineEmits(['click'])
|
|
@@ -96,6 +97,9 @@ function highlightNote() {
|
|
|
96
97
|
e.stopPropagation()
|
|
97
98
|
e.stopImmediatePropagation()
|
|
98
99
|
})
|
|
100
|
+
|
|
101
|
+
if (props.autoScroll && clicks === current)
|
|
102
|
+
marker.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -125,7 +129,7 @@ onMounted(() => {
|
|
|
125
129
|
/>
|
|
126
130
|
<div
|
|
127
131
|
v-else-if="note"
|
|
128
|
-
class="prose overflow-auto outline-none"
|
|
132
|
+
class="prose overflow-auto outline-none slidev-note"
|
|
129
133
|
:class="props.class"
|
|
130
134
|
@click="$emit('click')"
|
|
131
135
|
>
|
|
@@ -133,10 +137,16 @@ onMounted(() => {
|
|
|
133
137
|
</div>
|
|
134
138
|
<div
|
|
135
139
|
v-else
|
|
136
|
-
class="prose overflow-auto outline-none opacity-50 italic select-none"
|
|
140
|
+
class="prose overflow-auto outline-none opacity-50 italic select-none slidev-note"
|
|
137
141
|
:class="props.class"
|
|
138
142
|
@click="$emit('click')"
|
|
139
143
|
>
|
|
140
144
|
<p v-text="props.placeholder || 'No notes.'" />
|
|
141
145
|
</div>
|
|
142
146
|
</template>
|
|
147
|
+
|
|
148
|
+
<style>
|
|
149
|
+
.slidev-note :first-child {
|
|
150
|
+
margin-top: 0;
|
|
151
|
+
}
|
|
152
|
+
</style>
|
package/internals/NoteEditor.vue
CHANGED
|
@@ -83,9 +83,7 @@ function calculateHeight() {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
const inputHeight = ref<number | null>()
|
|
86
|
-
|
|
87
|
-
calculateHeight()
|
|
88
|
-
})
|
|
86
|
+
|
|
89
87
|
watch(
|
|
90
88
|
note,
|
|
91
89
|
() => {
|
|
@@ -93,19 +91,20 @@ watch(
|
|
|
93
91
|
calculateHeight()
|
|
94
92
|
})
|
|
95
93
|
},
|
|
96
|
-
{ flush: 'post' },
|
|
94
|
+
{ flush: 'post', immediate: true },
|
|
97
95
|
)
|
|
98
96
|
</script>
|
|
99
97
|
|
|
100
98
|
<template>
|
|
101
99
|
<NoteDisplay
|
|
102
100
|
v-if="!editing"
|
|
103
|
-
class="
|
|
101
|
+
class="border-transparent border-2"
|
|
104
102
|
:class="[props.class, note ? '' : 'opacity-25 italic select-none']"
|
|
105
103
|
:style="props.style"
|
|
106
104
|
:note="note || placeholder"
|
|
107
105
|
:note-html="info?.noteHTML"
|
|
108
106
|
:clicks-context="clicksContext"
|
|
107
|
+
:auto-scroll="!autoHeight"
|
|
109
108
|
/>
|
|
110
109
|
<textarea
|
|
111
110
|
v-else
|
|
@@ -116,7 +115,6 @@ watch(
|
|
|
116
115
|
:style="[props.style, inputHeight != null ? { height: `${inputHeight}px` } : {}]"
|
|
117
116
|
:class="props.class"
|
|
118
117
|
:placeholder="placeholder"
|
|
119
|
-
@keydown.esc="
|
|
120
|
-
@focus="editing = true"
|
|
118
|
+
@keydown.esc="editing = false"
|
|
121
119
|
/>
|
|
122
120
|
</template>
|
|
@@ -164,18 +164,19 @@ watchEffect(() => {
|
|
|
164
164
|
</div>
|
|
165
165
|
</div>
|
|
166
166
|
</Transition>
|
|
167
|
-
<div v-if="value" class="fixed top-4 right-4 text-gray-400 flex items-center gap-
|
|
168
|
-
<
|
|
169
|
-
|
|
167
|
+
<div v-if="value" class="fixed top-4 right-4 text-gray-400 flex flex-col items-center gap-2">
|
|
168
|
+
<IconButton title="Close" class="text-2xl" @click="close">
|
|
169
|
+
<carbon:close />
|
|
170
|
+
</IconButton>
|
|
171
|
+
<IconButton
|
|
172
|
+
as="a"
|
|
173
|
+
title="Slides Overview"
|
|
170
174
|
target="_blank"
|
|
171
|
-
|
|
175
|
+
href="/overview"
|
|
172
176
|
tab-index="-1"
|
|
173
|
-
class="
|
|
177
|
+
class="text-2xl"
|
|
174
178
|
>
|
|
175
|
-
|
|
176
|
-
</RouterLink>
|
|
177
|
-
<IconButton title="Close" class="text-2xl" @click="close">
|
|
178
|
-
<carbon:close />
|
|
179
|
+
<carbon:list-boxes />
|
|
179
180
|
</IconButton>
|
|
180
181
|
</div>
|
|
181
182
|
</template>
|
|
@@ -54,7 +54,7 @@ onMounted(() => {
|
|
|
54
54
|
</IconButton>
|
|
55
55
|
<MenuButton :disabled="recording">
|
|
56
56
|
<template #button>
|
|
57
|
-
<IconButton title="Select recording device" class="h-full !text-sm !px-0">
|
|
57
|
+
<IconButton title="Select recording device" class="h-full !text-sm !px-0 aspect-initial">
|
|
58
58
|
<carbon:chevron-up class="opacity-50" />
|
|
59
59
|
</IconButton>
|
|
60
60
|
</template>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.48.0-beta.
|
|
4
|
+
"version": "0.48.0-beta.14",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"unocss": "^0.58.5",
|
|
56
56
|
"vue": "^3.4.20",
|
|
57
57
|
"vue-router": "^4.3.0",
|
|
58
|
-
"@slidev/types": "0.48.0-beta.
|
|
59
|
-
"@slidev/parser": "0.48.0-beta.
|
|
58
|
+
"@slidev/types": "0.48.0-beta.14",
|
|
59
|
+
"@slidev/parser": "0.48.0-beta.14"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"vite": "^5.1.4"
|
package/pages/notes.vue
CHANGED
|
@@ -52,6 +52,7 @@ function decreaseFontSize() {
|
|
|
52
52
|
:note-html="currentRoute?.meta?.slide?.noteHTML"
|
|
53
53
|
:placeholder="`No notes for Slide ${pageNo}.`"
|
|
54
54
|
:clicks-context="currentRoute?.meta?.__clicksContext"
|
|
55
|
+
:auto-scroll="true"
|
|
55
56
|
/>
|
|
56
57
|
</div>
|
|
57
58
|
<div class="flex-none border-t border-main">
|
package/pages/overview.vue
CHANGED
|
@@ -3,7 +3,7 @@ import { computed, nextTick, onMounted, reactive, ref } from 'vue'
|
|
|
3
3
|
import { useHead } from '@unhead/vue'
|
|
4
4
|
import type { RouteRecordRaw } from 'vue-router'
|
|
5
5
|
import type { ClicksContext } from 'packages/types'
|
|
6
|
-
import { themeVars } from '../env'
|
|
6
|
+
import { configs, themeVars } from '../env'
|
|
7
7
|
import { openInEditor, rawRoutes } from '../logic/nav'
|
|
8
8
|
import { useFixedClicks } from '../composables/useClicks'
|
|
9
9
|
import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
|
|
@@ -18,8 +18,9 @@ import { CLICKS_MAX } from '../constants'
|
|
|
18
18
|
|
|
19
19
|
const cardWidth = 450
|
|
20
20
|
|
|
21
|
+
const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
|
|
21
22
|
useHead({
|
|
22
|
-
title:
|
|
23
|
+
title: `Overview - ${slideTitle}`,
|
|
23
24
|
})
|
|
24
25
|
|
|
25
26
|
const blocks: Map<number, HTMLElement> = reactive(new Map())
|
|
@@ -56,7 +56,10 @@ const slidesWithNote = computed(() => rawRoutes
|
|
|
56
56
|
<div class="flex-auto" />
|
|
57
57
|
</div>
|
|
58
58
|
</h2>
|
|
59
|
-
<NoteDisplay
|
|
59
|
+
<NoteDisplay
|
|
60
|
+
:note-html="slide!.noteHTML"
|
|
61
|
+
class="max-w-full"
|
|
62
|
+
/>
|
|
60
63
|
</div>
|
|
61
64
|
<hr v-if="index < slidesWithNote.length - 1" class="border-main mb-8">
|
|
62
65
|
</div>
|
package/pages/presenter.vue
CHANGED
|
@@ -14,7 +14,7 @@ import { useFixedClicks } from '../composables/useClicks'
|
|
|
14
14
|
import SlideWrapper from '../internals/SlideWrapper'
|
|
15
15
|
import SlideContainer from '../internals/SlideContainer.vue'
|
|
16
16
|
import NavControls from '../internals/NavControls.vue'
|
|
17
|
-
import
|
|
17
|
+
import QuickOverview from '../internals/QuickOverview.vue'
|
|
18
18
|
import NoteEditor from '../internals/NoteEditor.vue'
|
|
19
19
|
import NoteStatic from '../internals/NoteStatic.vue'
|
|
20
20
|
import Goto from '../internals/Goto.vue'
|
|
@@ -45,12 +45,19 @@ const nextFrame = computed(() => {
|
|
|
45
45
|
else
|
|
46
46
|
return null
|
|
47
47
|
})
|
|
48
|
+
|
|
48
49
|
const nextFrameClicksCtx = computed(() => {
|
|
49
50
|
return nextFrame.value && clicksCtxMap[+nextFrame.value[0].path - 1]
|
|
50
51
|
})
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
|
|
53
|
+
watch(
|
|
54
|
+
[currentRoute, queryClicks],
|
|
55
|
+
() => {
|
|
56
|
+
if (nextFrameClicksCtx.value)
|
|
57
|
+
nextFrameClicksCtx.value.current = nextFrame.value![1]
|
|
58
|
+
},
|
|
59
|
+
{ immediate: true },
|
|
60
|
+
)
|
|
54
61
|
|
|
55
62
|
const SideEditor = shallowRef<any>()
|
|
56
63
|
if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
|
|
@@ -86,21 +93,6 @@ onMounted(() => {
|
|
|
86
93
|
<template>
|
|
87
94
|
<div class="bg-main h-full slidev-presenter">
|
|
88
95
|
<div class="grid-container" :class="`layout${presenterLayout}`">
|
|
89
|
-
<div class="grid-section top flex">
|
|
90
|
-
<img src="../assets/logo-title-horizontal.png" class="ml-2 my-auto h-10 py-1 lg:h-14 lg:py-2" style="height: 3.5rem;" alt="Slidev logo">
|
|
91
|
-
<div class="flex-auto" />
|
|
92
|
-
<div
|
|
93
|
-
class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
|
|
94
|
-
opacity="50 hover:100"
|
|
95
|
-
@click="resetTimer"
|
|
96
|
-
>
|
|
97
|
-
<carbon:time class="absolute" />
|
|
98
|
-
<carbon:renew class="absolute opacity-0" />
|
|
99
|
-
</div>
|
|
100
|
-
<div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
|
|
101
|
-
{{ timer }}
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
96
|
<div ref="main" class="relative grid-section main flex flex-col p-2 lg:p-4" :style="themeVars">
|
|
105
97
|
<SlideContainer
|
|
106
98
|
key="main"
|
|
@@ -110,8 +102,8 @@ onMounted(() => {
|
|
|
110
102
|
<SlidesShow render-context="presenter" />
|
|
111
103
|
</template>
|
|
112
104
|
</SlideContainer>
|
|
113
|
-
<div class="
|
|
114
|
-
|
|
105
|
+
<div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
|
|
106
|
+
Current
|
|
115
107
|
</div>
|
|
116
108
|
</div>
|
|
117
109
|
<div class="relative grid-section next flex flex-col p-2 lg:p-4" :style="themeVars">
|
|
@@ -129,8 +121,8 @@ onMounted(() => {
|
|
|
129
121
|
render-context="previewNext"
|
|
130
122
|
/>
|
|
131
123
|
</SlideContainer>
|
|
132
|
-
<div class="
|
|
133
|
-
|
|
124
|
+
<div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
|
|
125
|
+
Next
|
|
134
126
|
</div>
|
|
135
127
|
</div>
|
|
136
128
|
<!-- Notes -->
|
|
@@ -141,9 +133,9 @@ onMounted(() => {
|
|
|
141
133
|
<NoteEditor
|
|
142
134
|
v-if="__DEV__"
|
|
143
135
|
:key="`edit-${currentSlideId}`"
|
|
136
|
+
v-model:editing="notesEditing"
|
|
144
137
|
:no="currentSlideId"
|
|
145
138
|
class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
|
|
146
|
-
:editing="notesEditing"
|
|
147
139
|
:clicks-context="clicksContext"
|
|
148
140
|
:style="{ fontSize: `${presenterNotesFontSize}em` }"
|
|
149
141
|
/>
|
|
@@ -171,8 +163,20 @@ onMounted(() => {
|
|
|
171
163
|
</IconButton>
|
|
172
164
|
</div>
|
|
173
165
|
</div>
|
|
174
|
-
<div class="grid-section bottom">
|
|
166
|
+
<div class="grid-section bottom flex">
|
|
175
167
|
<NavControls :persist="true" />
|
|
168
|
+
<div flex-auto />
|
|
169
|
+
<div
|
|
170
|
+
class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
|
|
171
|
+
opacity="50 hover:100"
|
|
172
|
+
@click="resetTimer"
|
|
173
|
+
>
|
|
174
|
+
<carbon:time class="absolute" />
|
|
175
|
+
<carbon:renew class="absolute opacity-0" />
|
|
176
|
+
</div>
|
|
177
|
+
<div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
|
|
178
|
+
{{ timer }}
|
|
179
|
+
</div>
|
|
176
180
|
</div>
|
|
177
181
|
<DrawingControls v-if="__SLIDEV_FEATURE_DRAWINGS__" />
|
|
178
182
|
</div>
|
|
@@ -184,7 +188,7 @@ onMounted(() => {
|
|
|
184
188
|
</div>
|
|
185
189
|
</div>
|
|
186
190
|
<Goto />
|
|
187
|
-
<
|
|
191
|
+
<QuickOverview v-model="showOverview" />
|
|
188
192
|
</template>
|
|
189
193
|
|
|
190
194
|
<style scoped>
|
|
@@ -204,7 +208,7 @@ onMounted(() => {
|
|
|
204
208
|
}
|
|
205
209
|
|
|
206
210
|
.grid-container {
|
|
207
|
-
--uno: bg-
|
|
211
|
+
--uno: bg-gray/20;
|
|
208
212
|
height: 100%;
|
|
209
213
|
width: 100%;
|
|
210
214
|
display: grid;
|
|
@@ -213,9 +217,8 @@ onMounted(() => {
|
|
|
213
217
|
|
|
214
218
|
.grid-container.layout1 {
|
|
215
219
|
grid-template-columns: 1fr 1fr;
|
|
216
|
-
grid-template-rows:
|
|
220
|
+
grid-template-rows: 2fr 1fr min-content;
|
|
217
221
|
grid-template-areas:
|
|
218
|
-
'top top'
|
|
219
222
|
'main main'
|
|
220
223
|
'note next'
|
|
221
224
|
'bottom bottom';
|
|
@@ -223,9 +226,8 @@ onMounted(() => {
|
|
|
223
226
|
|
|
224
227
|
.grid-container.layout2 {
|
|
225
228
|
grid-template-columns: 3fr 2fr;
|
|
226
|
-
grid-template-rows:
|
|
229
|
+
grid-template-rows: 2fr 1fr min-content;
|
|
227
230
|
grid-template-areas:
|
|
228
|
-
'top top'
|
|
229
231
|
'note main'
|
|
230
232
|
'note next'
|
|
231
233
|
'bottom bottom';
|
|
@@ -234,9 +236,8 @@ onMounted(() => {
|
|
|
234
236
|
@media (max-aspect-ratio: 3/5) {
|
|
235
237
|
.grid-container.layout1 {
|
|
236
238
|
grid-template-columns: 1fr;
|
|
237
|
-
grid-template-rows:
|
|
239
|
+
grid-template-rows: 1fr 1fr 1fr min-content;
|
|
238
240
|
grid-template-areas:
|
|
239
|
-
'top'
|
|
240
241
|
'main'
|
|
241
242
|
'note'
|
|
242
243
|
'next'
|
|
@@ -247,9 +248,8 @@ onMounted(() => {
|
|
|
247
248
|
@media (min-aspect-ratio: 1/1) {
|
|
248
249
|
.grid-container.layout1 {
|
|
249
250
|
grid-template-columns: 1fr 1.1fr 0.9fr;
|
|
250
|
-
grid-template-rows:
|
|
251
|
+
grid-template-rows: 1fr 2fr min-content;
|
|
251
252
|
grid-template-areas:
|
|
252
|
-
'top top top'
|
|
253
253
|
'main main next'
|
|
254
254
|
'main main note'
|
|
255
255
|
'bottom bottom bottom';
|
|
@@ -279,10 +279,4 @@ onMounted(() => {
|
|
|
279
279
|
.grid-section.bottom {
|
|
280
280
|
grid-area: bottom;
|
|
281
281
|
}
|
|
282
|
-
.context {
|
|
283
|
-
position: absolute;
|
|
284
|
-
top: 0;
|
|
285
|
-
left: 0;
|
|
286
|
-
--uno: px-1 text-xs bg-gray-400 bg-opacity-50 opacity-75 rounded-br-md;
|
|
287
|
-
}
|
|
288
282
|
</style>
|