@slidev/client 0.48.0-beta.0 → 0.48.0-beta.2
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/VClicks.ts +10 -10
- package/internals/DrawingControls.vue +27 -39
- package/internals/Editor.vue +63 -33
- package/internals/IconButton.vue +12 -0
- package/internals/NavControls.vue +36 -67
- package/internals/NotesView.vue +7 -10
- package/internals/Play.vue +3 -3
- package/internals/Presenter.vue +9 -11
- package/internals/RecordingControls.vue +10 -14
- package/internals/SlidesOverview.vue +3 -4
- package/logic/nav.ts +5 -5
- package/package.json +6 -6
- package/state/index.ts +4 -2
- package/internals/HiddenText.vue +0 -9
package/builtin/VClicks.ts
CHANGED
|
@@ -62,12 +62,12 @@ export default defineComponent({
|
|
|
62
62
|
}) as T
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
let
|
|
65
|
+
let elements = this.$slots.default?.()
|
|
66
66
|
|
|
67
|
-
if (!
|
|
67
|
+
if (!elements)
|
|
68
68
|
return
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
elements = openAllTopLevelSlots(toArray(elements))
|
|
71
71
|
|
|
72
72
|
const mapSubList = (children: VNodeArrayChildren, depth = 1): VNodeArrayChildren => {
|
|
73
73
|
const vNodes = openAllTopLevelSlots(children).map((i) => {
|
|
@@ -96,7 +96,7 @@ export default defineComponent({
|
|
|
96
96
|
if (depth < +this.depth && Array.isArray(i.children))
|
|
97
97
|
vNode = h(i, {}, mapSubList(i.children, depth))
|
|
98
98
|
else
|
|
99
|
-
vNode =
|
|
99
|
+
vNode = i
|
|
100
100
|
|
|
101
101
|
const delta = thisShowIdx - execIdx
|
|
102
102
|
execIdx = thisShowIdx
|
|
@@ -116,23 +116,23 @@ export default defineComponent({
|
|
|
116
116
|
})
|
|
117
117
|
|
|
118
118
|
// handle ul, ol list
|
|
119
|
-
if (
|
|
120
|
-
return h(
|
|
119
|
+
if (elements.length === 1 && listTags.includes(elements[0].type as string) && Array.isArray(elements[0].children))
|
|
120
|
+
return h(elements[0], {}, [...mapChildren(elements[0].children), lastGap()])
|
|
121
121
|
|
|
122
|
-
if (
|
|
123
|
-
const tableNode =
|
|
122
|
+
if (elements.length === 1 && elements[0].type as string === 'table') {
|
|
123
|
+
const tableNode = elements[0]
|
|
124
124
|
if (Array.isArray(tableNode.children)) {
|
|
125
125
|
return h(tableNode, {}, tableNode.children.map((i) => {
|
|
126
126
|
if (!isVNode(i))
|
|
127
127
|
return i
|
|
128
128
|
else if (i.type === 'tbody' && Array.isArray(i.children))
|
|
129
|
-
return h(i, {}, [mapChildren(i.children), lastGap()])
|
|
129
|
+
return h(i, {}, [...mapChildren(i.children), lastGap()])
|
|
130
130
|
else
|
|
131
131
|
return h(i)
|
|
132
132
|
}))
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
return [mapChildren(
|
|
136
|
+
return [...mapChildren(elements), lastGap()]
|
|
137
137
|
},
|
|
138
138
|
})
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '../logic/drawings'
|
|
14
14
|
import VerticalDivider from './VerticalDivider.vue'
|
|
15
15
|
import Draggable from './Draggable.vue'
|
|
16
|
-
import
|
|
16
|
+
import IconButton from './IconButton.vue'
|
|
17
17
|
|
|
18
18
|
function undo() {
|
|
19
19
|
drauu.undo()
|
|
@@ -40,81 +40,69 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
40
40
|
:initial-x="10"
|
|
41
41
|
:initial-y="10"
|
|
42
42
|
>
|
|
43
|
-
<
|
|
44
|
-
<HiddenText text="Draw with stylus" />
|
|
43
|
+
<IconButton title="Draw with stylus" :class="{ shallow: drawingMode !== 'stylus' }" @click="setDrawingMode('stylus')">
|
|
45
44
|
<carbon:pen />
|
|
46
|
-
</
|
|
47
|
-
<
|
|
48
|
-
<HiddenText text="Draw a line" />
|
|
45
|
+
</IconButton>
|
|
46
|
+
<IconButton title="Draw a line" :class="{ shallow: drawingMode !== 'line' }" @click="setDrawingMode('line')">
|
|
49
47
|
<svg width="1em" height="1em" class="-mt-0.5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
|
|
50
48
|
<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" />
|
|
51
49
|
</svg>
|
|
52
|
-
</
|
|
53
|
-
<
|
|
54
|
-
<HiddenText text="Draw an arrow" />
|
|
50
|
+
</IconButton>
|
|
51
|
+
<IconButton title="Draw an arrow" :class="{ shallow: drawingMode !== 'arrow' }" @click="setDrawingMode('arrow')">
|
|
55
52
|
<carbon:arrow-up-right />
|
|
56
|
-
</
|
|
57
|
-
<
|
|
58
|
-
<HiddenText text="Draw an ellipse" />
|
|
53
|
+
</IconButton>
|
|
54
|
+
<IconButton title="Draw an ellipse" :class="{ shallow: drawingMode !== 'ellipse' }" @click="setDrawingMode('ellipse')">
|
|
59
55
|
<carbon:radio-button />
|
|
60
|
-
</
|
|
61
|
-
<
|
|
62
|
-
<HiddenText text="Draw a rectangle" />
|
|
56
|
+
</IconButton>
|
|
57
|
+
<IconButton title="Draw a rectangle" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
|
|
63
58
|
<carbon:checkbox />
|
|
64
|
-
</
|
|
59
|
+
</IconButton>
|
|
65
60
|
<!-- TODO: not sure why it's not working! -->
|
|
66
|
-
<!-- <
|
|
67
|
-
<HiddenText text="Erase" />
|
|
61
|
+
<!-- <IconButton title="Erase" :class="{ shallow: drawingMode != 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
68
62
|
<carbon:erase />
|
|
69
|
-
</
|
|
63
|
+
</IconButton> -->
|
|
70
64
|
|
|
71
65
|
<VerticalDivider />
|
|
72
66
|
|
|
73
|
-
<
|
|
67
|
+
<IconButton
|
|
74
68
|
v-for="color of brushColors"
|
|
75
69
|
:key="color"
|
|
76
|
-
|
|
70
|
+
title="Set brush color"
|
|
77
71
|
:class="brush.color === color ? 'active' : 'shallow'"
|
|
78
72
|
@click="setBrushColor(color)"
|
|
79
73
|
>
|
|
80
|
-
<HiddenText text="Set brush color" />
|
|
81
74
|
<div
|
|
82
75
|
class="w-6 h-6 transition-all transform border border-gray-400/50"
|
|
83
76
|
:class="brush.color !== color ? 'rounded-1/2 scale-85' : 'rounded-md'"
|
|
84
77
|
:style="drawingEnabled ? { background: color } : { borderColor: color }"
|
|
85
78
|
/>
|
|
86
|
-
</
|
|
79
|
+
</IconButton>
|
|
87
80
|
|
|
88
81
|
<VerticalDivider />
|
|
89
82
|
|
|
90
|
-
<
|
|
91
|
-
<HiddenText text="Undo" />
|
|
83
|
+
<IconButton title="Undo" :class="{ disabled: !canUndo }" @click="undo()">
|
|
92
84
|
<carbon:undo />
|
|
93
|
-
</
|
|
94
|
-
<
|
|
95
|
-
<HiddenText text="Redo" />
|
|
85
|
+
</IconButton>
|
|
86
|
+
<IconButton title="Redo" :class="{ disabled: !canRedo }" @click="redo()">
|
|
96
87
|
<carbon:redo />
|
|
97
|
-
</
|
|
98
|
-
<
|
|
99
|
-
<HiddenText text="Delete" />
|
|
88
|
+
</IconButton>
|
|
89
|
+
<IconButton title="Delete" :class="{ disabled: !canClear }" @click="clearDrauu()">
|
|
100
90
|
<carbon:delete />
|
|
101
|
-
</
|
|
91
|
+
</IconButton>
|
|
102
92
|
|
|
103
93
|
<VerticalDivider />
|
|
104
|
-
<
|
|
105
|
-
<HiddenText :text="drawingPinned ? 'Unpin drawing' : 'Pin drawing'" />
|
|
94
|
+
<IconButton :title="drawingPinned ? 'Unpin drawing' : 'Pin drawing'" :class="{ shallow: !drawingPinned }" @click="drawingPinned = !drawingPinned">
|
|
106
95
|
<carbon:pin-filled v-show="drawingPinned" class="transform -rotate-45" />
|
|
107
96
|
<carbon:pin v-show="!drawingPinned" />
|
|
108
|
-
</
|
|
109
|
-
<
|
|
97
|
+
</IconButton>
|
|
98
|
+
<IconButton
|
|
110
99
|
v-if="drawingEnabled"
|
|
111
|
-
|
|
100
|
+
:title="drawingPinned ? 'Drawing pinned' : 'Drawing unpinned'"
|
|
112
101
|
:class="{ shallow: !drawingEnabled }"
|
|
113
102
|
@click="drawingEnabled = !drawingEnabled"
|
|
114
103
|
>
|
|
115
|
-
<HiddenText :text="drawingPinned ? 'Drawing pinned' : 'Drawing unpinned'" />
|
|
116
104
|
<carbon:error v-show="drawingPinned" />
|
|
117
105
|
<carbon:close-outline v-show="!drawingPinned" />
|
|
118
|
-
</
|
|
106
|
+
</IconButton>
|
|
119
107
|
</Draggable>
|
|
120
108
|
</template>
|
package/internals/Editor.vue
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { throttledWatch, useEventListener } from '@vueuse/core'
|
|
3
|
-
import { computed, onMounted, ref, watch } from 'vue'
|
|
4
|
-
import { activeElement, editorWidth, isInputting, showEditor } from '../state'
|
|
3
|
+
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
|
4
|
+
import { activeElement, editorHeight, editorWidth, isInputting, showEditor, isEditorVertical as vertical } from '../state'
|
|
5
5
|
import { useCodeMirror } from '../setup/codemirror'
|
|
6
6
|
import { currentSlideId, openInEditor } from '../logic/nav'
|
|
7
7
|
import { useDynamicSlideInfo } from '../logic/note'
|
|
8
|
-
import
|
|
8
|
+
import IconButton from './IconButton.vue'
|
|
9
9
|
|
|
10
10
|
const props = defineProps<{
|
|
11
|
-
resize
|
|
11
|
+
resize?: boolean
|
|
12
12
|
}>()
|
|
13
13
|
|
|
14
14
|
const tab = ref<'content' | 'note'>('content')
|
|
@@ -56,8 +56,8 @@ useEventListener('keydown', (e) => {
|
|
|
56
56
|
}
|
|
57
57
|
})
|
|
58
58
|
|
|
59
|
-
onMounted(() => {
|
|
60
|
-
useCodeMirror(
|
|
59
|
+
onMounted(async () => {
|
|
60
|
+
const contentEditor = await useCodeMirror(
|
|
61
61
|
contentInput,
|
|
62
62
|
computed({
|
|
63
63
|
get() { return content.value },
|
|
@@ -77,7 +77,7 @@ onMounted(() => {
|
|
|
77
77
|
},
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
-
useCodeMirror(
|
|
80
|
+
const noteEditor = await useCodeMirror(
|
|
81
81
|
noteInput,
|
|
82
82
|
computed({
|
|
83
83
|
get() { return note.value },
|
|
@@ -94,14 +94,26 @@ onMounted(() => {
|
|
|
94
94
|
fencedCodeBlockDefaultMode: 'javascript',
|
|
95
95
|
},
|
|
96
96
|
)
|
|
97
|
+
|
|
98
|
+
watch([tab, vertical], () => {
|
|
99
|
+
nextTick(() => {
|
|
100
|
+
if (tab.value === 'content')
|
|
101
|
+
contentEditor.refresh()
|
|
102
|
+
else
|
|
103
|
+
noteEditor.refresh()
|
|
104
|
+
})
|
|
105
|
+
})
|
|
97
106
|
})
|
|
98
107
|
|
|
99
108
|
const handlerDown = ref(false)
|
|
100
109
|
function onHandlerDown() {
|
|
101
110
|
handlerDown.value = true
|
|
102
111
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
112
|
+
function updateSize(v?: number) {
|
|
113
|
+
if (vertical.value)
|
|
114
|
+
editorHeight.value = Math.min(Math.max(300, v ?? editorHeight.value), window.innerHeight - 200)
|
|
115
|
+
else
|
|
116
|
+
editorWidth.value = Math.min(Math.max(318, v ?? editorWidth.value), window.innerWidth - 200)
|
|
105
117
|
}
|
|
106
118
|
function switchTab(newTab: typeof tab.value) {
|
|
107
119
|
tab.value = newTab
|
|
@@ -111,14 +123,17 @@ function switchTab(newTab: typeof tab.value) {
|
|
|
111
123
|
|
|
112
124
|
if (props.resize) {
|
|
113
125
|
useEventListener('pointermove', (e) => {
|
|
114
|
-
if (handlerDown.value)
|
|
115
|
-
|
|
126
|
+
if (handlerDown.value) {
|
|
127
|
+
updateSize(vertical.value
|
|
128
|
+
? window.innerHeight - e.pageY
|
|
129
|
+
: window.innerWidth - e.pageX)
|
|
130
|
+
}
|
|
116
131
|
}, { passive: true })
|
|
117
132
|
useEventListener('pointerup', () => {
|
|
118
133
|
handlerDown.value = false
|
|
119
134
|
})
|
|
120
135
|
useEventListener('resize', () => {
|
|
121
|
-
|
|
136
|
+
updateSize()
|
|
122
137
|
})
|
|
123
138
|
}
|
|
124
139
|
|
|
@@ -134,46 +149,61 @@ throttledWatch(
|
|
|
134
149
|
|
|
135
150
|
<template>
|
|
136
151
|
<div
|
|
137
|
-
v-if="resize"
|
|
138
|
-
class="
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
152
|
+
v-if="resize" class="fixed bg-gray-400 select-none opacity-0 hover:opacity-10 z-100"
|
|
153
|
+
:class="vertical ? 'left-0 right-0 w-full h-10px' : 'top-0 bottom-0 w-10px h-full'" :style="{
|
|
154
|
+
opacity: handlerDown ? '0.3' : undefined,
|
|
155
|
+
bottom: vertical ? `${editorHeight - 5}px` : undefined,
|
|
156
|
+
right: !vertical ? `${editorWidth - 5}px` : undefined,
|
|
157
|
+
cursor: vertical ? 'row-resize' : 'col-resize',
|
|
158
|
+
}" @pointerdown="onHandlerDown"
|
|
142
159
|
/>
|
|
143
160
|
<div
|
|
144
161
|
class="shadow bg-main p-4 grid grid-rows-[max-content_1fr] h-full overflow-hidden"
|
|
145
162
|
:class="resize ? 'border-l border-gray-400 border-opacity-20' : ''"
|
|
146
|
-
:style="resize ? {
|
|
163
|
+
:style="resize ? {
|
|
164
|
+
height: vertical ? `${editorHeight}px` : undefined,
|
|
165
|
+
width: !vertical ? `${editorWidth}px` : undefined,
|
|
166
|
+
} : {}"
|
|
147
167
|
>
|
|
148
168
|
<div class="flex pb-2 text-xl -mt-1">
|
|
149
169
|
<div class="mr-4 rounded flex">
|
|
150
|
-
<
|
|
151
|
-
|
|
170
|
+
<IconButton
|
|
171
|
+
title="Switch to content tab" :class="tab === 'content' ? 'text-$slidev-theme-primary' : ''"
|
|
172
|
+
@click="switchTab('content')"
|
|
173
|
+
>
|
|
152
174
|
<carbon:account />
|
|
153
|
-
</
|
|
154
|
-
<
|
|
155
|
-
|
|
175
|
+
</IconButton>
|
|
176
|
+
<IconButton
|
|
177
|
+
title="Switch to notes tab" :class="tab === 'note' ? 'text-$slidev-theme-primary' : ''"
|
|
178
|
+
@click="switchTab('note')"
|
|
179
|
+
>
|
|
156
180
|
<carbon:align-box-bottom-right />
|
|
157
|
-
</
|
|
181
|
+
</IconButton>
|
|
158
182
|
</div>
|
|
159
183
|
<span class="text-2xl pt-1">
|
|
160
184
|
{{ tab === 'content' ? 'Slide' : 'Notes' }}
|
|
161
185
|
</span>
|
|
162
186
|
<div class="flex-auto" />
|
|
163
|
-
<
|
|
164
|
-
<
|
|
187
|
+
<template v-if="resize">
|
|
188
|
+
<IconButton v-if="vertical" title="Dock to right" @click="vertical = false">
|
|
189
|
+
<carbon:open-panel-right />
|
|
190
|
+
</IconButton>
|
|
191
|
+
<IconButton v-else title="Dock to bottom" @click="vertical = true">
|
|
192
|
+
<carbon:open-panel-bottom />
|
|
193
|
+
</IconButton>
|
|
194
|
+
</template>
|
|
195
|
+
<IconButton title="Open in editor" @click="openInEditor()">
|
|
165
196
|
<carbon:launch />
|
|
166
|
-
</
|
|
167
|
-
<
|
|
168
|
-
<HiddenText text="Close" />
|
|
197
|
+
</IconButton>
|
|
198
|
+
<IconButton title="Close" @click="close">
|
|
169
199
|
<carbon:close />
|
|
170
|
-
</
|
|
200
|
+
</IconButton>
|
|
171
201
|
</div>
|
|
172
|
-
<div class="
|
|
173
|
-
<div v-show="tab === 'content'" class="
|
|
202
|
+
<div class="overflow-hidden">
|
|
203
|
+
<div v-show="tab === 'content'" class="w-full h-full">
|
|
174
204
|
<textarea ref="contentInput" placeholder="Create slide content..." />
|
|
175
205
|
</div>
|
|
176
|
-
<div v-show="tab === 'note'" class="
|
|
206
|
+
<div v-show="tab === 'note'" class="w-full h-full">
|
|
177
207
|
<textarea ref="noteInput" placeholder="Write some notes..." />
|
|
178
208
|
</div>
|
|
179
209
|
</div>
|
|
@@ -8,7 +8,7 @@ import { configs } from '../env'
|
|
|
8
8
|
import Settings from './Settings.vue'
|
|
9
9
|
import MenuButton from './MenuButton.vue'
|
|
10
10
|
import VerticalDivider from './VerticalDivider.vue'
|
|
11
|
-
import
|
|
11
|
+
import IconButton from './IconButton.vue'
|
|
12
12
|
|
|
13
13
|
// @ts-expect-error virtual module
|
|
14
14
|
import CustomNavControls from '/@slidev/custom-nav-controls'
|
|
@@ -52,47 +52,27 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
52
52
|
:class="barStyle"
|
|
53
53
|
@mouseleave="onMouseLeave"
|
|
54
54
|
>
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<template v-else>
|
|
61
|
-
<HiddenText text="Enter fullscreen" />
|
|
62
|
-
<carbon:maximize />
|
|
63
|
-
</template>
|
|
64
|
-
</button>
|
|
65
|
-
|
|
66
|
-
<button class="slidev-icon-btn" :class="{ disabled: !hasPrev }" @click="prev">
|
|
67
|
-
<HiddenText text="Go to previous slide" />
|
|
55
|
+
<IconButton v-if="!isEmbedded" :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
|
|
56
|
+
<carbon:minimize v-if="isFullscreen" />
|
|
57
|
+
<carbon:maximize v-else />
|
|
58
|
+
</IconButton>
|
|
59
|
+
<IconButton :class="{ disabled: !hasPrev }" title="Go to previous slide" @click="prev">
|
|
68
60
|
<carbon:arrow-left />
|
|
69
|
-
</
|
|
70
|
-
|
|
71
|
-
<button class="slidev-icon-btn" :class="{ disabled: !hasNext }" title="Next" @click="next">
|
|
72
|
-
<HiddenText text="Go to next slide" />
|
|
61
|
+
</IconButton>
|
|
62
|
+
<IconButton :class="{ disabled: !hasNext }" title="Go to next slide" @click="next">
|
|
73
63
|
<carbon:arrow-right />
|
|
74
|
-
</
|
|
75
|
-
|
|
76
|
-
<button v-if="!isEmbedded" class="slidev-icon-btn" title="Slides overview" @click="toggleOverview()">
|
|
77
|
-
<HiddenText text="Show slide overview" />
|
|
64
|
+
</IconButton>
|
|
65
|
+
<IconButton v-if="!isEmbedded" title="Show slide overview" @click="toggleOverview()">
|
|
78
66
|
<carbon:apps />
|
|
79
|
-
</
|
|
80
|
-
|
|
81
|
-
<button
|
|
67
|
+
</IconButton>
|
|
68
|
+
<IconButton
|
|
82
69
|
v-if="!isColorSchemaConfigured"
|
|
83
|
-
|
|
84
|
-
title="Toggle dark mode"
|
|
70
|
+
:title="isDark ? 'Switch to light mode theme' : 'Switch to dark mode theme'"
|
|
85
71
|
@click="toggleDark()"
|
|
86
72
|
>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
</template>
|
|
91
|
-
<template v-else>
|
|
92
|
-
<HiddenText text="Switch to dark mode theme" />
|
|
93
|
-
<carbon-sun />
|
|
94
|
-
</template>
|
|
95
|
-
</button>
|
|
73
|
+
<carbon-moon v-if="isDark" />
|
|
74
|
+
<carbon-sun v-else />
|
|
75
|
+
</IconButton>
|
|
96
76
|
|
|
97
77
|
<VerticalDivider />
|
|
98
78
|
|
|
@@ -102,33 +82,25 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
102
82
|
<VerticalDivider />
|
|
103
83
|
</template>
|
|
104
84
|
|
|
105
|
-
<
|
|
85
|
+
<IconButton
|
|
106
86
|
v-if="isPresenter"
|
|
107
|
-
|
|
108
|
-
title="Show presenter cursor"
|
|
87
|
+
:title="showPresenterCursor ? 'Hide presenter cursor' : 'Show presenter cursor'"
|
|
109
88
|
@click="showPresenterCursor = !showPresenterCursor"
|
|
110
89
|
>
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
</template>
|
|
115
|
-
<template v-else>
|
|
116
|
-
<HiddenText text="Show presenter cursor" />
|
|
117
|
-
<ph-cursor-duotone />
|
|
118
|
-
</template>
|
|
119
|
-
</button>
|
|
90
|
+
<ph-cursor-fill v-if="showPresenterCursor" />
|
|
91
|
+
<ph-cursor-duotone v-else />
|
|
92
|
+
</IconButton>
|
|
120
93
|
</template>
|
|
121
94
|
|
|
122
95
|
<template v-if="__SLIDEV_FEATURE_DRAWINGS__ && (!configs.drawings.presenterOnly || isPresenter) && !isEmbedded">
|
|
123
|
-
<
|
|
124
|
-
<HiddenText v-if="drawingEnabled" :text="drawingEnabled ? 'Hide drawing toolbar' : 'Show drawing toolbar'" />
|
|
96
|
+
<IconButton class="relative" :title="drawingEnabled ? 'Hide drawing toolbar' : 'Show drawing toolbar'" @click="drawingEnabled = !drawingEnabled">
|
|
125
97
|
<carbon:pen />
|
|
126
98
|
<div
|
|
127
99
|
v-if="drawingEnabled"
|
|
128
100
|
class="absolute left-1 right-1 bottom-0 h-0.7 rounded-full"
|
|
129
101
|
:style="{ background: brush.color }"
|
|
130
102
|
/>
|
|
131
|
-
</
|
|
103
|
+
</IconButton>
|
|
132
104
|
<VerticalDivider />
|
|
133
105
|
</template>
|
|
134
106
|
|
|
@@ -140,43 +112,40 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
140
112
|
<carbon:user-speaker />
|
|
141
113
|
</RouterLink>
|
|
142
114
|
|
|
143
|
-
<
|
|
115
|
+
<IconButton
|
|
144
116
|
v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__"
|
|
145
|
-
|
|
117
|
+
:title="showEditor ? 'Hide editor' : 'Show editor'"
|
|
118
|
+
class="<md:hidden"
|
|
146
119
|
@click="showEditor = !showEditor"
|
|
147
120
|
>
|
|
148
|
-
<HiddenText :text="showEditor ? 'Hide editor' : 'Show editor'" />
|
|
149
121
|
<carbon:text-annotation-toggle />
|
|
150
|
-
</
|
|
122
|
+
</IconButton>
|
|
151
123
|
|
|
152
|
-
<
|
|
124
|
+
<IconButton v-if="isPresenter" title="Toggle Presenter Layout" @click="togglePresenterLayout">
|
|
153
125
|
<carbon:template />
|
|
154
126
|
{{ presenterLayout }}
|
|
155
|
-
</
|
|
127
|
+
</IconButton>
|
|
156
128
|
</template>
|
|
157
129
|
<template v-if="!__DEV__">
|
|
158
|
-
<
|
|
159
|
-
<HiddenText text="Download as PDF" />
|
|
130
|
+
<IconButton v-if="configs.download" title="Download as PDF" @click="downloadPDF">
|
|
160
131
|
<carbon:download />
|
|
161
|
-
</
|
|
132
|
+
</IconButton>
|
|
162
133
|
</template>
|
|
163
134
|
|
|
164
|
-
<
|
|
135
|
+
<IconButton
|
|
165
136
|
v-if="!isPresenter && configs.info && !isEmbedded"
|
|
166
|
-
|
|
137
|
+
title="Show info"
|
|
167
138
|
@click="showInfoDialog = !showInfoDialog"
|
|
168
139
|
>
|
|
169
|
-
<HiddenText text="Show info" />
|
|
170
140
|
<carbon:information />
|
|
171
|
-
</
|
|
141
|
+
</IconButton>
|
|
172
142
|
|
|
173
143
|
<template v-if="!isPresenter && !isEmbedded">
|
|
174
144
|
<MenuButton>
|
|
175
145
|
<template #button>
|
|
176
|
-
<
|
|
177
|
-
<HiddenText text="Adjust settings" />
|
|
146
|
+
<IconButton title="Adjust settings">
|
|
178
147
|
<carbon:settings-adjust />
|
|
179
|
-
</
|
|
148
|
+
</IconButton>
|
|
180
149
|
</template>
|
|
181
150
|
<template #menu>
|
|
182
151
|
<Settings />
|
package/internals/NotesView.vue
CHANGED
|
@@ -8,7 +8,7 @@ import { fullscreen } from '../state'
|
|
|
8
8
|
import { total } from '../logic/nav'
|
|
9
9
|
import { rawRoutes } from '../routes'
|
|
10
10
|
import NoteDisplay from './NoteDisplay.vue'
|
|
11
|
-
import
|
|
11
|
+
import IconButton from './IconButton.vue'
|
|
12
12
|
|
|
13
13
|
const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
|
|
14
14
|
useHead({
|
|
@@ -55,19 +55,16 @@ function decreaseFontSize() {
|
|
|
55
55
|
</div>
|
|
56
56
|
<div class="flex-none border-t border-gray-400 border-opacity-20">
|
|
57
57
|
<div class="flex gap-1 items-center px-6 py-3">
|
|
58
|
-
<
|
|
59
|
-
<HiddenText :text="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" />
|
|
58
|
+
<IconButton :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
|
|
60
59
|
<carbon:minimize v-if="isFullscreen" />
|
|
61
60
|
<carbon:maximize v-else />
|
|
62
|
-
</
|
|
63
|
-
<
|
|
64
|
-
<HiddenText text="Increase font size" />
|
|
61
|
+
</IconButton>
|
|
62
|
+
<IconButton title="Increase font size" @click="increaseFontSize">
|
|
65
63
|
<carbon:zoom-in />
|
|
66
|
-
</
|
|
67
|
-
<
|
|
68
|
-
<HiddenText text="Decrease font size" />
|
|
64
|
+
</IconButton>
|
|
65
|
+
<IconButton title="Decrease font size" @click="decreaseFontSize">
|
|
69
66
|
<carbon:zoom-out />
|
|
70
|
-
</
|
|
67
|
+
</IconButton>
|
|
71
68
|
<div class="flex-auto" />
|
|
72
69
|
<div class="p2 text-center">
|
|
73
70
|
{{ pageNo }} / {{ total }}
|
package/internals/Play.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, ref, shallowRef } from 'vue'
|
|
3
|
-
import { isScreenVertical, showEditor, slideScale, windowSize } from '../state'
|
|
3
|
+
import { isEditorVertical, isScreenVertical, showEditor, slideScale, windowSize } from '../state'
|
|
4
4
|
import { isEmbedded, isPrintMode, next, prev, useSwipeControls } from '../logic/nav'
|
|
5
5
|
import { isDrawing } from '../logic/drawings'
|
|
6
6
|
import { registerShortcuts } from '../logic/shortcuts'
|
|
@@ -42,7 +42,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
42
42
|
|
|
43
43
|
<template>
|
|
44
44
|
<PrintStyle v-if="isPrintMode" />
|
|
45
|
-
<div id="page-root" ref="root" class="grid grid-cols-[1fr_max-content]" :style="themeVars">
|
|
45
|
+
<div id="page-root" ref="root" class="grid" :class="isEditorVertical ? 'grid-rows-[1fr_max-content]' : 'grid-cols-[1fr_max-content]'" :style="themeVars">
|
|
46
46
|
<SlideContainer
|
|
47
47
|
class="w-full h-full"
|
|
48
48
|
:style="{ background: 'var(--slidev-slide-container-background, black)' }"
|
|
@@ -58,7 +58,7 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
58
58
|
<div
|
|
59
59
|
class="absolute bottom-0 left-0 transition duration-300 opacity-0 hover:opacity-100"
|
|
60
60
|
:class="[
|
|
61
|
-
persistNav ? 'opacity-100 right-0' : 'opacity-0 p-2',
|
|
61
|
+
persistNav ? '!opacity-100 right-0' : 'opacity-0 p-2',
|
|
62
62
|
isDrawing ? 'pointer-events-none' : '',
|
|
63
63
|
]"
|
|
64
64
|
>
|
package/internals/Presenter.vue
CHANGED
|
@@ -20,7 +20,7 @@ import Goto from './Goto.vue'
|
|
|
20
20
|
import SlidesShow from './SlidesShow.vue'
|
|
21
21
|
import SlideWrapper from './SlideWrapper'
|
|
22
22
|
import DrawingControls from './DrawingControls.vue'
|
|
23
|
-
import
|
|
23
|
+
import IconButton from './IconButton.vue'
|
|
24
24
|
|
|
25
25
|
const main = ref<HTMLDivElement>()
|
|
26
26
|
|
|
@@ -150,21 +150,19 @@ onMounted(() => {
|
|
|
150
150
|
:style="{ fontSize: `${presenterNotesFontSize}em` }"
|
|
151
151
|
/>
|
|
152
152
|
<div class="border-t border-main py-1 px-2 text-sm">
|
|
153
|
-
<
|
|
154
|
-
<HiddenText text="Increase font size" />
|
|
153
|
+
<IconButton title="Increase font size" @click="increasePresenterFontSize">
|
|
155
154
|
<carbon:zoom-in />
|
|
156
|
-
</
|
|
157
|
-
<
|
|
158
|
-
<HiddenText text="Decrease font size" />
|
|
155
|
+
</IconButton>
|
|
156
|
+
<IconButton title="Decrease font size" @click="decreasePresenterFontSize">
|
|
159
157
|
<carbon:zoom-out />
|
|
160
|
-
</
|
|
161
|
-
<
|
|
158
|
+
</IconButton>
|
|
159
|
+
<IconButton
|
|
162
160
|
v-if="__DEV__"
|
|
163
|
-
|
|
161
|
+
title="Edit Notes"
|
|
162
|
+
@click="notesEditing = !notesEditing"
|
|
164
163
|
>
|
|
165
|
-
<HiddenText text="Edit Notes" />
|
|
166
164
|
<carbon:edit />
|
|
167
|
-
</
|
|
165
|
+
</IconButton>
|
|
168
166
|
</div>
|
|
169
167
|
</div>
|
|
170
168
|
<div class="grid-section bottom">
|
|
@@ -5,7 +5,7 @@ import { recorder } from '../logic/recording'
|
|
|
5
5
|
import { currentCamera, showRecordingDialog } from '../state'
|
|
6
6
|
import DevicesList from './DevicesList.vue'
|
|
7
7
|
import MenuButton from './MenuButton.vue'
|
|
8
|
-
import
|
|
8
|
+
import IconButton from './IconButton.vue'
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
11
|
recording,
|
|
@@ -34,33 +34,29 @@ onMounted(() => {
|
|
|
34
34
|
</script>
|
|
35
35
|
|
|
36
36
|
<template>
|
|
37
|
-
<
|
|
37
|
+
<IconButton
|
|
38
38
|
v-if="currentCamera !== 'none'"
|
|
39
|
-
class="
|
|
39
|
+
class="<md:hidden"
|
|
40
40
|
:class="{ 'text-green-500': Boolean(showAvatar && streamCamera) }"
|
|
41
|
-
title="
|
|
41
|
+
title="Toggle camera view"
|
|
42
42
|
@click="toggleAvatar"
|
|
43
43
|
>
|
|
44
|
-
<HiddenText text="Toggle camera view" />
|
|
45
44
|
<carbon:user-avatar />
|
|
46
|
-
</
|
|
45
|
+
</IconButton>
|
|
47
46
|
|
|
48
|
-
<
|
|
49
|
-
class="slidev-icon-btn"
|
|
47
|
+
<IconButton
|
|
50
48
|
:class="{ 'text-red-500': recording }"
|
|
51
|
-
title="
|
|
49
|
+
:title="recording ? 'Stop record video' : 'Record video'"
|
|
52
50
|
@click="toggleRecording"
|
|
53
51
|
>
|
|
54
|
-
<HiddenText :text="recording ? 'Stop record video' : 'Record video'" />
|
|
55
52
|
<carbon:stop-outline v-if="recording" />
|
|
56
53
|
<carbon:video v-else />
|
|
57
|
-
</
|
|
54
|
+
</IconButton>
|
|
58
55
|
<MenuButton :disabled="recording">
|
|
59
56
|
<template #button>
|
|
60
|
-
<
|
|
61
|
-
<HiddenText text="Select recording device" />
|
|
57
|
+
<IconButton title="Select recording device" class="h-full !text-sm !px-0">
|
|
62
58
|
<carbon:chevron-up class="opacity-50" />
|
|
63
|
-
</
|
|
59
|
+
</IconButton>
|
|
64
60
|
</template>
|
|
65
61
|
<template #menu>
|
|
66
62
|
<DevicesList />
|
|
@@ -10,7 +10,7 @@ import { getSlideClass } from '../utils'
|
|
|
10
10
|
import SlideContainer from './SlideContainer.vue'
|
|
11
11
|
import SlideWrapper from './SlideWrapper'
|
|
12
12
|
import DrawingPreview from './DrawingPreview.vue'
|
|
13
|
-
import
|
|
13
|
+
import IconButton from './IconButton.vue'
|
|
14
14
|
|
|
15
15
|
const props = defineProps<{ modelValue: boolean }>()
|
|
16
16
|
|
|
@@ -163,8 +163,7 @@ watchEffect(() => {
|
|
|
163
163
|
</div>
|
|
164
164
|
</div>
|
|
165
165
|
</Transition>
|
|
166
|
-
<
|
|
167
|
-
<HiddenText text="Close" />
|
|
166
|
+
<IconButton v-if="value" title="Close" class="fixed text-2xl top-4 right-4 text-gray-400" @click="close">
|
|
168
167
|
<carbon:close />
|
|
169
|
-
</
|
|
168
|
+
</IconButton>
|
|
170
169
|
</template>
|
package/logic/nav.ts
CHANGED
|
@@ -83,9 +83,9 @@ watch(currentRoute, (next, prev) => {
|
|
|
83
83
|
navDirection.value = Number(next?.path) - Number(prev?.path)
|
|
84
84
|
})
|
|
85
85
|
|
|
86
|
-
export function next() {
|
|
86
|
+
export async function next() {
|
|
87
87
|
if (clicksTotal.value <= queryClicks.value)
|
|
88
|
-
nextSlide()
|
|
88
|
+
await nextSlide()
|
|
89
89
|
else
|
|
90
90
|
queryClicks.value += 1
|
|
91
91
|
}
|
|
@@ -101,9 +101,9 @@ export function getPath(no: number | string) {
|
|
|
101
101
|
return isPresenter.value ? `/presenter/${no}` : `/${no}`
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
export function nextSlide() {
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
export async function nextSlide() {
|
|
105
|
+
if (currentPage.value < rawRoutes.length)
|
|
106
|
+
await go(currentPage.value + 1)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export async function prevSlide(lastClicks = true) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
|
-
"version": "0.48.0-beta.
|
|
3
|
+
"version": "0.48.0-beta.2",
|
|
4
4
|
"description": "Presentation slides for developers",
|
|
5
5
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"@antfu/utils": "^0.7.7",
|
|
23
23
|
"@iconify-json/carbon": "^1.1.30",
|
|
24
24
|
"@iconify-json/ph": "^1.1.11",
|
|
25
|
-
"@shikijs/vitepress-twoslash": "^1.1.
|
|
25
|
+
"@shikijs/vitepress-twoslash": "^1.1.3",
|
|
26
26
|
"@unhead/vue": "^1.8.10",
|
|
27
27
|
"@unocss/reset": "^0.58.5",
|
|
28
28
|
"@vueuse/core": "^10.7.2",
|
|
29
29
|
"@vueuse/math": "^10.7.2",
|
|
30
30
|
"@vueuse/motion": "^2.0.0",
|
|
31
|
-
"codemirror": "^5.65.
|
|
31
|
+
"codemirror": "^5.65.16",
|
|
32
32
|
"defu": "^6.1.4",
|
|
33
33
|
"drauu": "^0.3.7",
|
|
34
34
|
"file-saver": "^2.0.5",
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"unocss": "^0.58.5",
|
|
47
47
|
"vue": "^3.4.19",
|
|
48
48
|
"vue-router": "^4.2.5",
|
|
49
|
-
"@slidev/
|
|
50
|
-
"@slidev/
|
|
49
|
+
"@slidev/parser": "0.48.0-beta.2",
|
|
50
|
+
"@slidev/types": "0.48.0-beta.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"vite": "^5.1.
|
|
53
|
+
"vite": "^5.1.3"
|
|
54
54
|
}
|
|
55
55
|
}
|
package/state/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const breakpoints = useBreakpoints({
|
|
|
13
13
|
})
|
|
14
14
|
export const windowSize = useWindowSize()
|
|
15
15
|
export const magicKeys = useMagicKeys()
|
|
16
|
-
export const isScreenVertical = computed(() => windowSize.height.value - windowSize.width.value / slideAspect >
|
|
16
|
+
export const isScreenVertical = computed(() => windowSize.height.value - windowSize.width.value / slideAspect > 120)
|
|
17
17
|
export const fullscreen = useFullscreen(isClient ? document.body : null)
|
|
18
18
|
|
|
19
19
|
export const activeElement = useActiveElement()
|
|
@@ -27,7 +27,9 @@ export const slideScale = useLocalStorage<number>('slidev-scale', 0)
|
|
|
27
27
|
export const showOverview = useLocalStorage('slidev-show-overview', false)
|
|
28
28
|
export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true)
|
|
29
29
|
export const showEditor = useLocalStorage('slidev-show-editor', false)
|
|
30
|
-
export const
|
|
30
|
+
export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false)
|
|
31
|
+
export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 318)
|
|
32
|
+
export const editorHeight = useLocalStorage('slidev-editor-height', isClient ? window.innerHeight * 0.4 : 300)
|
|
31
33
|
|
|
32
34
|
export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1)
|
|
33
35
|
export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1)
|