@slidev/client 0.28.10 → 0.29.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/Toc.vue +3 -1
- package/builtin/TocList.vue +15 -4
- package/internals/DevicesList.vue +19 -1
- package/internals/NavControls.vue +4 -0
- package/internals/RecordingDialog.vue +8 -5
- package/logic/recording.ts +29 -6
- package/package.json +11 -11
package/builtin/Toc.vue
CHANGED
|
@@ -15,12 +15,14 @@ import { tree } from '../logic/nav'
|
|
|
15
15
|
const props = withDefaults(
|
|
16
16
|
defineProps<{
|
|
17
17
|
columns?: string | number
|
|
18
|
+
listClass?: string | string[]
|
|
18
19
|
maxDepth?: string | number
|
|
19
20
|
minDepth?: string | number
|
|
20
21
|
mode?: 'all' | 'onlyCurrentTree' | 'onlySiblings'
|
|
21
22
|
}>(),
|
|
22
23
|
{
|
|
23
24
|
columns: 1,
|
|
25
|
+
listClass: '',
|
|
24
26
|
maxDepth: Infinity,
|
|
25
27
|
minDepth: 1,
|
|
26
28
|
mode: 'all',
|
|
@@ -78,6 +80,6 @@ const toc = computed(() => {
|
|
|
78
80
|
|
|
79
81
|
<template>
|
|
80
82
|
<div class="slidev-toc" :style="`column-count:${columns}`">
|
|
81
|
-
<TocList :level="1" :list="toc" />
|
|
83
|
+
<TocList :level="1" :list="toc" :list-class="listClass" />
|
|
82
84
|
</div>
|
|
83
85
|
</template>
|
package/builtin/TocList.vue
CHANGED
|
@@ -7,19 +7,30 @@ Usage:
|
|
|
7
7
|
<TocList :list="list"/>
|
|
8
8
|
-->
|
|
9
9
|
<script setup lang="ts">
|
|
10
|
+
import { computed } from 'vue'
|
|
11
|
+
import { toArray } from '@antfu/utils'
|
|
10
12
|
import type { TocItem } from '../logic/nav'
|
|
11
13
|
|
|
12
|
-
withDefaults(defineProps<{
|
|
14
|
+
const props = withDefaults(defineProps<{
|
|
13
15
|
level: number
|
|
14
16
|
list: TocItem[]
|
|
17
|
+
listClass?: string | string[]
|
|
15
18
|
}>(), { level: 1 })
|
|
19
|
+
|
|
20
|
+
const classes = computed(() => {
|
|
21
|
+
return [
|
|
22
|
+
...toArray(props.listClass || []),
|
|
23
|
+
'slidev-toc-list',
|
|
24
|
+
`slidev-toc-list-level-${props.level}`,
|
|
25
|
+
]
|
|
26
|
+
})
|
|
16
27
|
</script>
|
|
17
28
|
|
|
18
29
|
<template>
|
|
19
|
-
<
|
|
30
|
+
<ol v-if="list && list.length > 0" :class="classes">
|
|
20
31
|
<li v-for="item in list" :key="item.path" :class="['slidev-toc-item', {'slidev-toc-item-active': item.active}, {'slidev-toc-item-parent-active': item.activeParent}]">
|
|
21
32
|
<RouterLink :to="item.path" v-html="item.title" />
|
|
22
|
-
<TocList :level="level + 1" :list="item.children" />
|
|
33
|
+
<TocList :level="level + 1" :list="item.children" :list-class="listClass" />
|
|
23
34
|
</li>
|
|
24
|
-
</
|
|
35
|
+
</ol>
|
|
25
36
|
</template>
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import { currentCamera, currentMic } from '../state'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
cameras,
|
|
6
|
+
ensureDevicesListPermissions,
|
|
7
|
+
microphones,
|
|
8
|
+
mimeExtMap,
|
|
9
|
+
mimeType,
|
|
10
|
+
supportedMimeTypes,
|
|
11
|
+
} from '../logic/recording'
|
|
5
12
|
import SelectList from './SelectList.vue'
|
|
6
13
|
import type { SelectionItem } from './types'
|
|
7
14
|
|
|
@@ -27,6 +34,11 @@ const microphonesItems = computed<SelectionItem<string>[]>(() => [
|
|
|
27
34
|
})),
|
|
28
35
|
])
|
|
29
36
|
|
|
37
|
+
const mimeTypeItems = supportedMimeTypes.map(mime => ({
|
|
38
|
+
value: mime,
|
|
39
|
+
display: mimeExtMap[mime].toUpperCase(),
|
|
40
|
+
}))
|
|
41
|
+
|
|
30
42
|
ensureDevicesListPermissions()
|
|
31
43
|
</script>
|
|
32
44
|
|
|
@@ -34,5 +46,11 @@ ensureDevicesListPermissions()
|
|
|
34
46
|
<div class="text-sm">
|
|
35
47
|
<SelectList v-model="currentCamera" title="Camera" :items="camerasItems" />
|
|
36
48
|
<SelectList v-model="currentMic" title="Microphone" :items="microphonesItems" />
|
|
49
|
+
<SelectList
|
|
50
|
+
v-if="mimeTypeItems.length"
|
|
51
|
+
v-model="mimeType"
|
|
52
|
+
title="mimeType"
|
|
53
|
+
:items="mimeTypeItems"
|
|
54
|
+
/>
|
|
37
55
|
</div>
|
|
38
56
|
</template>
|
|
@@ -8,6 +8,8 @@ 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
|
+
// @ts-expect-error virtual module
|
|
12
|
+
import CustomNavControls from '/@slidev/custom-nav-controls'
|
|
11
13
|
|
|
12
14
|
const props = defineProps({
|
|
13
15
|
persist: {
|
|
@@ -153,6 +155,8 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
|
|
|
153
155
|
<span class="opacity-50">/ {{ total }}</span>
|
|
154
156
|
</div>
|
|
155
157
|
</div>
|
|
158
|
+
|
|
159
|
+
<CustomNavControls />
|
|
156
160
|
</div>
|
|
157
161
|
</nav>
|
|
158
162
|
</template>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useVModel } from '@vueuse/core'
|
|
3
3
|
import { nextTick } from 'vue'
|
|
4
|
-
import { getFilename, recordCamera, recorder, recordingName } from '../logic/recording'
|
|
4
|
+
import { getFilename, mimeType, recordCamera, recorder, recordingName } from '../logic/recording'
|
|
5
5
|
import Modal from './Modal.vue'
|
|
6
6
|
import DevicesList from './DevicesList.vue'
|
|
7
7
|
|
|
@@ -25,7 +25,9 @@ function close() {
|
|
|
25
25
|
async function start() {
|
|
26
26
|
close()
|
|
27
27
|
await nextTick()
|
|
28
|
-
startRecording(
|
|
28
|
+
startRecording({
|
|
29
|
+
mimeType: mimeType.value,
|
|
30
|
+
})
|
|
29
31
|
}
|
|
30
32
|
</script>
|
|
31
33
|
|
|
@@ -46,7 +48,7 @@ async function start() {
|
|
|
46
48
|
placeholder="Enter the title..."
|
|
47
49
|
>
|
|
48
50
|
<div class="text-xs w-full opacity-50 py-2">
|
|
49
|
-
<div>This will be used in the output filename that might <br>help you better
|
|
51
|
+
<div>This will be used in the output filename that might <br>help you better organize your recording chips.</div>
|
|
50
52
|
</div>
|
|
51
53
|
</div>
|
|
52
54
|
<div class="form-check">
|
|
@@ -57,15 +59,16 @@ async function start() {
|
|
|
57
59
|
>
|
|
58
60
|
<label for="record-camera" @click="recordCamera = !recordCamera">Record camera separately</label>
|
|
59
61
|
</div>
|
|
62
|
+
|
|
60
63
|
<div class="text-xs w-full opacity-50">
|
|
61
64
|
<div class="mt-2 opacity-50">
|
|
62
65
|
Enumerated filenames
|
|
63
66
|
</div>
|
|
64
67
|
<div class="font-mono">
|
|
65
|
-
{{ getFilename('screen') }}
|
|
68
|
+
{{ getFilename('screen', mimeType) }}
|
|
66
69
|
</div>
|
|
67
70
|
<div v-if="recordCamera" class="font-mono">
|
|
68
|
-
{{ getFilename('camera') }}
|
|
71
|
+
{{ getFilename('camera', mimeType) }}
|
|
69
72
|
</div>
|
|
70
73
|
</div>
|
|
71
74
|
</div>
|
package/logic/recording.ts
CHANGED
|
@@ -1,24 +1,44 @@
|
|
|
1
1
|
import type { Ref } from 'vue'
|
|
2
2
|
import { nextTick, ref, shallowRef, watch } from 'vue'
|
|
3
|
-
import { useDevicesList, useEventListener } from '@vueuse/core'
|
|
3
|
+
import { useDevicesList, useEventListener, useStorage } 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'
|
|
7
7
|
import { currentCamera, currentMic } from '../state'
|
|
8
8
|
|
|
9
|
+
type Defined<T> = T extends undefined ? never : T
|
|
10
|
+
type MimeType = Defined<RecorderOptions['mimeType']>
|
|
11
|
+
|
|
9
12
|
export const recordingName = ref('')
|
|
10
13
|
export const recordCamera = ref(true)
|
|
14
|
+
export const mimeType = useStorage<MimeType>('slidev-record-mimetype', 'video/webm')
|
|
15
|
+
|
|
16
|
+
export const mimeExtMap: Record<string, string> = {
|
|
17
|
+
'video/webm': 'webm',
|
|
18
|
+
'video/webm;codecs=h264': 'mp4',
|
|
19
|
+
'video/x-matroska;codecs=avc1': 'mkv',
|
|
20
|
+
}
|
|
11
21
|
|
|
12
|
-
export function getFilename(media?: string) {
|
|
22
|
+
export function getFilename(media?: string, mimeType?: string) {
|
|
13
23
|
const d = new Date()
|
|
14
24
|
|
|
15
25
|
const pad = (v: number) => `${v}`.padStart(2, '0')
|
|
16
26
|
|
|
17
27
|
const date = `${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
const ext = mimeType ? mimeExtMap[mimeType] : 'webm'
|
|
30
|
+
|
|
31
|
+
return `${[media, recordingName.value, date].filter(isTruthy).join('-')}.${ext}`
|
|
20
32
|
}
|
|
21
33
|
|
|
34
|
+
function getSupportedMimeTypes() {
|
|
35
|
+
if (MediaRecorder && typeof MediaRecorder.isTypeSupported === 'function')
|
|
36
|
+
return Object.keys(mimeExtMap).filter(mime => MediaRecorder.isTypeSupported(mime))
|
|
37
|
+
return []
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const supportedMimeTypes = getSupportedMimeTypes()
|
|
41
|
+
|
|
22
42
|
export const {
|
|
23
43
|
devices,
|
|
24
44
|
videoInputs: cameras,
|
|
@@ -115,7 +135,7 @@ export function useRecording() {
|
|
|
115
135
|
}
|
|
116
136
|
})
|
|
117
137
|
|
|
118
|
-
async function startRecording() {
|
|
138
|
+
async function startRecording(customConfig?: RecorderOptions) {
|
|
119
139
|
await ensureDevicesListPermissions()
|
|
120
140
|
const { default: Recorder } = await import('recordrtc')
|
|
121
141
|
await startCameraStream()
|
|
@@ -132,6 +152,9 @@ export function useRecording() {
|
|
|
132
152
|
},
|
|
133
153
|
})
|
|
134
154
|
|
|
155
|
+
// merge config
|
|
156
|
+
Object.assign(config, customConfig)
|
|
157
|
+
|
|
135
158
|
if (streamCamera.value) {
|
|
136
159
|
const audioTrack = streamCamera.value!.getAudioTracks()?.[0]
|
|
137
160
|
if (audioTrack)
|
|
@@ -159,7 +182,7 @@ export function useRecording() {
|
|
|
159
182
|
if (recordCamera.value) {
|
|
160
183
|
const blob = recorderCamera.value!.getBlob()
|
|
161
184
|
const url = URL.createObjectURL(blob)
|
|
162
|
-
download(getFilename('camera'), url)
|
|
185
|
+
download(getFilename('camera', config.mimeType), url)
|
|
163
186
|
window.URL.revokeObjectURL(url)
|
|
164
187
|
}
|
|
165
188
|
recorderCamera.value = undefined
|
|
@@ -169,7 +192,7 @@ export function useRecording() {
|
|
|
169
192
|
recorderSlides.value?.stopRecording(() => {
|
|
170
193
|
const blob = recorderSlides.value!.getBlob()
|
|
171
194
|
const url = URL.createObjectURL(blob)
|
|
172
|
-
download(getFilename('screen'), url)
|
|
195
|
+
download(getFilename('screen', config.mimeType), url)
|
|
173
196
|
window.URL.revokeObjectURL(url)
|
|
174
197
|
closeStream(streamSlides)
|
|
175
198
|
recorderSlides.value = undefined
|
package/package.json
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.2",
|
|
4
4
|
"description": "Presentation slides for developers",
|
|
5
5
|
"homepage": "https://sli.dev",
|
|
6
6
|
"bugs": "https://github.com/slidevjs/slidev/issues",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
10
11
|
"url": "https://github.com/slidevjs/slidev"
|
|
11
12
|
},
|
|
12
13
|
"funding": "https://github.com/sponsors/antfu",
|
|
13
|
-
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@antfu/utils": "^0.5.0",
|
|
16
|
-
"@slidev/parser": "0.
|
|
17
|
-
"@slidev/types": "0.
|
|
18
|
-
"@vueuse/core": "^
|
|
16
|
+
"@slidev/parser": "0.29.2",
|
|
17
|
+
"@slidev/types": "0.29.2",
|
|
18
|
+
"@vueuse/core": "^8.1.2",
|
|
19
19
|
"@vueuse/head": "^0.7.5",
|
|
20
|
-
"@vueuse/motion": "^2.0.0-beta.
|
|
20
|
+
"@vueuse/motion": "^2.0.0-beta.12",
|
|
21
21
|
"codemirror": "^5.65.2",
|
|
22
22
|
"drauu": "^0.3.0",
|
|
23
23
|
"file-saver": "^2.0.5",
|
|
24
24
|
"js-base64": "^3.7.2",
|
|
25
25
|
"js-yaml": "^4.1.0",
|
|
26
|
-
"katex": "^0.15.
|
|
26
|
+
"katex": "^0.15.3",
|
|
27
27
|
"mermaid": "^8.14.0",
|
|
28
|
-
"monaco-editor": "^0.
|
|
28
|
+
"monaco-editor": "^0.33.0",
|
|
29
29
|
"nanoid": "^3.3.1",
|
|
30
|
-
"prettier": "^2.
|
|
30
|
+
"prettier": "^2.6.0",
|
|
31
31
|
"recordrtc": "^5.6.2",
|
|
32
32
|
"resolve": "^1.22.0",
|
|
33
|
-
"vite-plugin-windicss": "^1.8.
|
|
33
|
+
"vite-plugin-windicss": "^1.8.3",
|
|
34
34
|
"vue": "^3.2.31",
|
|
35
|
-
"vue-router": "^4.0.
|
|
35
|
+
"vue-router": "^4.0.14",
|
|
36
36
|
"windicss": "^3.5.1"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|