@slidev/client 0.48.0-beta.3 → 0.48.0-beta.5
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/CodeBlockWrapper.vue +4 -3
- package/builtin/KaTexBlockWrapper.vue +4 -3
- package/builtin/RenderWhen.vue +3 -3
- package/builtin/SlideCurrentNo.vue +2 -3
- package/builtin/SlidesTotal.vue +3 -4
- package/builtin/SlidevVideo.vue +8 -6
- package/builtin/Toc.vue +3 -3
- package/builtin/Tweet.vue +4 -15
- package/builtin/VClickGap.vue +3 -5
- package/builtin/VClicks.ts +1 -1
- package/composables/useClicks.ts +16 -13
- package/composables/useTweetScript.ts +17 -0
- package/constants.ts +46 -0
- package/context.ts +70 -0
- package/internals/DrawingControls.vue +41 -8
- package/internals/DrawingLayer.vue +3 -2
- package/internals/PrintContainer.vue +3 -2
- package/internals/PrintSlideClick.vue +3 -2
- package/internals/SlideContainer.vue +3 -3
- package/internals/SlideWrapper.ts +28 -12
- package/logic/drawings.ts +6 -3
- package/logic/nav.ts +1 -1
- package/modules/context.ts +3 -2
- package/package.json +15 -8
- package/routes.ts +4 -11
- package/setup/codemirror.ts +7 -0
- package/state/index.ts +10 -10
|
@@ -14,11 +14,12 @@ Learn more: https://sli.dev/guide/syntax.html#line-highlighting
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
15
|
import { parseRangeString } from '@slidev/parser/core'
|
|
16
16
|
import { useClipboard } from '@vueuse/core'
|
|
17
|
-
import { computed,
|
|
17
|
+
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
18
18
|
import type { PropType } from 'vue'
|
|
19
19
|
import { configs } from '../env'
|
|
20
20
|
import { makeId } from '../logic/utils'
|
|
21
|
-
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET
|
|
21
|
+
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET } from '../constants'
|
|
22
|
+
import { useSlideContext } from '../context'
|
|
22
23
|
|
|
23
24
|
const props = defineProps({
|
|
24
25
|
ranges: {
|
|
@@ -47,7 +48,7 @@ const props = defineProps({
|
|
|
47
48
|
},
|
|
48
49
|
})
|
|
49
50
|
|
|
50
|
-
const clicks =
|
|
51
|
+
const { $clicksContext: clicks } = useSlideContext()
|
|
51
52
|
const el = ref<HTMLDivElement>()
|
|
52
53
|
const id = makeId()
|
|
53
54
|
|
|
@@ -20,11 +20,12 @@ Learn more: https://sli.dev/guide/syntax.html#latex-line-highlighting
|
|
|
20
20
|
-->
|
|
21
21
|
|
|
22
22
|
<script setup lang="ts">
|
|
23
|
-
import { computed,
|
|
23
|
+
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
24
24
|
import type { PropType } from 'vue'
|
|
25
25
|
import { parseRangeString } from '@slidev/parser'
|
|
26
|
-
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET
|
|
26
|
+
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET } from '../constants'
|
|
27
27
|
import { makeId } from '../logic/utils'
|
|
28
|
+
import { useSlideContext } from '../context'
|
|
28
29
|
|
|
29
30
|
const props = defineProps({
|
|
30
31
|
ranges: {
|
|
@@ -45,7 +46,7 @@ const props = defineProps({
|
|
|
45
46
|
},
|
|
46
47
|
})
|
|
47
48
|
|
|
48
|
-
const clicks =
|
|
49
|
+
const { $clicksContext: clicks } = useSlideContext()
|
|
49
50
|
const el = ref<HTMLDivElement>()
|
|
50
51
|
const id = makeId()
|
|
51
52
|
|
package/builtin/RenderWhen.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { RenderContext } from '@slidev/types'
|
|
3
|
-
import { computed,
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
4
|
import { useElementVisibility } from '@vueuse/core'
|
|
5
|
-
import {
|
|
5
|
+
import { useSlideContext } from '../context'
|
|
6
6
|
|
|
7
7
|
type Context = 'main' | 'visible' | RenderContext
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ const targetVisible = useElementVisibility(target)
|
|
|
16
16
|
// When context has `visible`, we need to wrap the content with a div to track the visibility
|
|
17
17
|
const needsDomWrapper = Array.isArray(context) ? context.includes('visible') : context === 'visible'
|
|
18
18
|
|
|
19
|
-
const currentContext =
|
|
19
|
+
const { $renderContext: currentContext } = useSlideContext()
|
|
20
20
|
const shouldRender = computed(() => {
|
|
21
21
|
const anyContext = Array.isArray(context) ? context.some(contextMatch) : contextMatch(context)
|
|
22
22
|
const allConditions = Array.isArray(context) ? context.every(conditionsMatch) : conditionsMatch(context)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { injectionCurrentPage } from '../constants'
|
|
2
|
+
import { useSlideContext } from '../context'
|
|
4
3
|
|
|
5
|
-
const $page =
|
|
4
|
+
const { $page } = useSlideContext()
|
|
6
5
|
</script>
|
|
7
6
|
|
|
8
7
|
<template>
|
package/builtin/SlidesTotal.vue
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { injectionSlidevContext } from '../constants'
|
|
2
|
+
import { useSlideContext } from '../context'
|
|
4
3
|
|
|
5
|
-
const $
|
|
4
|
+
const { $nav } = useSlideContext()
|
|
6
5
|
</script>
|
|
7
6
|
|
|
8
7
|
<template>
|
|
9
|
-
<span>{{ $
|
|
8
|
+
<span>{{ $nav.total }}</span>
|
|
10
9
|
</template>
|
package/builtin/SlidevVideo.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed,
|
|
3
|
-
import {
|
|
2
|
+
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
3
|
+
import { useSlideContext } from '../context'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
autoPlay?: boolean | 'once' | 'resume' | 'resumeOnce'
|
|
@@ -8,10 +8,12 @@ const props = defineProps<{
|
|
|
8
8
|
autoReset?: 'slide' | 'click'
|
|
9
9
|
}>()
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const {
|
|
12
|
+
$slidev,
|
|
13
|
+
$clicksContext: clicks,
|
|
14
|
+
$renderContext: currentContext,
|
|
15
|
+
$route: route,
|
|
16
|
+
} = useSlideContext()
|
|
15
17
|
|
|
16
18
|
const video = ref<HTMLMediaElement>()
|
|
17
19
|
const played = ref(false)
|
package/builtin/Toc.vue
CHANGED
|
@@ -8,9 +8,9 @@ Usage:
|
|
|
8
8
|
<Toc columns='2' maxDepth='3' mode='onlySiblings'/>
|
|
9
9
|
-->
|
|
10
10
|
<script setup lang='ts'>
|
|
11
|
-
import { computed
|
|
11
|
+
import { computed } from 'vue'
|
|
12
12
|
import type { TocItem } from '@slidev/types'
|
|
13
|
-
import {
|
|
13
|
+
import { useSlideContext } from '../context'
|
|
14
14
|
|
|
15
15
|
const props = withDefaults(
|
|
16
16
|
defineProps<{
|
|
@@ -33,7 +33,7 @@ const props = withDefaults(
|
|
|
33
33
|
},
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
const $slidev =
|
|
36
|
+
const { $slidev } = useSlideContext()
|
|
37
37
|
|
|
38
38
|
function filterTreeDepth(tree: TocItem[], level = 1): TocItem[] {
|
|
39
39
|
if (level > Number(props.maxDepth)) {
|
package/builtin/Tweet.vue
CHANGED
|
@@ -7,9 +7,9 @@ Usage:
|
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
<script setup lang="ts">
|
|
10
|
-
import { useScriptTag } from '@vueuse/core'
|
|
11
10
|
import { getCurrentInstance, onMounted, ref } from 'vue'
|
|
12
11
|
import { isDark } from '../logic/dark'
|
|
12
|
+
import { useTweetScript } from '../composables/useTweetScript'
|
|
13
13
|
|
|
14
14
|
const props = defineProps<{
|
|
15
15
|
id: string | number
|
|
@@ -41,21 +41,10 @@ async function create() {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// @ts-expect-error global
|
|
44
|
-
if (window?.twttr?.widgets)
|
|
44
|
+
if (window?.twttr?.widgets)
|
|
45
45
|
onMounted(create)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
useScriptTag(
|
|
49
|
-
'https://platform.twitter.com/widgets.js',
|
|
50
|
-
() => {
|
|
51
|
-
if (vm.isMounted)
|
|
52
|
-
create()
|
|
53
|
-
else
|
|
54
|
-
onMounted(create, vm)
|
|
55
|
-
},
|
|
56
|
-
{ async: true },
|
|
57
|
-
)
|
|
58
|
-
}
|
|
46
|
+
else
|
|
47
|
+
useTweetScript(vm, create)
|
|
59
48
|
</script>
|
|
60
49
|
|
|
61
50
|
<template>
|
package/builtin/VClickGap.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { Fragment,
|
|
3
|
-
import { injectionClicksContext } from '../constants'
|
|
2
|
+
import { Fragment, onMounted, watchEffect } from 'vue'
|
|
4
3
|
import { makeId } from '../logic/utils'
|
|
4
|
+
import { useSlideContext } from '../context'
|
|
5
5
|
|
|
6
6
|
const props = defineProps({
|
|
7
7
|
size: {
|
|
@@ -10,12 +10,10 @@ const props = defineProps({
|
|
|
10
10
|
},
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const { $clicksContext: clicks } = useSlideContext()
|
|
14
14
|
|
|
15
15
|
onMounted(() => {
|
|
16
16
|
watchEffect((onCleanup) => {
|
|
17
|
-
const clicks = clicksRef?.value
|
|
18
|
-
|
|
19
17
|
if (!clicks || clicks.disabled)
|
|
20
18
|
return
|
|
21
19
|
|
package/builtin/VClicks.ts
CHANGED
package/composables/useClicks.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { RouteRecordRaw } from 'vue-router'
|
|
|
6
6
|
import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
|
|
7
7
|
import { normalizeAtProp } from '../logic/utils'
|
|
8
8
|
|
|
9
|
-
function useClicksContextBase(
|
|
9
|
+
function useClicksContextBase(getCurrent: () => number, clicksOverrides?: number): ClicksContext {
|
|
10
10
|
const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
|
|
11
11
|
const map: ClicksContext['map'] = shallowReactive(new Map())
|
|
12
12
|
|
|
@@ -53,7 +53,7 @@ function useClicksContextBase(route: RouteRecordRaw | undefined, getCurrent: ()
|
|
|
53
53
|
get total() {
|
|
54
54
|
// eslint-disable-next-line no-unused-expressions
|
|
55
55
|
routeForceRefresh.value
|
|
56
|
-
return
|
|
56
|
+
return clicksOverrides
|
|
57
57
|
?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
|
|
58
58
|
},
|
|
59
59
|
}
|
|
@@ -63,21 +63,24 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
|
|
|
63
63
|
if (route?.meta?.__clicksContext)
|
|
64
64
|
return route.meta.__clicksContext
|
|
65
65
|
const thisPath = +(route?.path ?? 99999)
|
|
66
|
-
const context = useClicksContextBase(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
const context = useClicksContextBase(
|
|
67
|
+
() => {
|
|
68
|
+
const currentPath = +(currentRoute.value?.path ?? 99999)
|
|
69
|
+
if (currentPath === thisPath)
|
|
70
|
+
return queryClicks.value
|
|
71
|
+
else if (currentPath > thisPath)
|
|
72
|
+
return 99999
|
|
73
|
+
else
|
|
74
|
+
return 0
|
|
75
|
+
},
|
|
76
|
+
route?.meta?.clicks,
|
|
77
|
+
)
|
|
75
78
|
if (route?.meta)
|
|
76
79
|
route.meta.__clicksContext = context
|
|
77
80
|
return context
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
export function useFixedClicks(route
|
|
83
|
+
export function useFixedClicks(route?: RouteRecordRaw | undefined, currentInit = 0): [Ref<number>, ClicksContext] {
|
|
81
84
|
const current = ref(currentInit)
|
|
82
|
-
return [current, useClicksContextBase(
|
|
85
|
+
return [current, useClicksContextBase(() => current.value, route?.meta?.clicks)]
|
|
83
86
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createSharedComposable, useScriptTag } from '@vueuse/core'
|
|
2
|
+
import type { ComponentInternalInstance } from 'vue'
|
|
3
|
+
import { onMounted } from 'vue'
|
|
4
|
+
|
|
5
|
+
export const useTweetScript = createSharedComposable(
|
|
6
|
+
(vm: ComponentInternalInstance, create: () => void) =>
|
|
7
|
+
useScriptTag(
|
|
8
|
+
'https://platform.twitter.com/widgets.js',
|
|
9
|
+
() => {
|
|
10
|
+
if (vm.isMounted)
|
|
11
|
+
create()
|
|
12
|
+
else
|
|
13
|
+
onMounted(create, vm)
|
|
14
|
+
},
|
|
15
|
+
{ async: true },
|
|
16
|
+
),
|
|
17
|
+
)
|
package/constants.ts
CHANGED
|
@@ -24,3 +24,49 @@ export const TRUST_ORIGINS = [
|
|
|
24
24
|
'localhost',
|
|
25
25
|
'127.0.0.1',
|
|
26
26
|
]
|
|
27
|
+
|
|
28
|
+
export const FRONTMATTER_FIELDS = [
|
|
29
|
+
'clicks',
|
|
30
|
+
'disabled',
|
|
31
|
+
'hide',
|
|
32
|
+
'hideInToc',
|
|
33
|
+
'layout',
|
|
34
|
+
'level',
|
|
35
|
+
'preload',
|
|
36
|
+
'routeAlias',
|
|
37
|
+
'src',
|
|
38
|
+
'title',
|
|
39
|
+
'transition',
|
|
40
|
+
'zoom',
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
export const HEADMATTER_FIELDS = [
|
|
44
|
+
...FRONTMATTER_FIELDS,
|
|
45
|
+
'theme',
|
|
46
|
+
'titleTemplate',
|
|
47
|
+
'info',
|
|
48
|
+
'author',
|
|
49
|
+
'keywords',
|
|
50
|
+
'presenter',
|
|
51
|
+
'download',
|
|
52
|
+
'exportFilename',
|
|
53
|
+
'export',
|
|
54
|
+
'highlighter',
|
|
55
|
+
'lineNumbers',
|
|
56
|
+
'monaco',
|
|
57
|
+
'remoteAssets',
|
|
58
|
+
'selectable',
|
|
59
|
+
'record',
|
|
60
|
+
'colorSchema',
|
|
61
|
+
'routerMode',
|
|
62
|
+
'aspectRatio',
|
|
63
|
+
'canvasWidth',
|
|
64
|
+
'themeConfig',
|
|
65
|
+
'favicon',
|
|
66
|
+
'plantUmlServer',
|
|
67
|
+
'fonts',
|
|
68
|
+
'defaults',
|
|
69
|
+
'drawings',
|
|
70
|
+
'htmlAttrs',
|
|
71
|
+
'mdc',
|
|
72
|
+
]
|
package/context.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { shallowRef, toRef } from 'vue'
|
|
2
|
+
import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
|
|
3
|
+
import { useFixedClicks } from './composables/useClicks'
|
|
4
|
+
import {
|
|
5
|
+
FRONTMATTER_FIELDS,
|
|
6
|
+
HEADMATTER_FIELDS,
|
|
7
|
+
injectionClicksContext,
|
|
8
|
+
injectionCurrentPage,
|
|
9
|
+
injectionFrontmatter,
|
|
10
|
+
injectionRenderContext,
|
|
11
|
+
injectionRoute,
|
|
12
|
+
injectionSlidevContext,
|
|
13
|
+
} from './constants'
|
|
14
|
+
|
|
15
|
+
const clicksContextFallback = shallowRef(useFixedClicks()[1])
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the current slide context, should be called inside the setup function of a component inside slide
|
|
19
|
+
*/
|
|
20
|
+
export function useSlideContext() {
|
|
21
|
+
const $slidev = injectLocal(injectionSlidevContext)!
|
|
22
|
+
const $nav = toRef($slidev, 'nav')
|
|
23
|
+
const $clicksContext = injectLocal(injectionClicksContext, clicksContextFallback)!.value
|
|
24
|
+
const $clicks = toRef($clicksContext, 'current')
|
|
25
|
+
const $page = injectLocal(injectionCurrentPage)!
|
|
26
|
+
const $renderContext = injectLocal(injectionRenderContext)!
|
|
27
|
+
const $frontmatter = injectLocal(injectionFrontmatter, {})
|
|
28
|
+
const $route = injectLocal(injectionRoute, undefined)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
$slidev,
|
|
32
|
+
$nav,
|
|
33
|
+
$clicksContext,
|
|
34
|
+
$clicks,
|
|
35
|
+
$page,
|
|
36
|
+
$route,
|
|
37
|
+
$renderContext,
|
|
38
|
+
$frontmatter,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function provideFrontmatter(frontmatter: Record<string, any>) {
|
|
43
|
+
provideLocal(injectionFrontmatter, frontmatter)
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
$slidev,
|
|
47
|
+
$page,
|
|
48
|
+
} = useSlideContext()
|
|
49
|
+
|
|
50
|
+
// update frontmatter in router to make HMR work better
|
|
51
|
+
const route = $slidev.nav.rawRoutes.find(i => i.path === String($page.value))
|
|
52
|
+
if (route?.meta?.slide?.frontmatter) {
|
|
53
|
+
for (const key of Object.keys(route.meta.slide.frontmatter)) {
|
|
54
|
+
if (!(key in frontmatter))
|
|
55
|
+
delete route.meta.slide.frontmatter[key]
|
|
56
|
+
}
|
|
57
|
+
Object.assign(route.meta.slide.frontmatter, frontmatter)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convert frontmatter options to props for v-bind
|
|
63
|
+
* It removes known options fields, and expose an extra `frontmatter` field that contains full frontmatter
|
|
64
|
+
*/
|
|
65
|
+
export function frontmatterToProps(frontmatter: Record<string, any>, pageNo: number) {
|
|
66
|
+
return {
|
|
67
|
+
...objectOmit(frontmatter, pageNo === 0 ? HEADMATTER_FIELDS : FRONTMATTER_FIELDS),
|
|
68
|
+
frontmatter,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { Menu } from 'floating-vue'
|
|
3
|
+
import 'floating-vue/dist/style.css'
|
|
2
4
|
import {
|
|
3
5
|
brush,
|
|
4
6
|
brushColors,
|
|
@@ -21,19 +23,24 @@ function undo() {
|
|
|
21
23
|
function redo() {
|
|
22
24
|
drauu.redo()
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
let lastDrawingMode: typeof drawingMode.value = 'stylus'
|
|
24
28
|
function setDrawingMode(mode: typeof drawingMode.value) {
|
|
25
29
|
drawingMode.value = mode
|
|
26
30
|
drawingEnabled.value = true
|
|
31
|
+
if (mode !== 'eraseLine')
|
|
32
|
+
lastDrawingMode = mode
|
|
27
33
|
}
|
|
28
34
|
function setBrushColor(color: typeof brush.color) {
|
|
29
35
|
brush.color = color
|
|
30
36
|
drawingEnabled.value = true
|
|
37
|
+
drawingMode.value = lastDrawingMode
|
|
31
38
|
}
|
|
32
39
|
</script>
|
|
33
40
|
|
|
34
41
|
<template>
|
|
35
42
|
<Draggable
|
|
36
|
-
class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200"
|
|
43
|
+
class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200 z-20"
|
|
37
44
|
dark="border border-gray-400 border-opacity-10"
|
|
38
45
|
:class="drawingEnabled ? '' : drawingPinned ? 'opacity-40 hover:opacity-90' : 'opacity-0 pointer-events-none'"
|
|
39
46
|
storage-key="slidev-drawing-pos"
|
|
@@ -57,23 +64,41 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
57
64
|
<IconButton title="Draw a rectangle" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
|
|
58
65
|
<carbon:checkbox />
|
|
59
66
|
</IconButton>
|
|
60
|
-
|
|
61
|
-
<!-- <IconButton title="Erase" :class="{ shallow: drawingMode != 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
67
|
+
<IconButton title="Erase" :class="{ shallow: drawingMode !== 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
62
68
|
<carbon:erase />
|
|
63
|
-
</IconButton>
|
|
69
|
+
</IconButton>
|
|
64
70
|
|
|
65
71
|
<VerticalDivider />
|
|
66
72
|
|
|
73
|
+
<Menu>
|
|
74
|
+
<IconButton title="Adjust stroke width" :class="{ shallow: drawingMode === 'eraseLine' }">
|
|
75
|
+
<svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
|
|
76
|
+
<line x1="2" y1="15" x2="22" y2="4" stroke="currentColor" stroke-width="1" stroke-linecap="round" />
|
|
77
|
+
<line x1="2" y1="24" x2="28" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
|
78
|
+
<line x1="7" y1="31" x2="29" y2="19" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
|
79
|
+
</svg>
|
|
80
|
+
</IconButton>
|
|
81
|
+
<template #popper>
|
|
82
|
+
<div class="flex bg-main p-2">
|
|
83
|
+
<div class="inline-block w-7 text-center">
|
|
84
|
+
{{ brush.size }}
|
|
85
|
+
</div>
|
|
86
|
+
<div class="pt-.5">
|
|
87
|
+
<input v-model="brush.size" type="range" min="1" max="15" @change="drawingMode = lastDrawingMode">
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</template>
|
|
91
|
+
</Menu>
|
|
67
92
|
<IconButton
|
|
68
93
|
v-for="color of brushColors"
|
|
69
94
|
:key="color"
|
|
70
95
|
title="Set brush color"
|
|
71
|
-
:class="brush.color === color ? 'active' : 'shallow'"
|
|
96
|
+
:class="brush.color === color && drawingMode !== 'eraseLine' ? 'active' : 'shallow'"
|
|
72
97
|
@click="setBrushColor(color)"
|
|
73
98
|
>
|
|
74
99
|
<div
|
|
75
|
-
class="w-6 h-6 transition-all transform border
|
|
76
|
-
:class="brush.color !== color ? 'rounded-1/2 scale-85' : 'rounded-md'"
|
|
100
|
+
class="w-6 h-6 transition-all transform border"
|
|
101
|
+
:class="brush.color !== color ? 'rounded-1/2 scale-85 border-white' : 'rounded-md border-gray-300/50'"
|
|
77
102
|
:style="drawingEnabled ? { background: color } : { borderColor: color }"
|
|
78
103
|
/>
|
|
79
104
|
</IconButton>
|
|
@@ -87,7 +112,7 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
87
112
|
<carbon:redo />
|
|
88
113
|
</IconButton>
|
|
89
114
|
<IconButton title="Delete" :class="{ disabled: !canClear }" @click="clearDrauu()">
|
|
90
|
-
<carbon:
|
|
115
|
+
<carbon:trash-can />
|
|
91
116
|
</IconButton>
|
|
92
117
|
|
|
93
118
|
<VerticalDivider />
|
|
@@ -106,3 +131,11 @@ function setBrushColor(color: typeof brush.color) {
|
|
|
106
131
|
</IconButton>
|
|
107
132
|
</Draggable>
|
|
108
133
|
</template>
|
|
134
|
+
|
|
135
|
+
<style lang="postcss">
|
|
136
|
+
.v-popper--theme-menu {
|
|
137
|
+
.v-popper__arrow-inner {
|
|
138
|
+
@apply border-main;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
3
|
+
import { injectLocal } from '@vueuse/core'
|
|
3
4
|
import { drauu, drawingEnabled, loadCanvas } from '../logic/drawings'
|
|
4
5
|
import { injectionSlideScale } from '../constants'
|
|
5
6
|
|
|
6
|
-
const scale =
|
|
7
|
+
const scale = injectLocal(injectionSlideScale)!
|
|
7
8
|
const svg = ref<SVGSVGElement>()
|
|
8
9
|
|
|
9
10
|
onMounted(() => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { parseRangeString } from '@slidev/parser/core'
|
|
3
|
-
import { computed
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { provideLocal } from '@vueuse/core'
|
|
4
5
|
import { configs, slideAspect, slideWidth } from '../env'
|
|
5
6
|
import { injectionSlideScale } from '../constants'
|
|
6
7
|
import { route as currentRoute, rawRoutes } from '../logic/nav'
|
|
@@ -31,7 +32,7 @@ const className = computed(() => ({
|
|
|
31
32
|
'select-none': !configs.selectable,
|
|
32
33
|
}))
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
provideLocal(injectionSlideScale, scale)
|
|
35
36
|
</script>
|
|
36
37
|
|
|
37
38
|
<template>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { RouteRecordRaw } from 'vue-router'
|
|
3
|
-
import { computed,
|
|
3
|
+
import { computed, reactive, shallowRef } from 'vue'
|
|
4
4
|
import type { ClicksContext } from '@slidev/types'
|
|
5
|
+
import { provideLocal } from '@vueuse/core'
|
|
5
6
|
import { injectionSlidevContext } from '../constants'
|
|
6
7
|
import { configs, slideHeight, slideWidth } from '../env'
|
|
7
8
|
import { getSlideClass } from '../utils'
|
|
@@ -33,7 +34,7 @@ const id = computed(() =>
|
|
|
33
34
|
`${props.route.path.toString().padStart(3, '0')}-${(props.nav.clicks.value + 1).toString().padStart(2, '0')}`,
|
|
34
35
|
)
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
provideLocal(injectionSlidevContext, reactive({
|
|
37
38
|
nav: props.nav,
|
|
38
39
|
configs,
|
|
39
40
|
themeConfigs: computed(() => configs.themeConfig),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useElementSize, useStyleTag } from '@vueuse/core'
|
|
3
|
-
import { computed,
|
|
2
|
+
import { provideLocal, useElementSize, useStyleTag } from '@vueuse/core'
|
|
3
|
+
import { computed, ref, watchEffect } from 'vue'
|
|
4
4
|
import { configs, slideAspect, slideHeight, slideWidth } from '../env'
|
|
5
5
|
import { injectionSlideScale } from '../constants'
|
|
6
6
|
import { isPrintMode } from '../logic/nav'
|
|
@@ -64,7 +64,7 @@ if (props.isMain) {
|
|
|
64
64
|
`))
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
provideLocal(injectionSlideScale, scale as any)
|
|
68
68
|
</script>
|
|
69
69
|
|
|
70
70
|
<template>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { defineComponent, h,
|
|
1
|
+
import { computed, defineComponent, h, ref, toRef } from 'vue'
|
|
2
2
|
import type { PropType } from 'vue'
|
|
3
|
+
import { provideLocal } from '@vueuse/core'
|
|
3
4
|
import type { ClicksContext, RenderContext } from '@slidev/types'
|
|
5
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
4
6
|
import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
|
|
5
7
|
|
|
6
8
|
export default defineComponent({
|
|
@@ -20,23 +22,37 @@ export default defineComponent({
|
|
|
20
22
|
},
|
|
21
23
|
is: {
|
|
22
24
|
type: Object,
|
|
23
|
-
|
|
25
|
+
required: true,
|
|
24
26
|
},
|
|
25
27
|
route: {
|
|
26
|
-
type: Object
|
|
27
|
-
|
|
28
|
+
type: Object as PropType<RouteRecordRaw>,
|
|
29
|
+
required: true,
|
|
28
30
|
},
|
|
29
31
|
},
|
|
30
32
|
setup(props) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
provideLocal(injectionRoute, props.route)
|
|
34
|
+
provideLocal(injectionCurrentPage, ref(+props.route.path))
|
|
35
|
+
provideLocal(injectionRenderContext, ref(props.renderContext as RenderContext))
|
|
36
|
+
provideLocal(injectionActive, toRef(props, 'active'))
|
|
37
|
+
provideLocal(injectionClicksContext, toRef(props, 'clicksContext'))
|
|
38
|
+
|
|
39
|
+
const style = computed(() => {
|
|
40
|
+
const zoom = props.route.meta?.slide?.frontmatter.zoom ?? 1
|
|
41
|
+
return zoom === 1
|
|
42
|
+
? undefined
|
|
43
|
+
: {
|
|
44
|
+
width: `${100 / zoom}%`,
|
|
45
|
+
height: `${100 / zoom}%`,
|
|
46
|
+
transformOrigin: 'top left',
|
|
47
|
+
transform: `scale(${zoom})`,
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
style,
|
|
53
|
+
}
|
|
36
54
|
},
|
|
37
55
|
render() {
|
|
38
|
-
|
|
39
|
-
return h(this.$props.is)
|
|
40
|
-
return this.$slots?.default?.()
|
|
56
|
+
return h(this.$props.is, { style: this.style })
|
|
41
57
|
},
|
|
42
58
|
})
|
package/logic/drawings.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createDrauu } from 'drauu'
|
|
|
4
4
|
import { toReactive, useLocalStorage } from '@vueuse/core'
|
|
5
5
|
import { drawingState, onPatch, patch } from '../state/drawings'
|
|
6
6
|
import { configs } from '../env'
|
|
7
|
+
import { isInputting } from '../state'
|
|
7
8
|
import { currentPage, isPresenter } from './nav'
|
|
8
9
|
|
|
9
10
|
export const brushColors = [
|
|
@@ -40,11 +41,13 @@ export const drawingMode = computed({
|
|
|
40
41
|
set(v: DrawingMode | 'arrow') {
|
|
41
42
|
_mode.value = v
|
|
42
43
|
if (v === 'arrow') {
|
|
43
|
-
|
|
44
|
+
// eslint-disable-next-line ts/no-use-before-define
|
|
45
|
+
drauu.mode = 'line'
|
|
44
46
|
brush.arrowEnd = true
|
|
45
47
|
}
|
|
46
48
|
else {
|
|
47
|
-
|
|
49
|
+
// eslint-disable-next-line ts/no-use-before-define
|
|
50
|
+
drauu.mode = v
|
|
48
51
|
brush.arrowEnd = false
|
|
49
52
|
}
|
|
50
53
|
},
|
|
@@ -110,7 +113,7 @@ drauu.on('start', () => isDrawing.value = true)
|
|
|
110
113
|
drauu.on('end', () => isDrawing.value = false)
|
|
111
114
|
|
|
112
115
|
window.addEventListener('keydown', (e) => {
|
|
113
|
-
if (!drawingEnabled.value)
|
|
116
|
+
if (!drawingEnabled.value || isInputting.value)
|
|
114
117
|
return
|
|
115
118
|
|
|
116
119
|
const noModifier = !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey
|
package/logic/nav.ts
CHANGED
|
@@ -177,7 +177,7 @@ export async function downloadPDF() {
|
|
|
177
177
|
export async function openInEditor(url?: string) {
|
|
178
178
|
if (url == null) {
|
|
179
179
|
const slide = currentRoute.value?.meta?.slide
|
|
180
|
-
if (!slide
|
|
180
|
+
if (!slide)
|
|
181
181
|
return false
|
|
182
182
|
url = `${slide.filepath}:${slide.start}`
|
|
183
183
|
}
|
package/modules/context.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { App } from 'vue'
|
|
2
|
-
import { computed, reactive } from 'vue'
|
|
2
|
+
import { computed, reactive, ref } from 'vue'
|
|
3
3
|
import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
|
|
4
4
|
import type { ComputedRef } from '@vue/reactivity'
|
|
5
5
|
import type { configs } from '../env'
|
|
6
6
|
import * as nav from '../logic/nav'
|
|
7
7
|
import { route } from '../logic/nav'
|
|
8
8
|
import { isDark } from '../logic/dark'
|
|
9
|
-
import { injectionCurrentPage, injectionSlidevContext } from '../constants'
|
|
9
|
+
import { injectionCurrentPage, injectionRenderContext, injectionSlidevContext } from '../constants'
|
|
10
10
|
import { useContext } from '../composables/useContext'
|
|
11
11
|
|
|
12
12
|
export type SlidevContextNavKey = 'path' | 'total' | 'clicksContext' | 'clicks' | 'clicksTotal' | 'currentPage' | 'currentPath' | 'currentRoute' | 'currentSlideId' | 'currentLayout' | 'nextRoute' | 'rawTree' | 'treeWithActiveStatuses' | 'tree' | 'downloadPDF' | 'next' | 'nextSlide' | 'openInEditor' | 'prev' | 'prevSlide' | 'rawRoutes' | 'go'
|
|
@@ -25,6 +25,7 @@ export default function createSlidevContext() {
|
|
|
25
25
|
return {
|
|
26
26
|
install(app: App) {
|
|
27
27
|
const context = reactive(useContext(route))
|
|
28
|
+
app.provide(injectionRenderContext, ref('none'))
|
|
28
29
|
app.provide(injectionSlidevContext, context)
|
|
29
30
|
app.provide(injectionCurrentPage, computed(() => context.nav.currentPage))
|
|
30
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.48.0-beta.5",
|
|
4
5
|
"description": "Presentation slides for developers",
|
|
5
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
6
7
|
"license": "MIT",
|
|
@@ -13,6 +14,12 @@
|
|
|
13
14
|
"bugs": "https://github.com/slidevjs/slidev/issues",
|
|
14
15
|
"exports": {
|
|
15
16
|
"./package.json": "./package.json",
|
|
17
|
+
"./constants": "./constants.ts",
|
|
18
|
+
"./context": "./context.ts",
|
|
19
|
+
"./env": "./env.ts",
|
|
20
|
+
"./layoutHelper": "./layoutHelper.ts",
|
|
21
|
+
"./routes": "./routes.ts",
|
|
22
|
+
"./utils": "./utils.ts",
|
|
16
23
|
"./*": "./*"
|
|
17
24
|
},
|
|
18
25
|
"engines": {
|
|
@@ -22,15 +29,15 @@
|
|
|
22
29
|
"@antfu/utils": "^0.7.7",
|
|
23
30
|
"@iconify-json/carbon": "^1.1.30",
|
|
24
31
|
"@iconify-json/ph": "^1.1.11",
|
|
25
|
-
"@shikijs/vitepress-twoslash": "^1.1.
|
|
32
|
+
"@shikijs/vitepress-twoslash": "^1.1.6",
|
|
26
33
|
"@unhead/vue": "^1.8.10",
|
|
27
34
|
"@unocss/reset": "^0.58.5",
|
|
28
|
-
"@vueuse/core": "^10.
|
|
29
|
-
"@vueuse/math": "^10.
|
|
35
|
+
"@vueuse/core": "^10.8.0",
|
|
36
|
+
"@vueuse/math": "^10.8.0",
|
|
30
37
|
"@vueuse/motion": "^2.0.0",
|
|
31
38
|
"codemirror": "^5.65.16",
|
|
32
39
|
"defu": "^6.1.4",
|
|
33
|
-
"drauu": "^0.
|
|
40
|
+
"drauu": "^0.4.0",
|
|
34
41
|
"file-saver": "^2.0.5",
|
|
35
42
|
"floating-vue": "^5.2.2",
|
|
36
43
|
"fuse.js": "^7.0.0",
|
|
@@ -39,15 +46,15 @@
|
|
|
39
46
|
"katex": "^0.16.9",
|
|
40
47
|
"mermaid": "^10.8.0",
|
|
41
48
|
"monaco-editor": "^0.37.1",
|
|
42
|
-
"nanoid": "^5.0.
|
|
49
|
+
"nanoid": "^5.0.6",
|
|
43
50
|
"prettier": "^3.2.5",
|
|
44
51
|
"recordrtc": "^5.6.2",
|
|
45
52
|
"resolve": "^1.22.8",
|
|
46
53
|
"unocss": "^0.58.5",
|
|
47
54
|
"vue": "^3.4.19",
|
|
48
55
|
"vue-router": "^4.2.5",
|
|
49
|
-
"@slidev/parser": "0.48.0-beta.
|
|
50
|
-
"@slidev/types": "0.48.0-beta.
|
|
56
|
+
"@slidev/parser": "0.48.0-beta.5",
|
|
57
|
+
"@slidev/types": "0.48.0-beta.5"
|
|
51
58
|
},
|
|
52
59
|
"devDependencies": {
|
|
53
60
|
"vite": "^5.1.3"
|
package/routes.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
|
|
2
2
|
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
|
|
3
3
|
import type { TransitionGroupProps } from 'vue'
|
|
4
|
-
import type { ClicksContext } from '@slidev/types'
|
|
4
|
+
import type { ClicksContext, SlideInfo } from '@slidev/types'
|
|
5
5
|
|
|
6
6
|
// @ts-expect-error missing types
|
|
7
7
|
import _rawRoutes, { redirects } from '/@slidev/routes'
|
|
@@ -84,19 +84,12 @@ declare module 'vue-router' {
|
|
|
84
84
|
preload?: boolean
|
|
85
85
|
|
|
86
86
|
// slide info
|
|
87
|
-
slide?: {
|
|
87
|
+
slide?: Omit<SlideInfo, 'source'> & {
|
|
88
|
+
noteHTML: string
|
|
89
|
+
filepath: string
|
|
88
90
|
start: number
|
|
89
|
-
end: number
|
|
90
|
-
note?: string
|
|
91
|
-
noteHTML?: string
|
|
92
91
|
id: number
|
|
93
92
|
no: number
|
|
94
|
-
filepath: string
|
|
95
|
-
title?: string
|
|
96
|
-
level?: number
|
|
97
|
-
raw: string
|
|
98
|
-
content: string
|
|
99
|
-
frontmatter: Record<string, any>
|
|
100
93
|
}
|
|
101
94
|
|
|
102
95
|
// private fields
|
package/setup/codemirror.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Ref, WritableComputedRef } from 'vue'
|
|
2
|
+
import { onClickOutside } from '@vueuse/core'
|
|
2
3
|
import { watch } from 'vue'
|
|
3
4
|
import * as _CodeMirror from 'codemirror'
|
|
4
5
|
import 'codemirror/mode/javascript/javascript'
|
|
@@ -47,5 +48,11 @@ export async function useCodeMirror(
|
|
|
47
48
|
{ immediate: true },
|
|
48
49
|
)
|
|
49
50
|
|
|
51
|
+
onClickOutside(cm.getWrapperElement(), () => {
|
|
52
|
+
const el = cm.getInputField()
|
|
53
|
+
if (document.activeElement === el)
|
|
54
|
+
el.blur()
|
|
55
|
+
})
|
|
56
|
+
|
|
50
57
|
return cm
|
|
51
58
|
}
|
package/state/index.ts
CHANGED
|
@@ -20,19 +20,19 @@ 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 = useLocalStorage<string>('slidev-camera', 'default')
|
|
24
|
-
export const currentMic = useLocalStorage<string>('slidev-mic', 'default')
|
|
23
|
+
export const currentCamera = useLocalStorage<string>('slidev-camera', 'default', { listenToStorageChanges: false })
|
|
24
|
+
export const currentMic = useLocalStorage<string>('slidev-mic', 'default', { listenToStorageChanges: false })
|
|
25
25
|
export const slideScale = useLocalStorage<number>('slidev-scale', 0)
|
|
26
26
|
|
|
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 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)
|
|
27
|
+
export const showOverview = useLocalStorage('slidev-show-overview', false, { listenToStorageChanges: false })
|
|
28
|
+
export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true, { listenToStorageChanges: false })
|
|
29
|
+
export const showEditor = useLocalStorage('slidev-show-editor', false, { listenToStorageChanges: false })
|
|
30
|
+
export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false, { listenToStorageChanges: false })
|
|
31
|
+
export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 318, { listenToStorageChanges: false })
|
|
32
|
+
export const editorHeight = useLocalStorage('slidev-editor-height', isClient ? window.innerHeight * 0.4 : 300, { listenToStorageChanges: false })
|
|
33
33
|
|
|
34
|
-
export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1)
|
|
35
|
-
export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1)
|
|
34
|
+
export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1, { listenToStorageChanges: false })
|
|
35
|
+
export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1, { listenToStorageChanges: false })
|
|
36
36
|
|
|
37
37
|
export function togglePresenterLayout() {
|
|
38
38
|
presenterLayout.value = presenterLayout.value + 1
|