@slidev/client 0.39.0 → 0.40.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/constants.ts +5 -0
- package/internals/Draggable.vue +2 -2
- package/internals/DrawingControls.vue +12 -12
- package/internals/Editor.vue +4 -4
- package/internals/NavControls.vue +13 -13
- package/internals/{NoteViewer.vue → NoteDisplay.vue} +13 -3
- package/internals/NoteEditor.vue +3 -3
- package/internals/NoteStatic.vue +3 -3
- package/internals/NotesView.vue +76 -0
- package/internals/Presenter.vue +1 -1
- package/internals/PresenterPrint.vue +3 -3
- package/internals/RecordingControls.vue +3 -3
- package/internals/SlidesOverview.vue +1 -1
- package/internals/SlidesShow.vue +1 -1
- package/internals/WebCamera.vue +3 -3
- package/logic/dark.ts +2 -2
- package/logic/drawings.ts +4 -4
- package/logic/nav.ts +1 -0
- package/logic/recording.ts +2 -2
- package/package.json +10 -10
- package/routes.ts +24 -17
- package/setup/main.ts +1 -1
- package/setup/monaco.ts +1 -1
- package/setup/root.ts +25 -2
- package/setup/shortcuts.ts +1 -1
- package/state/index.ts +8 -8
- package/state/shared.ts +18 -1
- package/styles/index.css +4 -4
package/constants.ts
CHANGED
|
@@ -19,3 +19,8 @@ export const CLASS_VCLICK_GONE = 'slidev-vclick-gone'
|
|
|
19
19
|
export const CLASS_VCLICK_HIDDEN_EXP = 'slidev-vclick-hidden-explicitly'
|
|
20
20
|
export const CLASS_VCLICK_CURRENT = 'slidev-vclick-current'
|
|
21
21
|
export const CLASS_VCLICK_PRIOR = 'slidev-vclick-prior'
|
|
22
|
+
|
|
23
|
+
export const TRUST_ORIGINS = [
|
|
24
|
+
'localhost',
|
|
25
|
+
'127.0.0.1',
|
|
26
|
+
]
|
package/internals/Draggable.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref } from 'vue'
|
|
3
|
-
import { useDraggable,
|
|
3
|
+
import { useDraggable, useLocalStorage } from '@vueuse/core'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
storageKey?: string
|
|
@@ -10,7 +10,7 @@ const props = defineProps<{
|
|
|
10
10
|
const el = ref<HTMLElement | null>(null)
|
|
11
11
|
const initial = props.initial ?? { x: 0, y: 0 }
|
|
12
12
|
const point = props.storageKey
|
|
13
|
-
?
|
|
13
|
+
? useLocalStorage(props.storageKey, initial)
|
|
14
14
|
: ref(initial)
|
|
15
15
|
const { style } = useDraggable(el, { initialValue: point })
|
|
16
16
|
</script>
|
|
@@ -32,25 +32,25 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
32
32
|
:initial-x="10"
|
|
33
33
|
:initial-y="10"
|
|
34
34
|
>
|
|
35
|
-
<button class="icon-btn" :class="{ shallow: drawingMode !== 'stylus' }" @click="setDrawingMode('stylus')">
|
|
35
|
+
<button class="slidev-icon-btn" :class="{ shallow: drawingMode !== 'stylus' }" @click="setDrawingMode('stylus')">
|
|
36
36
|
<carbon:pen />
|
|
37
37
|
</button>
|
|
38
|
-
<button class="icon-btn" :class="{ shallow: drawingMode !== 'line' }" @click="setDrawingMode('line')">
|
|
38
|
+
<button class="slidev-icon-btn" :class="{ shallow: drawingMode !== 'line' }" @click="setDrawingMode('line')">
|
|
39
39
|
<svg width="1em" height="1em" class="-mt-0.5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
|
|
40
40
|
<path d="M21.71 3.29a1 1 0 0 0-1.42 0l-18 18a1 1 0 0 0 0 1.42a1 1 0 0 0 1.42 0l18-18a1 1 0 0 0 0-1.42z" fill="currentColor" />
|
|
41
41
|
</svg>
|
|
42
42
|
</button>
|
|
43
|
-
<button class="icon-btn" :class="{ shallow: drawingMode !== 'arrow' }" @click="setDrawingMode('arrow')">
|
|
43
|
+
<button class="slidev-icon-btn" :class="{ shallow: drawingMode !== 'arrow' }" @click="setDrawingMode('arrow')">
|
|
44
44
|
<carbon:arrow-up-right />
|
|
45
45
|
</button>
|
|
46
|
-
<button class="icon-btn" :class="{ shallow: drawingMode !== 'ellipse' }" @click="setDrawingMode('ellipse')">
|
|
46
|
+
<button class="slidev-icon-btn" :class="{ shallow: drawingMode !== 'ellipse' }" @click="setDrawingMode('ellipse')">
|
|
47
47
|
<carbon:radio-button />
|
|
48
48
|
</button>
|
|
49
|
-
<button class="icon-btn" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
|
|
49
|
+
<button class="slidev-icon-btn" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
|
|
50
50
|
<carbon:checkbox />
|
|
51
51
|
</button>
|
|
52
52
|
<!-- TODO: not sure why it's not working! -->
|
|
53
|
-
<!-- <button class="icon-btn" :class="{ shallow: drawingMode != 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
53
|
+
<!-- <button class="slidev-icon-btn" :class="{ shallow: drawingMode != 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
54
54
|
<carbon:erase />
|
|
55
55
|
</button> -->
|
|
56
56
|
|
|
@@ -59,7 +59,7 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
59
59
|
<button
|
|
60
60
|
v-for="color of brushColors"
|
|
61
61
|
:key="color"
|
|
62
|
-
class="icon-btn"
|
|
62
|
+
class="slidev-icon-btn"
|
|
63
63
|
:class="brush.color === color ? 'active' : 'shallow'"
|
|
64
64
|
@click="setBrushColor(color)"
|
|
65
65
|
>
|
|
@@ -72,24 +72,24 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
72
72
|
|
|
73
73
|
<VerticalDivider />
|
|
74
74
|
|
|
75
|
-
<button class="icon-btn" :class="{ disabled: !canUndo }" @click="undo()">
|
|
75
|
+
<button class="slidev-icon-btn" :class="{ disabled: !canUndo }" @click="undo()">
|
|
76
76
|
<carbon:undo />
|
|
77
77
|
</button>
|
|
78
|
-
<button class="icon-btn" :class="{ disabled: !canRedo }" @click="redo()">
|
|
78
|
+
<button class="slidev-icon-btn" :class="{ disabled: !canRedo }" @click="redo()">
|
|
79
79
|
<carbon:redo />
|
|
80
80
|
</button>
|
|
81
|
-
<button class="icon-btn" :class="{ disabled: !canClear }" @click="clearDrauu()">
|
|
81
|
+
<button class="slidev-icon-btn" :class="{ disabled: !canClear }" @click="clearDrauu()">
|
|
82
82
|
<carbon:delete />
|
|
83
83
|
</button>
|
|
84
84
|
|
|
85
85
|
<VerticalDivider />
|
|
86
|
-
<button class="icon-btn" :class="{ shallow: !drawingPinned }" @click="drawingPinned = !drawingPinned">
|
|
86
|
+
<button class="slidev-icon-btn" :class="{ shallow: !drawingPinned }" @click="drawingPinned = !drawingPinned">
|
|
87
87
|
<carbon:pin-filled v-show="drawingPinned" class="transform -rotate-45" />
|
|
88
88
|
<carbon:pin v-show="!drawingPinned" />
|
|
89
89
|
</button>
|
|
90
90
|
<button
|
|
91
91
|
v-if="drawingEnabled"
|
|
92
|
-
class="icon-btn"
|
|
92
|
+
class="slidev-icon-btn"
|
|
93
93
|
:class="{ shallow: !drawingEnabled }"
|
|
94
94
|
@click="drawingEnabled = !drawingEnabled"
|
|
95
95
|
>
|
package/internals/Editor.vue
CHANGED
|
@@ -137,10 +137,10 @@ throttledWatch(
|
|
|
137
137
|
>
|
|
138
138
|
<div class="flex pb-2 text-xl -mt-1">
|
|
139
139
|
<div class="mr-4 rounded flex">
|
|
140
|
-
<button class="icon-btn" :class="tab === 'content' ? 'text-$slidev-theme-primary' : ''" @click="switchTab('content')">
|
|
140
|
+
<button class="slidev-icon-btn" :class="tab === 'content' ? 'text-$slidev-theme-primary' : ''" @click="switchTab('content')">
|
|
141
141
|
<carbon:account />
|
|
142
142
|
</button>
|
|
143
|
-
<button class="icon-btn" :class="tab === 'note' ? 'text-$slidev-theme-primary' : ''" @click="switchTab('note')">
|
|
143
|
+
<button class="slidev-icon-btn" :class="tab === 'note' ? 'text-$slidev-theme-primary' : ''" @click="switchTab('note')">
|
|
144
144
|
<carbon:align-box-bottom-right />
|
|
145
145
|
</button>
|
|
146
146
|
</div>
|
|
@@ -148,10 +148,10 @@ throttledWatch(
|
|
|
148
148
|
{{ tab === 'content' ? 'Slide' : 'Notes' }}
|
|
149
149
|
</span>
|
|
150
150
|
<div class="flex-auto" />
|
|
151
|
-
<button class="icon-btn" @click="openInEditor()">
|
|
151
|
+
<button class="slidev-icon-btn" @click="openInEditor()">
|
|
152
152
|
<carbon:launch />
|
|
153
153
|
</button>
|
|
154
|
-
<button class="icon-btn" @click="close">
|
|
154
|
+
<button class="slidev-icon-btn" @click="close">
|
|
155
155
|
<carbon:close />
|
|
156
156
|
</button>
|
|
157
157
|
</div>
|
|
@@ -51,26 +51,26 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
51
51
|
:class="barStyle"
|
|
52
52
|
@mouseleave="onMouseLeave"
|
|
53
53
|
>
|
|
54
|
-
<button v-if="!isEmbedded" class="icon-btn" @click="toggleFullscreen">
|
|
54
|
+
<button v-if="!isEmbedded" class="slidev-icon-btn" @click="toggleFullscreen">
|
|
55
55
|
<carbon:minimize v-if="isFullscreen" />
|
|
56
56
|
<carbon:maximize v-else />
|
|
57
57
|
</button>
|
|
58
58
|
|
|
59
|
-
<button class="icon-btn" :class="{ disabled: !hasPrev }" @click="prev">
|
|
59
|
+
<button class="slidev-icon-btn" :class="{ disabled: !hasPrev }" @click="prev">
|
|
60
60
|
<carbon:arrow-left />
|
|
61
61
|
</button>
|
|
62
62
|
|
|
63
|
-
<button class="icon-btn" :class="{ disabled: !hasNext }" title="Next" @click="next">
|
|
63
|
+
<button class="slidev-icon-btn" :class="{ disabled: !hasNext }" title="Next" @click="next">
|
|
64
64
|
<carbon:arrow-right />
|
|
65
65
|
</button>
|
|
66
66
|
|
|
67
|
-
<button v-if="!isEmbedded" class="icon-btn" title="Slides overview" @click="toggleOverview()">
|
|
67
|
+
<button v-if="!isEmbedded" class="slidev-icon-btn" title="Slides overview" @click="toggleOverview()">
|
|
68
68
|
<carbon:apps />
|
|
69
69
|
</button>
|
|
70
70
|
|
|
71
71
|
<button
|
|
72
72
|
v-if="!isColorSchemaConfigured"
|
|
73
|
-
class="icon-btn"
|
|
73
|
+
class="slidev-icon-btn"
|
|
74
74
|
title="Toggle dark mode"
|
|
75
75
|
@click="toggleDark()"
|
|
76
76
|
>
|
|
@@ -88,7 +88,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
88
88
|
|
|
89
89
|
<button
|
|
90
90
|
v-if="isPresenter"
|
|
91
|
-
class="icon-btn"
|
|
91
|
+
class="slidev-icon-btn"
|
|
92
92
|
title="Show presenter cursor"
|
|
93
93
|
@click="showPresenterCursor = !showPresenterCursor"
|
|
94
94
|
>
|
|
@@ -98,7 +98,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
98
98
|
</template>
|
|
99
99
|
|
|
100
100
|
<template v-if="__SLIDEV_FEATURE_DRAWINGS__ && (!configs.drawings.presenterOnly || isPresenter) && !isEmbedded">
|
|
101
|
-
<button class="icon-btn relative" title="Drawing" @click="drawingEnabled = !drawingEnabled">
|
|
101
|
+
<button class="slidev-icon-btn relative" title="Drawing" @click="drawingEnabled = !drawingEnabled">
|
|
102
102
|
<carbon:pen />
|
|
103
103
|
<div
|
|
104
104
|
v-if="drawingEnabled"
|
|
@@ -110,26 +110,26 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
110
110
|
</template>
|
|
111
111
|
|
|
112
112
|
<template v-if="!isEmbedded">
|
|
113
|
-
<RouterLink v-if="isPresenter" :to="nonPresenterLink" class="icon-btn" title="Play Mode">
|
|
113
|
+
<RouterLink v-if="isPresenter" :to="nonPresenterLink" class="slidev-icon-btn" title="Play Mode">
|
|
114
114
|
<carbon:presentation-file />
|
|
115
115
|
</RouterLink>
|
|
116
|
-
<RouterLink v-if="__SLIDEV_FEATURE_PRESENTER__ && showPresenter" :to="presenterLink" class="icon-btn" title="Presenter Mode">
|
|
116
|
+
<RouterLink v-if="__SLIDEV_FEATURE_PRESENTER__ && showPresenter" :to="presenterLink" class="slidev-icon-btn" title="Presenter Mode">
|
|
117
117
|
<carbon:user-speaker />
|
|
118
118
|
</RouterLink>
|
|
119
119
|
|
|
120
|
-
<button v-if="__DEV__ && !isPresenter" class="icon-btn <md:hidden" @click="showEditor = !showEditor">
|
|
120
|
+
<button v-if="__DEV__ && !isPresenter" class="slidev-icon-btn <md:hidden" @click="showEditor = !showEditor">
|
|
121
121
|
<carbon:text-annotation-toggle />
|
|
122
122
|
</button>
|
|
123
123
|
</template>
|
|
124
124
|
<template v-if="!__DEV__">
|
|
125
|
-
<button v-if="configs.download" class="icon-btn" @click="downloadPDF">
|
|
125
|
+
<button v-if="configs.download" class="slidev-icon-btn" @click="downloadPDF">
|
|
126
126
|
<carbon:download />
|
|
127
127
|
</button>
|
|
128
128
|
</template>
|
|
129
129
|
|
|
130
130
|
<button
|
|
131
131
|
v-if="!isPresenter && configs.info && !isEmbedded"
|
|
132
|
-
class="icon-btn"
|
|
132
|
+
class="slidev-icon-btn"
|
|
133
133
|
@click="showInfoDialog = !showInfoDialog"
|
|
134
134
|
>
|
|
135
135
|
<carbon:information />
|
|
@@ -138,7 +138,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
138
138
|
<template v-if="!isPresenter && !isEmbedded">
|
|
139
139
|
<MenuButton>
|
|
140
140
|
<template #button>
|
|
141
|
-
<button class="icon-btn">
|
|
141
|
+
<button class="slidev-icon-btn">
|
|
142
142
|
<carbon:settings-adjust />
|
|
143
143
|
</button>
|
|
144
144
|
</template>
|
|
@@ -3,6 +3,7 @@ const props = defineProps<{
|
|
|
3
3
|
class?: string
|
|
4
4
|
noteHtml?: string
|
|
5
5
|
note?: string
|
|
6
|
+
placeholder?: string
|
|
6
7
|
}>()
|
|
7
8
|
|
|
8
9
|
defineEmits(['click'])
|
|
@@ -17,10 +18,19 @@ defineEmits(['click'])
|
|
|
17
18
|
v-html="noteHtml"
|
|
18
19
|
/>
|
|
19
20
|
<div
|
|
20
|
-
v-else
|
|
21
|
+
v-else-if="note"
|
|
21
22
|
class="prose overflow-auto outline-none"
|
|
22
23
|
:class="props.class"
|
|
23
24
|
@click="$emit('click')"
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
>
|
|
26
|
+
<p v-text="note" />
|
|
27
|
+
</div>
|
|
28
|
+
<div
|
|
29
|
+
v-else
|
|
30
|
+
class="prose overflow-auto outline-none opacity-50 italic"
|
|
31
|
+
:class="props.class"
|
|
32
|
+
@click="$emit('click')"
|
|
33
|
+
>
|
|
34
|
+
<p v-text="props.placeholder || 'No notes.'" />
|
|
35
|
+
</div>
|
|
26
36
|
</template>
|
package/internals/NoteEditor.vue
CHANGED
|
@@ -3,7 +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
|
|
6
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
7
7
|
|
|
8
8
|
const props = defineProps({
|
|
9
9
|
class: {
|
|
@@ -60,11 +60,11 @@ onClickOutside(input, () => {
|
|
|
60
60
|
</script>
|
|
61
61
|
|
|
62
62
|
<template>
|
|
63
|
-
<
|
|
63
|
+
<NoteDisplay
|
|
64
64
|
v-if="!editing && note"
|
|
65
65
|
:class="props.class"
|
|
66
66
|
:note="note"
|
|
67
|
-
:note-html="info?.
|
|
67
|
+
:note-html="info?.noteHTML"
|
|
68
68
|
@click="switchNoteEdit"
|
|
69
69
|
/>
|
|
70
70
|
<textarea
|
package/internals/NoteStatic.vue
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import { currentRoute } from '../logic/nav'
|
|
4
|
-
import
|
|
4
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
7
|
class?: string
|
|
8
8
|
}>()
|
|
9
9
|
|
|
10
10
|
const note = computed(() => currentRoute.value?.meta?.slide?.note)
|
|
11
|
-
const noteHtml = computed(() => currentRoute.value?.meta?.slide?.
|
|
11
|
+
const noteHtml = computed(() => currentRoute.value?.meta?.slide?.noteHTML)
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
<template>
|
|
15
|
-
<
|
|
15
|
+
<NoteDisplay
|
|
16
16
|
:class="props.class"
|
|
17
17
|
:note="note"
|
|
18
18
|
:note-html="noteHtml"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useHead } from '@vueuse/head'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { useLocalStorage } from '@vueuse/core'
|
|
5
|
+
import { configs } from '../env'
|
|
6
|
+
import { sharedState } from '../state/shared'
|
|
7
|
+
import { fullscreen } from '../state'
|
|
8
|
+
import { total } from '../logic/nav'
|
|
9
|
+
import { rawRoutes } from '../routes'
|
|
10
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
11
|
+
|
|
12
|
+
const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
|
|
13
|
+
useHead({
|
|
14
|
+
title: `Notes - ${slideTitle}`,
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const { isFullscreen, toggle: toggleFullscreen } = fullscreen
|
|
18
|
+
|
|
19
|
+
const fontSize = useLocalStorage('slidev-notes-font-size', 18)
|
|
20
|
+
const pageNo = computed(() => sharedState.lastUpdate?.type === 'viewer' ? sharedState.viewerPage : sharedState.page)
|
|
21
|
+
const currentRoute = computed(() => rawRoutes.find(i => i.path === `${pageNo.value}`))
|
|
22
|
+
const nextRoute = computed(() => rawRoutes.find(i => i.path === `${pageNo.value + 1}`))
|
|
23
|
+
|
|
24
|
+
function increaseFontSize() {
|
|
25
|
+
fontSize.value = fontSize.value + 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function decreaseFontSize() {
|
|
29
|
+
fontSize.value = fontSize.value - 1
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div
|
|
35
|
+
class="fixed top-0 left-0 h-2px bg-teal-500 transition-all duration-500"
|
|
36
|
+
:style="{ width: `${(pageNo - 1) / total * 100}%` }"
|
|
37
|
+
/>
|
|
38
|
+
<div class="h-full flex flex-col">
|
|
39
|
+
<div
|
|
40
|
+
class="px-5 flex-auto h-full overflow-auto"
|
|
41
|
+
:style="{ fontSize: `${fontSize}px` }"
|
|
42
|
+
>
|
|
43
|
+
<NoteDisplay
|
|
44
|
+
:note="currentRoute?.meta?.slide?.note"
|
|
45
|
+
:note-html="currentRoute?.meta?.slide?.noteHTML"
|
|
46
|
+
:placeholder="`No notes for Slide ${pageNo}.`"
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
<div v-if="nextRoute" class="px-5 py-2 max-h-60 overflow-auto border-t border-gray-400 border-opacity-20">
|
|
50
|
+
<NoteDisplay
|
|
51
|
+
class="opacity-50"
|
|
52
|
+
:note="nextRoute?.meta?.slide?.note"
|
|
53
|
+
:note-html="nextRoute?.meta?.slide?.noteHTML"
|
|
54
|
+
placeholder="No notes for next slide."
|
|
55
|
+
/>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="flex-none border-t border-gray-400 border-opacity-20">
|
|
58
|
+
<div class="flex gap-1 items-center px-6 py-3">
|
|
59
|
+
<button class="slidev-icon-btn" @click="toggleFullscreen">
|
|
60
|
+
<carbon:minimize v-if="isFullscreen" />
|
|
61
|
+
<carbon:maximize v-else />
|
|
62
|
+
</button>
|
|
63
|
+
<button class="slidev-icon-btn" @click="increaseFontSize">
|
|
64
|
+
<carbon:zoom-in />
|
|
65
|
+
</button>
|
|
66
|
+
<button class="slidev-icon-btn" @click="decreaseFontSize">
|
|
67
|
+
<carbon:zoom-out />
|
|
68
|
+
</button>
|
|
69
|
+
<div class="flex-auto" />
|
|
70
|
+
<div class="p2 text-center">
|
|
71
|
+
{{ pageNo }} / {{ total }}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</template>
|
package/internals/Presenter.vue
CHANGED
|
@@ -118,7 +118,7 @@ onMounted(() => {
|
|
|
118
118
|
class="h-full w-full"
|
|
119
119
|
>
|
|
120
120
|
<SlideWrapper
|
|
121
|
-
:is="nextSlide.route?.component"
|
|
121
|
+
:is="nextSlide.route?.component as any"
|
|
122
122
|
v-model:clicks-elements="nextTabElements"
|
|
123
123
|
:clicks="nextSlide.clicks"
|
|
124
124
|
:clicks-disabled="false"
|
|
@@ -4,7 +4,7 @@ import { useStyleTag } from '@vueuse/core'
|
|
|
4
4
|
import { useHead } from '@vueuse/head'
|
|
5
5
|
import { configs, themeVars } from '../env'
|
|
6
6
|
import { rawRoutes, total } from '../logic/nav'
|
|
7
|
-
import
|
|
7
|
+
import NoteDisplay from './NoteDisplay.vue'
|
|
8
8
|
|
|
9
9
|
useStyleTag(`
|
|
10
10
|
@page {
|
|
@@ -29,7 +29,7 @@ useHead({ title: `Notes - ${configs.title}` })
|
|
|
29
29
|
const slidesWithNote = computed(() => rawRoutes
|
|
30
30
|
.slice(0, -1)
|
|
31
31
|
.map(route => route.meta?.slide)
|
|
32
|
-
.filter(slide => slide !== undefined && slide.
|
|
32
|
+
.filter(slide => slide !== undefined && slide.noteHTML !== ''))
|
|
33
33
|
</script>
|
|
34
34
|
|
|
35
35
|
<template>
|
|
@@ -55,7 +55,7 @@ const slidesWithNote = computed(() => rawRoutes
|
|
|
55
55
|
<div class="flex-auto" />
|
|
56
56
|
</div>
|
|
57
57
|
</h2>
|
|
58
|
-
<
|
|
58
|
+
<NoteDisplay :note-html="slide!.noteHTML" class="max-w-full" />
|
|
59
59
|
</div>
|
|
60
60
|
<hr v-if="index < slidesWithNote.length - 1" class="border-gray-400/50 mb-8">
|
|
61
61
|
</div>
|
|
@@ -35,7 +35,7 @@ onMounted(() => {
|
|
|
35
35
|
<template>
|
|
36
36
|
<button
|
|
37
37
|
v-if="currentCamera !== 'none'"
|
|
38
|
-
class="icon-btn <md:hidden"
|
|
38
|
+
class="slidev-icon-btn <md:hidden"
|
|
39
39
|
:class="{ 'text-green-500': Boolean(showAvatar && streamCamera) }"
|
|
40
40
|
title="Show camera view"
|
|
41
41
|
@click="toggleAvatar"
|
|
@@ -44,7 +44,7 @@ onMounted(() => {
|
|
|
44
44
|
</button>
|
|
45
45
|
|
|
46
46
|
<button
|
|
47
|
-
class="icon-btn"
|
|
47
|
+
class="slidev-icon-btn"
|
|
48
48
|
:class="{ 'text-red-500': recording }"
|
|
49
49
|
title="Recording"
|
|
50
50
|
@click="toggleRecording"
|
|
@@ -54,7 +54,7 @@ onMounted(() => {
|
|
|
54
54
|
</button>
|
|
55
55
|
<MenuButton :disabled="recording">
|
|
56
56
|
<template #button>
|
|
57
|
-
<button class="icon-btn h-full !text-sm !px-0">
|
|
57
|
+
<button class="slidev-icon-btn h-full !text-sm !px-0">
|
|
58
58
|
<carbon:chevron-up class="opacity-50" />
|
|
59
59
|
</button>
|
|
60
60
|
</template>
|
|
@@ -101,7 +101,7 @@ watchEffect(() => {
|
|
|
101
101
|
</div>
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
|
-
<button v-if="value" class="fixed text-2xl top-4 right-4 icon-btn text-gray-400" @click="close">
|
|
104
|
+
<button v-if="value" class="fixed text-2xl top-4 right-4 slidev-icon-btn text-gray-400" @click="close">
|
|
105
105
|
<carbon:close />
|
|
106
106
|
</button>
|
|
107
107
|
</template>
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { computed, shallowRef, watch } from 'vue'
|
|
3
3
|
import { clicks, currentRoute, isPresenter, nextRoute, rawRoutes, transition } from '../logic/nav'
|
|
4
4
|
import { getSlideClass } from '../utils'
|
|
5
5
|
import SlideWrapper from './SlideWrapper'
|
package/internals/WebCamera.vue
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useDraggable, useEventListener,
|
|
2
|
+
import { useDraggable, useEventListener, useLocalStorage } from '@vueuse/core'
|
|
3
3
|
import { computed, onMounted, ref, watchEffect } from 'vue'
|
|
4
4
|
import { currentCamera } from '../state'
|
|
5
5
|
import { recorder } from '../logic/recording'
|
|
6
6
|
|
|
7
|
-
const size =
|
|
8
|
-
const position =
|
|
7
|
+
const size = useLocalStorage('slidev-webcam-size', Math.round(Math.min(window.innerHeight, (window.innerWidth) / 8)))
|
|
8
|
+
const position = useLocalStorage('slidev-webcam-pos', {
|
|
9
9
|
x: window.innerWidth - size.value - 30,
|
|
10
10
|
y: window.innerHeight - size.value - 30,
|
|
11
11
|
}, undefined, { deep: true })
|
package/logic/dark.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { isClient,
|
|
1
|
+
import { isClient, useLocalStorage, usePreferredDark, useToggle } from '@vueuse/core'
|
|
2
2
|
import { computed, watch } from 'vue'
|
|
3
3
|
import { configs } from '../env'
|
|
4
4
|
|
|
5
5
|
const preferredDark = usePreferredDark()
|
|
6
|
-
const store =
|
|
6
|
+
const store = useLocalStorage('slidev-color-schema', 'auto')
|
|
7
7
|
|
|
8
8
|
export const isColorSchemaConfigured = computed(() => configs.colorSchema !== 'auto')
|
|
9
9
|
export const isDark = computed<boolean>({
|
package/logic/drawings.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed, markRaw, nextTick, reactive, ref, watch } from 'vue'
|
|
2
2
|
import type { Brush, Options as DrauuOptions, DrawingMode } from 'drauu'
|
|
3
3
|
import { createDrauu } from 'drauu'
|
|
4
|
-
import { toReactive,
|
|
4
|
+
import { toReactive, useLocalStorage } from '@vueuse/core'
|
|
5
5
|
import { drawingState, onPatch, patch } from '../state/drawings'
|
|
6
6
|
import { configs } from '../env'
|
|
7
7
|
import { currentPage, isPresenter } from './nav'
|
|
@@ -16,14 +16,14 @@ export const brushColors = [
|
|
|
16
16
|
'#000000',
|
|
17
17
|
]
|
|
18
18
|
|
|
19
|
-
export const drawingEnabled =
|
|
20
|
-
export const drawingPinned =
|
|
19
|
+
export const drawingEnabled = useLocalStorage('slidev-drawing-enabled', false)
|
|
20
|
+
export const drawingPinned = useLocalStorage('slidev-drawing-pinned', false)
|
|
21
21
|
export const canUndo = ref(false)
|
|
22
22
|
export const canRedo = ref(false)
|
|
23
23
|
export const canClear = ref(false)
|
|
24
24
|
export const isDrawing = ref(false)
|
|
25
25
|
|
|
26
|
-
export const brush = toReactive(
|
|
26
|
+
export const brush = toReactive(useLocalStorage<Brush>('slidev-drawing-brush', {
|
|
27
27
|
color: brushColors[0],
|
|
28
28
|
size: 4,
|
|
29
29
|
mode: 'stylus',
|
package/logic/nav.ts
CHANGED
|
@@ -26,6 +26,7 @@ export const isPrintMode = computed(() => route.value.query.print !== undefined)
|
|
|
26
26
|
export const isPrintWithClicks = computed(() => route.value.query.print === 'clicks')
|
|
27
27
|
export const isEmbedded = computed(() => route.value.query.embedded !== undefined)
|
|
28
28
|
export const isPresenter = computed(() => route.value.path.startsWith('/presenter'))
|
|
29
|
+
export const isNotesViewer = computed(() => route.value.path.startsWith('/notes'))
|
|
29
30
|
export const isClicksDisabled = computed(() => isPrintMode.value && !isPrintWithClicks.value)
|
|
30
31
|
export const presenterPassword = computed(() => route.value.query.password)
|
|
31
32
|
export const showPresenter = computed(() => !isPresenter.value && (!configs.remote || presenterPassword.value === configs.remote))
|
package/logic/recording.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Ref } from 'vue'
|
|
2
2
|
import { nextTick, ref, shallowRef, watch } from 'vue'
|
|
3
|
-
import { useDevicesList, useEventListener,
|
|
3
|
+
import { useDevicesList, useEventListener, useLocalStorage } from '@vueuse/core'
|
|
4
4
|
import { isTruthy } from '@antfu/utils'
|
|
5
5
|
import type RecorderType from 'recordrtc'
|
|
6
6
|
import type { Options as RecorderOptions } from 'recordrtc'
|
|
@@ -11,7 +11,7 @@ type MimeType = Defined<RecorderOptions['mimeType']>
|
|
|
11
11
|
|
|
12
12
|
export const recordingName = ref('')
|
|
13
13
|
export const recordCamera = ref(true)
|
|
14
|
-
export const mimeType =
|
|
14
|
+
export const mimeType = useLocalStorage<MimeType>('slidev-record-mimetype', 'video/webm')
|
|
15
15
|
|
|
16
16
|
export const mimeExtMap: Record<string, string> = {
|
|
17
17
|
'video/webm': 'webm',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"description": "Presentation slides for developers",
|
|
5
5
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@antfu/utils": "^0.7.2",
|
|
19
|
-
"@unocss/reset": "^0.49.
|
|
20
|
-
"@vueuse/core": "^9.
|
|
21
|
-
"@vueuse/head": "^1.0.
|
|
22
|
-
"@vueuse/math": "^9.
|
|
19
|
+
"@unocss/reset": "^0.49.4",
|
|
20
|
+
"@vueuse/core": "^9.12.0",
|
|
21
|
+
"@vueuse/head": "^1.0.24",
|
|
22
|
+
"@vueuse/math": "^9.12.0",
|
|
23
23
|
"@vueuse/motion": "^2.0.0-beta.27",
|
|
24
24
|
"codemirror": "^5.65.5",
|
|
25
25
|
"defu": "^6.1.2",
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"prettier": "^2.8.3",
|
|
35
35
|
"recordrtc": "^5.6.2",
|
|
36
36
|
"resolve": "^1.22.1",
|
|
37
|
-
"unocss": "^0.49.
|
|
37
|
+
"unocss": "^0.49.4",
|
|
38
38
|
"vite-plugin-windicss": "^1.8.10",
|
|
39
|
-
"vue": "^3.2.
|
|
39
|
+
"vue": "^3.2.47",
|
|
40
40
|
"vue-router": "^4.1.6",
|
|
41
41
|
"vue-starport": "^0.3.0",
|
|
42
42
|
"windicss": "^3.5.6",
|
|
43
|
-
"@slidev/parser": "0.
|
|
44
|
-
"@slidev/types": "0.
|
|
43
|
+
"@slidev/parser": "0.40.1",
|
|
44
|
+
"@slidev/types": "0.40.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"vite": "^4.
|
|
47
|
+
"vite": "^4.1.1"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/routes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { RouteRecordRaw } from 'vue-router'
|
|
2
|
-
import type { SlideTransition } from '@slidev/types'
|
|
1
|
+
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
|
3
2
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
|
3
|
+
import type { TransitionGroupProps } from 'vue'
|
|
4
4
|
import Play from './internals/Play.vue'
|
|
5
5
|
import Print from './internals/Print.vue'
|
|
6
6
|
// @ts-expect-error missing types
|
|
@@ -25,24 +25,31 @@ export const routes: RouteRecordRaw[] = [
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
if (__SLIDEV_FEATURE_PRESENTER__) {
|
|
28
|
+
function passwordGuard(to: RouteLocationNormalized) {
|
|
29
|
+
if (!_configs.remote || _configs.remote === to.query.password)
|
|
30
|
+
return true
|
|
31
|
+
if (_configs.remote && to.query.password === undefined) {
|
|
32
|
+
// eslint-disable-next-line no-alert
|
|
33
|
+
const password = prompt('Enter password')
|
|
34
|
+
if (_configs.remote === password)
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
if (to.params.no)
|
|
38
|
+
return { path: `/${to.params.no}` }
|
|
39
|
+
return { path: '' }
|
|
40
|
+
}
|
|
28
41
|
routes.push({ path: '/presenter/print', component: () => import('./internals/PresenterPrint.vue') })
|
|
42
|
+
routes.push({
|
|
43
|
+
name: 'notes',
|
|
44
|
+
path: '/notes',
|
|
45
|
+
component: () => import('./internals/NotesView.vue'),
|
|
46
|
+
beforeEnter: passwordGuard,
|
|
47
|
+
})
|
|
29
48
|
routes.push({
|
|
30
49
|
name: 'presenter',
|
|
31
50
|
path: '/presenter/:no',
|
|
32
51
|
component: () => import('./internals/Presenter.vue'),
|
|
33
|
-
beforeEnter:
|
|
34
|
-
if (!_configs.remote || _configs.remote === to.query.password)
|
|
35
|
-
return true
|
|
36
|
-
if (_configs.remote && to.query.password === undefined) {
|
|
37
|
-
// eslint-disable-next-line no-alert
|
|
38
|
-
const password = prompt('Enter password')
|
|
39
|
-
if (_configs.remote === password)
|
|
40
|
-
return true
|
|
41
|
-
}
|
|
42
|
-
if (to.params.no)
|
|
43
|
-
return { path: `/${to.params.no}` }
|
|
44
|
-
return { path: '' }
|
|
45
|
-
},
|
|
52
|
+
beforeEnter: passwordGuard,
|
|
46
53
|
})
|
|
47
54
|
routes.push({
|
|
48
55
|
path: '/presenter',
|
|
@@ -65,14 +72,14 @@ declare module 'vue-router' {
|
|
|
65
72
|
start: number
|
|
66
73
|
end: number
|
|
67
74
|
note?: string
|
|
68
|
-
|
|
75
|
+
noteHTML?: string
|
|
69
76
|
id: number
|
|
70
77
|
no: number
|
|
71
78
|
filepath: string
|
|
72
79
|
title?: string
|
|
73
80
|
level?: number
|
|
74
81
|
}
|
|
75
|
-
transition?: string |
|
|
82
|
+
transition?: string | TransitionGroupProps | undefined
|
|
76
83
|
// private fields
|
|
77
84
|
__clicksElements: HTMLElement[]
|
|
78
85
|
__preloaded?: boolean
|
package/setup/main.ts
CHANGED
|
@@ -17,7 +17,7 @@ export default function setupMain(context: AppContext) {
|
|
|
17
17
|
context.app.use(StarportPlugin({ keepAlive: true }))
|
|
18
18
|
|
|
19
19
|
// @ts-expect-error inject in runtime
|
|
20
|
-
// eslint-disable-next-line
|
|
20
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
21
21
|
const injection_arg = context
|
|
22
22
|
|
|
23
23
|
/* __injections__ */
|
package/setup/monaco.ts
CHANGED
|
@@ -49,7 +49,7 @@ const setup = createSingletonPromise(async () => {
|
|
|
49
49
|
])
|
|
50
50
|
|
|
51
51
|
// @ts-expect-error injected in runtime
|
|
52
|
-
// eslint-disable-next-line
|
|
52
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
53
53
|
const injection_arg = monaco
|
|
54
54
|
// eslint-disable-next-line prefer-const
|
|
55
55
|
let injection_return: MonacoSetupReturn = {}
|
package/setup/root.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/* __imports__ */
|
|
2
2
|
import { watch } from 'vue'
|
|
3
3
|
import { useHead, useHtmlAttrs } from '@vueuse/head'
|
|
4
|
+
import { nanoid } from 'nanoid'
|
|
4
5
|
import { configs } from '../env'
|
|
5
6
|
import { initSharedState, onPatch, patch } from '../state/shared'
|
|
6
7
|
import { initDrawingState } from '../state/drawings'
|
|
7
|
-
import { clicks, currentPage, getPath, isPresenter } from '../logic/nav'
|
|
8
|
+
import { clicks, currentPage, getPath, isNotesViewer, isPresenter } from '../logic/nav'
|
|
8
9
|
import { router } from '../routes'
|
|
10
|
+
import { TRUST_ORIGINS } from '../constants'
|
|
9
11
|
|
|
10
12
|
export default function setupRoot() {
|
|
11
13
|
// @ts-expect-error injected in runtime
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
const injection_arg = undefined
|
|
14
16
|
|
|
15
17
|
/* __injections__ */
|
|
@@ -20,17 +22,38 @@ export default function setupRoot() {
|
|
|
20
22
|
initSharedState(`${title} - shared`)
|
|
21
23
|
initDrawingState(`${title} - drawings`)
|
|
22
24
|
|
|
25
|
+
const id = `${location.origin}_${nanoid()}`
|
|
26
|
+
|
|
23
27
|
// update shared state
|
|
24
28
|
function updateSharedState() {
|
|
29
|
+
if (isNotesViewer.value)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
// we allow Presenter mode, or Viewer mode from trusted origins to update the shared state
|
|
33
|
+
if (!isPresenter.value && !TRUST_ORIGINS.includes(location.host.split(':')[0]))
|
|
34
|
+
return
|
|
35
|
+
|
|
25
36
|
if (isPresenter.value) {
|
|
26
37
|
patch('page', +currentPage.value)
|
|
27
38
|
patch('clicks', clicks.value)
|
|
28
39
|
}
|
|
40
|
+
else {
|
|
41
|
+
patch('viewerPage', +currentPage.value)
|
|
42
|
+
patch('viewerClicks', clicks.value)
|
|
43
|
+
}
|
|
44
|
+
patch('lastUpdate', {
|
|
45
|
+
id,
|
|
46
|
+
type: isPresenter.value ? 'presenter' : 'viewer',
|
|
47
|
+
time: new Date().getTime(),
|
|
48
|
+
})
|
|
29
49
|
}
|
|
30
50
|
router.afterEach(updateSharedState)
|
|
31
51
|
watch(clicks, updateSharedState)
|
|
32
52
|
|
|
33
53
|
onPatch((state) => {
|
|
54
|
+
const routePath = router.currentRoute.value.path
|
|
55
|
+
if (!routePath.match(/^\/(\d+|presenter)\/?/))
|
|
56
|
+
return
|
|
34
57
|
if (+state.page !== +currentPage.value || clicks.value !== state.clicks) {
|
|
35
58
|
router.replace({
|
|
36
59
|
path: getPath(state.page),
|
package/setup/shortcuts.ts
CHANGED
|
@@ -11,7 +11,7 @@ export default function setupShortcuts() {
|
|
|
11
11
|
const { escape, space, shift, left, right, up, down, enter, d, g, o } = magicKeys
|
|
12
12
|
|
|
13
13
|
// @ts-expect-error injected in runtime
|
|
14
|
-
// eslint-disable-next-line
|
|
14
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
15
15
|
const injection_arg: NavOperations = {
|
|
16
16
|
next,
|
|
17
17
|
prev,
|
package/state/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { breakpointsTailwind, isClient, useActiveElement, useBreakpoints, useFullscreen,
|
|
1
|
+
import { breakpointsTailwind, isClient, useActiveElement, useBreakpoints, useFullscreen, useLocalStorage, useMagicKeys, useToggle, useWindowSize } from '@vueuse/core'
|
|
2
2
|
import { computed, ref } from 'vue'
|
|
3
3
|
import { slideAspect } from '../env'
|
|
4
4
|
|
|
@@ -20,13 +20,13 @@ export const activeElement = useActiveElement()
|
|
|
20
20
|
export const isInputting = computed(() => ['INPUT', 'TEXTAREA'].includes(activeElement.value?.tagName || '') || activeElement.value?.classList.contains('CodeMirror-code'))
|
|
21
21
|
export const isOnFocus = computed(() => ['BUTTON', 'A'].includes(activeElement.value?.tagName || ''))
|
|
22
22
|
|
|
23
|
-
export const currentCamera =
|
|
24
|
-
export const currentMic =
|
|
25
|
-
export const slideScale =
|
|
23
|
+
export const currentCamera = useLocalStorage<string>('slidev-camera', 'default')
|
|
24
|
+
export const currentMic = useLocalStorage<string>('slidev-mic', 'default')
|
|
25
|
+
export const slideScale = useLocalStorage<number>('slidev-scale', 0)
|
|
26
26
|
|
|
27
|
-
export const showOverview =
|
|
28
|
-
export const showPresenterCursor =
|
|
29
|
-
export const showEditor =
|
|
30
|
-
export const editorWidth =
|
|
27
|
+
export const showOverview = useLocalStorage('slidev-show-overview', false)
|
|
28
|
+
export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true)
|
|
29
|
+
export const showEditor = useLocalStorage('slidev-show-editor', false)
|
|
30
|
+
export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 100)
|
|
31
31
|
|
|
32
32
|
export const toggleOverview = useToggle(showOverview)
|
package/state/shared.ts
CHANGED
|
@@ -8,10 +8,27 @@ export interface SharedState {
|
|
|
8
8
|
x: number
|
|
9
9
|
y: number
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
viewerPage: number
|
|
13
|
+
viewerClicks: number
|
|
14
|
+
|
|
15
|
+
lastUpdate?: {
|
|
16
|
+
id: string
|
|
17
|
+
type: 'presenter' | 'viewer'
|
|
18
|
+
time: number
|
|
19
|
+
}
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
const { init, onPatch, patch, state } = createSyncState<SharedState>(serverState, {
|
|
14
23
|
page: 1,
|
|
15
24
|
clicks: 0,
|
|
25
|
+
viewerPage: 1,
|
|
26
|
+
viewerClicks: 0,
|
|
16
27
|
})
|
|
17
|
-
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
init as initSharedState,
|
|
31
|
+
onPatch,
|
|
32
|
+
patch,
|
|
33
|
+
state as sharedState,
|
|
34
|
+
}
|
package/styles/index.css
CHANGED
|
@@ -15,22 +15,22 @@ html {
|
|
|
15
15
|
background: transparent;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
.icon-btn {
|
|
18
|
+
.slidev-icon-btn {
|
|
19
19
|
@apply inline-block cursor-pointer select-none !outline-none;
|
|
20
20
|
@apply opacity-75 transition duration-200 ease-in-out align-middle rounded p-1;
|
|
21
21
|
@apply hover:(opacity-100 bg-gray-400 bg-opacity-10);
|
|
22
22
|
@apply md:p-2;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
.icon-btn.shallow {
|
|
25
|
+
.slidev-icon-btn.shallow {
|
|
26
26
|
@apply opacity-30
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
.icon-btn.active {
|
|
29
|
+
.slidev-icon-btn.active {
|
|
30
30
|
@apply opacity-100
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
.icon-btn.disabled {
|
|
33
|
+
.slidev-icon-btn.disabled {
|
|
34
34
|
@apply opacity-25 pointer-events-none;
|
|
35
35
|
}
|
|
36
36
|
|