@slidev/client 0.48.0-beta.21 → 0.48.0-beta.23
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 +23 -25
- package/builtin/Monaco.vue +64 -8
- package/builtin/ShikiMagicMove.vue +69 -16
- package/builtin/Tweet.vue +12 -1
- package/composables/useClicks.ts +10 -2
- package/composables/useNav.ts +12 -8
- package/{logic/note.ts → composables/useSlideInfo.ts} +11 -14
- package/composables/useViewTransition.ts +7 -4
- package/context.ts +4 -5
- package/env.ts +4 -4
- package/index.html +1 -0
- package/internals/CodeRunner.vue +139 -0
- package/internals/DomElement.vue +18 -0
- package/internals/IconButton.vue +1 -1
- package/internals/NoteEditable.vue +4 -3
- package/internals/NoteStatic.vue +2 -2
- package/internals/PrintContainer.vue +4 -4
- package/internals/PrintSlideClick.vue +3 -3
- package/internals/QuickOverview.vue +2 -1
- package/internals/SideEditor.vue +1 -1
- package/internals/SlideContainer.vue +6 -6
- package/internals/SlideLoading.vue +1 -1
- package/internals/SlideWrapper.vue +79 -0
- package/internals/SlidesShow.vue +1 -1
- package/logic/nav.ts +1 -1
- package/logic/route.ts +6 -1
- package/logic/utils.ts +25 -1
- package/main.ts +1 -0
- package/modules/context.ts +4 -2
- package/modules/mermaid.ts +3 -1
- package/package.json +10 -9
- package/pages/overview.vue +7 -6
- package/pages/presenter.vue +1 -1
- package/setup/code-runners.ts +169 -0
- package/setup/monaco.ts +11 -10
- package/shim-vue.d.ts +4 -8
- package/state/index.ts +1 -1
- package/styles/code.css +2 -2
- package/internals/SlideWrapper.ts +0 -58
- package/styles/monaco.css +0 -27
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { debounce, toArray } from '@antfu/utils'
|
|
3
|
+
import { useVModel } from '@vueuse/core'
|
|
4
|
+
import type { CodeRunnerOutput } from '@slidev/types'
|
|
5
|
+
import { computed, ref, shallowRef, watch } from 'vue'
|
|
6
|
+
import { isPrintMode } from '../logic/nav'
|
|
7
|
+
import { useSlideContext } from '../context'
|
|
8
|
+
import setupCodeRunners from '../setup/code-runners'
|
|
9
|
+
import IconButton from './IconButton.vue'
|
|
10
|
+
import DomElement from './DomElement.vue'
|
|
11
|
+
|
|
12
|
+
const props = defineProps<{
|
|
13
|
+
modelValue: string
|
|
14
|
+
lang: string
|
|
15
|
+
autorun: boolean | 'once'
|
|
16
|
+
height?: string
|
|
17
|
+
highlightOutput: boolean
|
|
18
|
+
runnerOptions?: Record<string, unknown>
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const emit = defineEmits(['update:modelValue'])
|
|
22
|
+
const code = useVModel(props, 'modelValue', emit)
|
|
23
|
+
|
|
24
|
+
const { $renderContext } = useSlideContext()
|
|
25
|
+
const disabled = computed(() => !['slide', 'presenter'].includes($renderContext.value))
|
|
26
|
+
|
|
27
|
+
const autorun = isPrintMode.value ? 'once' : props.autorun
|
|
28
|
+
const isRunning = ref(autorun)
|
|
29
|
+
const outputs = shallowRef<CodeRunnerOutput[]>()
|
|
30
|
+
const runCount = ref(0)
|
|
31
|
+
const highlightFn = ref<(code: string, lang: string) => string>()
|
|
32
|
+
|
|
33
|
+
const triggerRun = debounce(200, async () => {
|
|
34
|
+
if (disabled.value)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
const { highlight, run } = await setupCodeRunners()
|
|
38
|
+
highlightFn.value = highlight
|
|
39
|
+
|
|
40
|
+
const setAsRunning = setTimeout(() => {
|
|
41
|
+
isRunning.value = true
|
|
42
|
+
}, 500)
|
|
43
|
+
|
|
44
|
+
outputs.value = toArray(await run(code.value, props.lang, props.runnerOptions ?? {}))
|
|
45
|
+
runCount.value += 1
|
|
46
|
+
isRunning.value = false
|
|
47
|
+
|
|
48
|
+
clearTimeout(setAsRunning)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
if (autorun === 'once')
|
|
52
|
+
triggerRun()
|
|
53
|
+
else if (autorun)
|
|
54
|
+
watch(code, triggerRun, { immediate: true })
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<div
|
|
59
|
+
class="relative flex flex-col rounded-b border-t border-main"
|
|
60
|
+
:style="{ height: props.height }"
|
|
61
|
+
data-waitfor=".slidev-runner-output"
|
|
62
|
+
>
|
|
63
|
+
<div v-if="disabled" class="text-sm text-center opacity-50">
|
|
64
|
+
Code is disabled in the "{{ $renderContext }}" mode
|
|
65
|
+
</div>
|
|
66
|
+
<div v-else-if="isRunning" class="text-sm text-center opacity-50">
|
|
67
|
+
Running...
|
|
68
|
+
</div>
|
|
69
|
+
<div v-else-if="!outputs?.length" class="text-sm text-center opacity-50">
|
|
70
|
+
Click the play button to run the code
|
|
71
|
+
</div>
|
|
72
|
+
<div v-else :key="`run-${runCount}`" class="slidev-runner-output">
|
|
73
|
+
<template v-for="output, _idx1 of outputs" :key="_idx1">
|
|
74
|
+
<div v-if="'html' in output" v-html="output.html" />
|
|
75
|
+
<div v-else-if="'error' in output" class="text-red-500">
|
|
76
|
+
{{ output.error }}
|
|
77
|
+
</div>
|
|
78
|
+
<DomElement v-else-if="'element' in output" :element="output.element" />
|
|
79
|
+
<div v-else class="output-line">
|
|
80
|
+
<template
|
|
81
|
+
v-for="item, idx2 in toArray(output)"
|
|
82
|
+
:key="idx2"
|
|
83
|
+
>
|
|
84
|
+
<span
|
|
85
|
+
v-if="item.highlightLang && highlightFn"
|
|
86
|
+
class="highlighted"
|
|
87
|
+
v-html="highlightFn(item.text, item.highlightLang)"
|
|
88
|
+
/>
|
|
89
|
+
<span v-else :class="item.class">{{ item.text }}</span>
|
|
90
|
+
<span v-if="idx2 < toArray(output).length - 1" class="separator">,</span>
|
|
91
|
+
</template>
|
|
92
|
+
</div>
|
|
93
|
+
</template>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div v-if="code.trim()" class="absolute right-1 top-1 max-h-full flex gap-1">
|
|
97
|
+
<IconButton class="w-8 h-8 max-h-full flex justify-center items-center" title="Run code" @click="triggerRun">
|
|
98
|
+
<carbon:play />
|
|
99
|
+
</IconButton>
|
|
100
|
+
</div>
|
|
101
|
+
</template>
|
|
102
|
+
|
|
103
|
+
<style lang="postcss">
|
|
104
|
+
.slidev-runner-output {
|
|
105
|
+
@apply px-5 py-3 flex-grow text-xs leading-[.8rem] font-$slidev-code-font-family select-text;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.slidev-runner-output .log-type {
|
|
109
|
+
@apply font-bold op-70;
|
|
110
|
+
|
|
111
|
+
&.DBG {
|
|
112
|
+
@apply text-gray-500;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
&.LOG {
|
|
116
|
+
@apply text-blue-500;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&.WRN {
|
|
120
|
+
@apply text-orange-500;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
&.ERR {
|
|
124
|
+
@apply text-red-500;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.slidev-runner-output .output-line {
|
|
129
|
+
@apply flex my-1 w-full;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.slidev-runner-output .separator {
|
|
133
|
+
@apply op-40 mr-1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.slidev-runner-output .highlighted > pre {
|
|
137
|
+
@apply inline text-wrap !bg-transparent;
|
|
138
|
+
}
|
|
139
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, watchEffect } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
element: HTMLElement
|
|
6
|
+
}>()
|
|
7
|
+
|
|
8
|
+
const container = ref<HTMLElement>()
|
|
9
|
+
|
|
10
|
+
watchEffect(() => {
|
|
11
|
+
if (container.value)
|
|
12
|
+
container.value.appendChild(props.element)
|
|
13
|
+
})
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div ref="container" />
|
|
18
|
+
</template>
|
package/internals/IconButton.vue
CHANGED
|
@@ -7,7 +7,7 @@ defineProps<{
|
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<template>
|
|
10
|
-
<component :is="as || 'button'" class="slidev-icon-btn" :title="title"
|
|
10
|
+
<component :is="as || 'button'" class="slidev-icon-btn" :title="title">
|
|
11
11
|
<span class="sr-only">{{ title }}</span>
|
|
12
12
|
<slot>
|
|
13
13
|
<div :class="icon" />
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { PropType } from 'vue'
|
|
3
|
-
import { nextTick, ref, watch, watchEffect } from 'vue'
|
|
3
|
+
import { nextTick, ref, toRef, watch, watchEffect } from 'vue'
|
|
4
4
|
import { ignorableWatch, onClickOutside, useVModel } from '@vueuse/core'
|
|
5
5
|
import type { ClicksContext } from '@slidev/types'
|
|
6
|
-
import { useDynamicSlideInfo } from '../
|
|
6
|
+
import { useDynamicSlideInfo } from '../composables/useSlideInfo'
|
|
7
7
|
import NoteDisplay from './NoteDisplay.vue'
|
|
8
8
|
|
|
9
9
|
const props = defineProps({
|
|
10
10
|
no: {
|
|
11
11
|
type: Number,
|
|
12
|
+
required: true,
|
|
12
13
|
},
|
|
13
14
|
class: {
|
|
14
15
|
default: '',
|
|
@@ -38,7 +39,7 @@ const emit = defineEmits<{
|
|
|
38
39
|
|
|
39
40
|
const editing = useVModel(props, 'editing', emit, { passive: true })
|
|
40
41
|
|
|
41
|
-
const { info, update } = useDynamicSlideInfo(props
|
|
42
|
+
const { info, update } = useDynamicSlideInfo(toRef(props, 'no'))
|
|
42
43
|
|
|
43
44
|
const note = ref('')
|
|
44
45
|
let timer: any
|
package/internals/NoteStatic.vue
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { ClicksContext } from '@slidev/types'
|
|
3
|
-
import { useSlideInfo } from '../
|
|
3
|
+
import { useSlideInfo } from '../composables/useSlideInfo'
|
|
4
4
|
import NoteDisplay from './NoteDisplay.vue'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
|
-
no
|
|
7
|
+
no: number
|
|
8
8
|
class?: string
|
|
9
9
|
clicksContext?: ClicksContext
|
|
10
10
|
}>()
|
|
@@ -12,14 +12,14 @@ const props = defineProps<{
|
|
|
12
12
|
}>()
|
|
13
13
|
|
|
14
14
|
const width = computed(() => props.width)
|
|
15
|
-
const height = computed(() => props.width / slideAspect)
|
|
15
|
+
const height = computed(() => props.width / slideAspect.value)
|
|
16
16
|
|
|
17
17
|
const screenAspect = computed(() => width.value / height.value)
|
|
18
18
|
|
|
19
19
|
const scale = computed(() => {
|
|
20
|
-
if (screenAspect.value < slideAspect)
|
|
21
|
-
return width.value / slideWidth
|
|
22
|
-
return (height.value * slideAspect) / slideWidth
|
|
20
|
+
if (screenAspect.value < slideAspect.value)
|
|
21
|
+
return width.value / slideWidth.value
|
|
22
|
+
return (height.value * slideAspect.value) / slideWidth.value
|
|
23
23
|
})
|
|
24
24
|
|
|
25
25
|
// In print mode, the routes will never change. So we don't need reactivity here.
|
|
@@ -5,7 +5,7 @@ import { injectionSlidevContext } from '../constants'
|
|
|
5
5
|
import { configs, slideHeight, slideWidth } from '../env'
|
|
6
6
|
import { getSlideClass } from '../utils'
|
|
7
7
|
import type { SlidevContextNav } from '../composables/useNav'
|
|
8
|
-
import SlideWrapper from './SlideWrapper'
|
|
8
|
+
import SlideWrapper from './SlideWrapper.vue'
|
|
9
9
|
|
|
10
10
|
import GlobalTop from '#slidev/global-components/top'
|
|
11
11
|
import GlobalBottom from '#slidev/global-components/bottom'
|
|
@@ -17,8 +17,8 @@ const { nav } = defineProps<{
|
|
|
17
17
|
const route = computed(() => nav.currentSlideRoute.value)
|
|
18
18
|
|
|
19
19
|
const style = computed(() => ({
|
|
20
|
-
height: `${slideHeight}px`,
|
|
21
|
-
width: `${slideWidth}px`,
|
|
20
|
+
height: `${slideHeight.value}px`,
|
|
21
|
+
width: `${slideWidth.value}px`,
|
|
22
22
|
}))
|
|
23
23
|
|
|
24
24
|
const DrawingPreview = shallowRef<any>()
|
|
@@ -8,7 +8,7 @@ import { useFixedClicks } from '../composables/useClicks'
|
|
|
8
8
|
import { getSlideClass } from '../utils'
|
|
9
9
|
import { CLICKS_MAX } from '../constants'
|
|
10
10
|
import SlideContainer from './SlideContainer.vue'
|
|
11
|
-
import SlideWrapper from './SlideWrapper'
|
|
11
|
+
import SlideWrapper from './SlideWrapper.vue'
|
|
12
12
|
import DrawingPreview from './DrawingPreview.vue'
|
|
13
13
|
import IconButton from './IconButton.vue'
|
|
14
14
|
|
|
@@ -167,6 +167,7 @@ watchEffect(() => {
|
|
|
167
167
|
<carbon:close />
|
|
168
168
|
</IconButton>
|
|
169
169
|
<IconButton
|
|
170
|
+
v-if="__DEV__"
|
|
170
171
|
as="a"
|
|
171
172
|
title="Slides Overview"
|
|
172
173
|
target="_blank"
|
package/internals/SideEditor.vue
CHANGED
|
@@ -4,7 +4,7 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
|
|
4
4
|
import { activeElement, editorHeight, editorWidth, isInputting, showEditor, isEditorVertical as vertical } from '../state'
|
|
5
5
|
import { useCodeMirror } from '../setup/codemirror'
|
|
6
6
|
import { currentSlideNo, openInEditor } from '../logic/nav'
|
|
7
|
-
import { useDynamicSlideInfo } from '../
|
|
7
|
+
import { useDynamicSlideInfo } from '../composables/useSlideInfo'
|
|
8
8
|
import IconButton from './IconButton.vue'
|
|
9
9
|
|
|
10
10
|
const props = defineProps<{
|
|
@@ -25,7 +25,7 @@ const root = ref<HTMLDivElement>()
|
|
|
25
25
|
const element = useElementSize(root)
|
|
26
26
|
|
|
27
27
|
const width = computed(() => props.width ? props.width : element.width.value)
|
|
28
|
-
const height = computed(() => props.width ? props.width / slideAspect : element.height.value)
|
|
28
|
+
const height = computed(() => props.width ? props.width / slideAspect.value : element.height.value)
|
|
29
29
|
|
|
30
30
|
if (props.width) {
|
|
31
31
|
watchEffect(() => {
|
|
@@ -41,14 +41,14 @@ const screenAspect = computed(() => width.value / height.value)
|
|
|
41
41
|
const scale = computed(() => {
|
|
42
42
|
if (props.scale && !isPrintMode.value)
|
|
43
43
|
return props.scale
|
|
44
|
-
if (screenAspect.value < slideAspect)
|
|
45
|
-
return width.value / slideWidth
|
|
46
|
-
return height.value * slideAspect / slideWidth
|
|
44
|
+
if (screenAspect.value < slideAspect.value)
|
|
45
|
+
return width.value / slideWidth.value
|
|
46
|
+
return height.value * slideAspect.value / slideWidth.value
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
const style = computed(() => ({
|
|
50
|
-
'height': `${slideHeight}px`,
|
|
51
|
-
'width': `${slideWidth}px`,
|
|
50
|
+
'height': `${slideHeight.value}px`,
|
|
51
|
+
'width': `${slideWidth.value}px`,
|
|
52
52
|
'transform': `translate(-50%, -50%) scale(${scale.value})`,
|
|
53
53
|
'--slidev-slide-scale': scale.value,
|
|
54
54
|
}))
|
|
@@ -10,7 +10,7 @@ onMounted(() => {
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<template>
|
|
13
|
-
<div class="h-full w-full flex items-center justify-center gap-2">
|
|
13
|
+
<div class="h-full w-full flex items-center justify-center gap-2 slidev-slide-loading">
|
|
14
14
|
<template v-if="timeout">
|
|
15
15
|
<div class="i-svg-spinners-90-ring-with-bg text-xl" />
|
|
16
16
|
<div>Loading slide...</div>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, defineAsyncComponent, defineComponent, h, onMounted, ref, toRef } from 'vue'
|
|
3
|
+
import type { PropType } from 'vue'
|
|
4
|
+
import { provideLocal } from '@vueuse/core'
|
|
5
|
+
import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
|
|
6
|
+
import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
|
|
7
|
+
import SlideLoading from './SlideLoading.vue'
|
|
8
|
+
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
clicksContext: {
|
|
11
|
+
type: Object as PropType<ClicksContext>,
|
|
12
|
+
required: true,
|
|
13
|
+
},
|
|
14
|
+
renderContext: {
|
|
15
|
+
type: String as PropType<RenderContext>,
|
|
16
|
+
default: 'slide',
|
|
17
|
+
},
|
|
18
|
+
active: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
22
|
+
is: {
|
|
23
|
+
type: Function as PropType<() => any>,
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
route: {
|
|
27
|
+
type: Object as PropType<SlideRoute>,
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
provideLocal(injectionRoute, props.route)
|
|
33
|
+
provideLocal(injectionCurrentPage, ref(props.route.no))
|
|
34
|
+
provideLocal(injectionRenderContext, ref(props.renderContext as RenderContext))
|
|
35
|
+
provideLocal(injectionActive, toRef(props, 'active'))
|
|
36
|
+
provideLocal(injectionClicksContext, toRef(props, 'clicksContext'))
|
|
37
|
+
|
|
38
|
+
const style = computed(() => {
|
|
39
|
+
const zoom = props.route.meta?.slide?.frontmatter.zoom ?? 1
|
|
40
|
+
return zoom === 1
|
|
41
|
+
? undefined
|
|
42
|
+
: {
|
|
43
|
+
width: `${100 / zoom}%`,
|
|
44
|
+
height: `${100 / zoom}%`,
|
|
45
|
+
transformOrigin: 'top left',
|
|
46
|
+
transform: `scale(${zoom})`,
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const SlideComponent = defineAsyncComponent({
|
|
51
|
+
loader: async () => {
|
|
52
|
+
const component = await props.is()
|
|
53
|
+
return defineComponent({
|
|
54
|
+
setup(_, { attrs }) {
|
|
55
|
+
onMounted(() => {
|
|
56
|
+
props.clicksContext.onMounted()
|
|
57
|
+
})
|
|
58
|
+
return () => h(component.default, attrs)
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
},
|
|
62
|
+
delay: 300,
|
|
63
|
+
loadingComponent: SlideLoading,
|
|
64
|
+
})
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<component
|
|
69
|
+
:is="SlideComponent"
|
|
70
|
+
:style="style"
|
|
71
|
+
:class="{ 'disable-view-transition': !['slide', 'presenter'].includes(props.renderContext) }"
|
|
72
|
+
/>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<style scoped>
|
|
76
|
+
.disable-view-transition:deep(*) {
|
|
77
|
+
view-transition-name: none !important;
|
|
78
|
+
}
|
|
79
|
+
</style>
|
package/internals/SlidesShow.vue
CHANGED
|
@@ -5,7 +5,7 @@ import { getSlideClass } from '../utils'
|
|
|
5
5
|
import { useViewTransition } from '../composables/useViewTransition'
|
|
6
6
|
import { skipTransition } from '../composables/hmr'
|
|
7
7
|
import { usePrimaryClicks } from '../composables/useClicks'
|
|
8
|
-
import SlideWrapper from './SlideWrapper'
|
|
8
|
+
import SlideWrapper from './SlideWrapper.vue'
|
|
9
9
|
import PresenterMouse from './PresenterMouse.vue'
|
|
10
10
|
|
|
11
11
|
import GlobalTop from '#slidev/global-components/top'
|
package/logic/nav.ts
CHANGED
package/logic/route.ts
CHANGED
|
@@ -19,7 +19,12 @@ export function useRouteQuery<T extends string | string[]>(
|
|
|
19
19
|
},
|
|
20
20
|
set(v) {
|
|
21
21
|
nextTick(() => {
|
|
22
|
-
router[unref(mode) as 'replace' | 'push']({
|
|
22
|
+
router[unref(mode) as 'replace' | 'push']({
|
|
23
|
+
query: {
|
|
24
|
+
...router.currentRoute.value.query,
|
|
25
|
+
[name]: `${v}` === defaultValue ? undefined : v,
|
|
26
|
+
},
|
|
27
|
+
})
|
|
23
28
|
})
|
|
24
29
|
},
|
|
25
30
|
})
|
package/logic/utils.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { parseRangeString } from '@slidev/parser/core'
|
|
1
2
|
import { useTimestamp } from '@vueuse/core'
|
|
2
3
|
import { computed, ref } from 'vue'
|
|
4
|
+
import { CLASS_VCLICK_TARGET } from '../constants'
|
|
3
5
|
|
|
4
6
|
export function useTimer() {
|
|
5
7
|
const tsStart = ref(Date.now())
|
|
@@ -24,7 +26,7 @@ export function useTimer() {
|
|
|
24
26
|
|
|
25
27
|
export function makeId(length = 5) {
|
|
26
28
|
const result = []
|
|
27
|
-
const characters = '
|
|
29
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
|
28
30
|
const charactersLength = characters.length
|
|
29
31
|
for (let i = 0; i < length; i++)
|
|
30
32
|
result.push(characters.charAt(Math.floor(Math.random() * charactersLength)))
|
|
@@ -48,3 +50,25 @@ export function normalizeAtProp(at: string | number = '+1'): [isRelative: boolea
|
|
|
48
50
|
n,
|
|
49
51
|
]
|
|
50
52
|
}
|
|
53
|
+
|
|
54
|
+
export function updateCodeHighlightRange(
|
|
55
|
+
rangeStr: string,
|
|
56
|
+
linesCount: number,
|
|
57
|
+
startLine: number,
|
|
58
|
+
getTokenOfLine: (line: number) => Element[],
|
|
59
|
+
) {
|
|
60
|
+
const highlights: number[] = parseRangeString(linesCount + startLine - 1, rangeStr)
|
|
61
|
+
for (let line = 0; line < linesCount; line++) {
|
|
62
|
+
const tokens = getTokenOfLine(line)
|
|
63
|
+
const isHighlighted = highlights.includes(line + startLine)
|
|
64
|
+
for (const token of tokens) {
|
|
65
|
+
// token.classList.toggle(CLASS_VCLICK_TARGET, true)
|
|
66
|
+
token.classList.toggle('slidev-code-highlighted', isHighlighted)
|
|
67
|
+
token.classList.toggle('slidev-code-dishonored', !isHighlighted)
|
|
68
|
+
|
|
69
|
+
// for backward compatibility
|
|
70
|
+
token.classList.toggle('highlighted', isHighlighted)
|
|
71
|
+
token.classList.toggle('dishonored', !isHighlighted)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
package/main.ts
CHANGED
package/modules/context.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { App } from 'vue'
|
|
2
|
-
import { computed, reactive, ref } from 'vue'
|
|
2
|
+
import { computed, reactive, ref, shallowRef } from 'vue'
|
|
3
3
|
import type { ComputedRef } from '@vue/reactivity'
|
|
4
4
|
import type { configs } from '../env'
|
|
5
5
|
import * as nav from '../logic/nav'
|
|
6
6
|
import { isDark } from '../logic/dark'
|
|
7
|
-
import { injectionCurrentPage, injectionRenderContext, injectionSlidevContext } from '../constants'
|
|
7
|
+
import { injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionSlidevContext } from '../constants'
|
|
8
8
|
import { useContext } from '../composables/useContext'
|
|
9
9
|
import type { SlidevContextNav } from '../composables/useNav'
|
|
10
|
+
import { useFixedClicks } from '../composables/useClicks'
|
|
10
11
|
|
|
11
12
|
export interface SlidevContext {
|
|
12
13
|
nav: SlidevContextNav
|
|
@@ -21,6 +22,7 @@ export function createSlidevContext() {
|
|
|
21
22
|
app.provide(injectionRenderContext, ref('none'))
|
|
22
23
|
app.provide(injectionSlidevContext, context)
|
|
23
24
|
app.provide(injectionCurrentPage, computed(() => context.nav.currentSlideNo))
|
|
25
|
+
app.provide(injectionClicksContext, shallowRef(useFixedClicks()))
|
|
24
26
|
|
|
25
27
|
// allows controls from postMessages
|
|
26
28
|
if (__DEV__) {
|
package/modules/mermaid.ts
CHANGED
|
@@ -8,8 +8,10 @@ mermaid.startOnLoad = false
|
|
|
8
8
|
mermaid.initialize({ startOnLoad: false })
|
|
9
9
|
|
|
10
10
|
const cache = new Map<string, string>()
|
|
11
|
+
let containerElement: Element | undefined
|
|
11
12
|
|
|
12
13
|
export async function renderMermaid(lzEncoded: string, options: any) {
|
|
14
|
+
containerElement ??= document.getElementById('mermaid-rendering-container')!
|
|
13
15
|
const key = lzEncoded + JSON.stringify(options)
|
|
14
16
|
const _cache = cache.get(key)
|
|
15
17
|
if (_cache)
|
|
@@ -22,7 +24,7 @@ export async function renderMermaid(lzEncoded: string, options: any) {
|
|
|
22
24
|
})
|
|
23
25
|
const code = lz.decompressFromBase64(lzEncoded)
|
|
24
26
|
const id = makeId()
|
|
25
|
-
const { svg } = await mermaid.render(id, code)
|
|
27
|
+
const { svg } = await mermaid.render(id, code, containerElement)
|
|
26
28
|
cache.set(key, svg)
|
|
27
29
|
return svg
|
|
28
30
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slidev/client",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.48.0-beta.
|
|
4
|
+
"version": "0.48.0-beta.23",
|
|
5
5
|
"description": "Presentation slides for developers",
|
|
6
6
|
"author": "antfu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@antfu/utils": "^0.7.7",
|
|
30
|
-
"@iconify-json/carbon": "^1.1.
|
|
30
|
+
"@iconify-json/carbon": "^1.1.31",
|
|
31
31
|
"@iconify-json/ph": "^1.1.11",
|
|
32
32
|
"@iconify-json/svg-spinners": "^1.1.2",
|
|
33
33
|
"@shikijs/monaco": "^1.1.7",
|
|
34
34
|
"@shikijs/vitepress-twoslash": "^1.1.7",
|
|
35
35
|
"@slidev/rough-notation": "^0.1.0",
|
|
36
36
|
"@typescript/ata": "^0.9.4",
|
|
37
|
-
"@unhead/vue": "^1.8.
|
|
37
|
+
"@unhead/vue": "^1.8.11",
|
|
38
38
|
"@unocss/reset": "^0.58.5",
|
|
39
39
|
"@vueuse/core": "^10.9.0",
|
|
40
40
|
"@vueuse/math": "^10.9.0",
|
|
@@ -47,20 +47,21 @@
|
|
|
47
47
|
"js-yaml": "^4.1.0",
|
|
48
48
|
"katex": "^0.16.9",
|
|
49
49
|
"lz-string": "^1.5.0",
|
|
50
|
-
"mermaid": "^10.
|
|
50
|
+
"mermaid": "^10.9.0",
|
|
51
51
|
"monaco-editor": "^0.46.0",
|
|
52
52
|
"prettier": "^3.2.5",
|
|
53
53
|
"recordrtc": "^5.6.2",
|
|
54
54
|
"shiki": "^1.1.7",
|
|
55
|
-
"shiki-magic-move": "^0.
|
|
56
|
-
"typescript": "^5.
|
|
55
|
+
"shiki-magic-move": "^0.3.4",
|
|
56
|
+
"typescript": "^5.4.2",
|
|
57
57
|
"unocss": "^0.58.5",
|
|
58
58
|
"vue": "^3.4.21",
|
|
59
|
+
"vue-demi": "^0.14.7",
|
|
59
60
|
"vue-router": "^4.3.0",
|
|
60
|
-
"@slidev/types": "0.48.0-beta.
|
|
61
|
-
"@slidev/parser": "0.48.0-beta.
|
|
61
|
+
"@slidev/types": "0.48.0-beta.23",
|
|
62
|
+
"@slidev/parser": "0.48.0-beta.23"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
|
-
"vite": "^5.1.
|
|
65
|
+
"vite": "^5.1.5"
|
|
65
66
|
}
|
|
66
67
|
}
|
package/pages/overview.vue
CHANGED
|
@@ -8,7 +8,7 @@ import { useFixedClicks } from '../composables/useClicks'
|
|
|
8
8
|
import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
|
|
9
9
|
import { getSlideClass } from '../utils'
|
|
10
10
|
import SlideContainer from '../internals/SlideContainer.vue'
|
|
11
|
-
import SlideWrapper from '../internals/SlideWrapper'
|
|
11
|
+
import SlideWrapper from '../internals/SlideWrapper.vue'
|
|
12
12
|
import DrawingPreview from '../internals/DrawingPreview.vue'
|
|
13
13
|
import IconButton from '../internals/IconButton.vue'
|
|
14
14
|
import NoteEditable from '../internals/NoteEditable.vue'
|
|
@@ -139,6 +139,7 @@ onMounted(() => {
|
|
|
139
139
|
:key="route.no"
|
|
140
140
|
:ref="el => blocks.set(idx, el as any)"
|
|
141
141
|
class="relative border-t border-main of-hidden flex gap-4 min-h-50 group"
|
|
142
|
+
:class="idx === 0 ? 'pt5' : ''"
|
|
142
143
|
>
|
|
143
144
|
<div class="select-none w-13 text-right my4 flex flex-col gap-1 items-end">
|
|
144
145
|
<div class="text-3xl op20 mb2">
|
|
@@ -192,19 +193,19 @@ onMounted(() => {
|
|
|
192
193
|
<IconButton
|
|
193
194
|
title="Edit Note"
|
|
194
195
|
class="rounded-full w-9 h-9 text-sm"
|
|
195
|
-
:class="edittingNote ===
|
|
196
|
-
@click="edittingNote =
|
|
196
|
+
:class="edittingNote === route.no ? 'important:op0' : ''"
|
|
197
|
+
@click="edittingNote = route.no"
|
|
197
198
|
>
|
|
198
199
|
<carbon:pen />
|
|
199
200
|
</IconButton>
|
|
200
201
|
</div>
|
|
201
202
|
<NoteEditable
|
|
202
|
-
:no="
|
|
203
|
+
:no="route.no"
|
|
203
204
|
class="max-w-250 w-250 text-lg rounded p3"
|
|
204
205
|
:auto-height="true"
|
|
205
|
-
:editing="edittingNote ===
|
|
206
|
+
:editing="edittingNote === route.no"
|
|
206
207
|
:clicks-context="getClicksContext(route)"
|
|
207
|
-
@dblclick="edittingNote !==
|
|
208
|
+
@dblclick="edittingNote !== route.no ? edittingNote = route.no : null"
|
|
208
209
|
@update:editing="edittingNote = null"
|
|
209
210
|
@marker-click="(e, clicks) => onMarkerClick(e, clicks, route)"
|
|
210
211
|
/>
|
package/pages/presenter.vue
CHANGED
|
@@ -12,7 +12,7 @@ import { getSlideClass } from '../utils'
|
|
|
12
12
|
import { useTimer } from '../logic/utils'
|
|
13
13
|
import { isDrawing } from '../logic/drawings'
|
|
14
14
|
import { useFixedClicks, usePrimaryClicks } from '../composables/useClicks'
|
|
15
|
-
import SlideWrapper from '../internals/SlideWrapper'
|
|
15
|
+
import SlideWrapper from '../internals/SlideWrapper.vue'
|
|
16
16
|
import SlideContainer from '../internals/SlideContainer.vue'
|
|
17
17
|
import NavControls from '../internals/NavControls.vue'
|
|
18
18
|
import QuickOverview from '../internals/QuickOverview.vue'
|