@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
|
@@ -12,13 +12,12 @@ Learn more: https://sli.dev/guide/syntax.html#line-highlighting
|
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
|
-
import { parseRangeString } from '@slidev/parser/core'
|
|
16
15
|
import { useClipboard } from '@vueuse/core'
|
|
17
16
|
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
18
17
|
import type { PropType } from 'vue'
|
|
19
18
|
import { configs } from '../env'
|
|
20
|
-
import { makeId } from '../logic/utils'
|
|
21
|
-
import { CLASS_VCLICK_HIDDEN
|
|
19
|
+
import { makeId, updateCodeHighlightRange } from '../logic/utils'
|
|
20
|
+
import { CLASS_VCLICK_HIDDEN } from '../constants'
|
|
22
21
|
import { useSlideContext } from '../context'
|
|
23
22
|
|
|
24
23
|
const props = defineProps({
|
|
@@ -87,28 +86,27 @@ onMounted(() => {
|
|
|
87
86
|
if (hide)
|
|
88
87
|
rangeStr = props.ranges[index.value + 1] ?? finallyRange.value
|
|
89
88
|
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
89
|
+
const pre = el.value.querySelector('.shiki')!
|
|
90
|
+
const lines = Array.from(pre.querySelectorAll('code > .line'))
|
|
91
|
+
const linesCount = lines.length
|
|
92
|
+
|
|
93
|
+
updateCodeHighlightRange(
|
|
94
|
+
rangeStr,
|
|
95
|
+
linesCount,
|
|
96
|
+
props.startLine,
|
|
97
|
+
no => [lines[no]],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// Scroll to the highlighted line if `maxHeight` is set
|
|
101
|
+
if (props.maxHeight) {
|
|
102
|
+
const highlightedEls = Array.from(pre.querySelectorAll('.line.highlighted')) as HTMLElement[]
|
|
103
|
+
const height = highlightedEls.reduce((acc, el) => el.offsetHeight + acc, 0)
|
|
104
|
+
if (height > el.value.offsetHeight) {
|
|
105
|
+
highlightedEls[0].scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
106
|
+
}
|
|
107
|
+
else if (highlightedEls.length > 0) {
|
|
108
|
+
const middleEl = highlightedEls[Math.round((highlightedEls.length - 1) / 2)]
|
|
109
|
+
middleEl.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
112
110
|
}
|
|
113
111
|
}
|
|
114
112
|
})
|
package/builtin/Monaco.vue
CHANGED
|
@@ -12,11 +12,12 @@ Learn more: https://sli.dev/guide/syntax.html#monaco-editor
|
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
|
-
import type * as monaco from 'monaco-editor'
|
|
16
|
-
import { computed, nextTick, onMounted, ref } from 'vue'
|
|
17
15
|
import { debounce } from '@antfu/utils'
|
|
18
16
|
import lz from 'lz-string'
|
|
17
|
+
import type * as monaco from 'monaco-editor'
|
|
18
|
+
import { computed, nextTick, onMounted, ref } from 'vue'
|
|
19
19
|
import { makeId } from '../logic/utils'
|
|
20
|
+
import CodeRunner from '../internals/CodeRunner.vue'
|
|
20
21
|
|
|
21
22
|
const props = withDefaults(defineProps<{
|
|
22
23
|
codeLz: string
|
|
@@ -27,6 +28,11 @@ const props = withDefaults(defineProps<{
|
|
|
27
28
|
height?: number | string // Posible values: 'initial', 'auto', '100%', '200px', etc.
|
|
28
29
|
editorOptions?: monaco.editor.IEditorOptions
|
|
29
30
|
ata?: boolean
|
|
31
|
+
runnable?: boolean
|
|
32
|
+
autorun?: boolean | 'once'
|
|
33
|
+
outputHeight?: string
|
|
34
|
+
highlightOutput?: boolean
|
|
35
|
+
runnerOptions?: Record<string, unknown>
|
|
30
36
|
}>(), {
|
|
31
37
|
codeLz: '',
|
|
32
38
|
lang: 'typescript',
|
|
@@ -34,10 +40,13 @@ const props = withDefaults(defineProps<{
|
|
|
34
40
|
lineNumbers: 'off',
|
|
35
41
|
height: 'initial',
|
|
36
42
|
ata: true,
|
|
43
|
+
runnable: false,
|
|
44
|
+
autorun: true,
|
|
45
|
+
highlightOutput: true,
|
|
37
46
|
})
|
|
38
47
|
|
|
39
|
-
const code = lz.decompressFromBase64(props.codeLz).trimEnd()
|
|
40
|
-
const diff = props.diffLz && lz.decompressFromBase64(props.diffLz).trimEnd()
|
|
48
|
+
const code = ref(lz.decompressFromBase64(props.codeLz).trimEnd())
|
|
49
|
+
const diff = props.diffLz && ref(lz.decompressFromBase64(props.diffLz).trimEnd())
|
|
41
50
|
|
|
42
51
|
const langMap: Record<string, string> = {
|
|
43
52
|
ts: 'typescript',
|
|
@@ -69,7 +78,8 @@ onMounted(async () => {
|
|
|
69
78
|
// Lazy load monaco, so it will be bundled in async chunk
|
|
70
79
|
const { default: setup } = await import('../setup/monaco')
|
|
71
80
|
const { ata, monaco } = await setup()
|
|
72
|
-
const model = monaco.editor.createModel(code, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
|
|
81
|
+
const model = monaco.editor.createModel(code.value, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
|
|
82
|
+
model.onDidChangeContent(() => code.value = model.getValue())
|
|
73
83
|
const commonOptions = {
|
|
74
84
|
automaticLayout: true,
|
|
75
85
|
readOnly: props.readonly,
|
|
@@ -89,7 +99,8 @@ onMounted(async () => {
|
|
|
89
99
|
|
|
90
100
|
let editableEditor: monaco.editor.IStandaloneCodeEditor
|
|
91
101
|
if (diff) {
|
|
92
|
-
const diffModel = monaco.editor.createModel(diff, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
|
|
102
|
+
const diffModel = monaco.editor.createModel(diff.value, lang, monaco.Uri.parse(`file:///${makeId()}.${ext}`))
|
|
103
|
+
diffModel.onDidChangeContent(() => code.value = model.getValue())
|
|
93
104
|
const editor = monaco.editor.createDiffEditor(container.value!, {
|
|
94
105
|
renderOverviewRuler: false,
|
|
95
106
|
...commonOptions,
|
|
@@ -140,11 +151,56 @@ onMounted(async () => {
|
|
|
140
151
|
: /* BELOW */ `` // reset
|
|
141
152
|
}
|
|
142
153
|
}
|
|
154
|
+
nextTick(() => monaco.editor.remeasureFonts())
|
|
143
155
|
})
|
|
144
156
|
</script>
|
|
145
157
|
|
|
146
158
|
<template>
|
|
147
|
-
<div
|
|
148
|
-
<div ref="
|
|
159
|
+
<div class="relative slidev-monaco-container">
|
|
160
|
+
<div ref="outer" class="relative slidev-monaco-container-inner" :style="{ height }">
|
|
161
|
+
<div ref="container" class="absolute inset-0.5" />
|
|
162
|
+
</div>
|
|
163
|
+
<CodeRunner
|
|
164
|
+
v-if="props.runnable"
|
|
165
|
+
v-model="code"
|
|
166
|
+
:lang="lang"
|
|
167
|
+
:autorun="props.autorun"
|
|
168
|
+
:height="props.outputHeight"
|
|
169
|
+
:highlight-output="props.highlightOutput"
|
|
170
|
+
:runner-options="props.runnerOptions"
|
|
171
|
+
/>
|
|
149
172
|
</div>
|
|
150
173
|
</template>
|
|
174
|
+
|
|
175
|
+
<style>
|
|
176
|
+
div[widgetid='messageoverlay'] {
|
|
177
|
+
transform: translateY(calc(100% * (var(--slidev-slide-scale) - 1)));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.slidev-monaco-container {
|
|
181
|
+
position: relative;
|
|
182
|
+
margin: var(--slidev-code-margin);
|
|
183
|
+
line-height: var(--slidev-code-line-height);
|
|
184
|
+
border-radius: var(--slidev-code-radius);
|
|
185
|
+
background: var(--slidev-code-background);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.slidev-monaco-container-inner {
|
|
189
|
+
padding: var(--slidev-code-padding);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.slidev-monaco-container .monaco-editor {
|
|
193
|
+
--monaco-monospace-font: var(--slidev-code-font-family);
|
|
194
|
+
--vscode-editor-background: var(--slidev-code-background);
|
|
195
|
+
--vscode-editorGutter-background: var(--slidev-code-background);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Revert styles */
|
|
199
|
+
.slidev-monaco-container .monaco-editor a {
|
|
200
|
+
border-bottom: none;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.slidev-monaco-container .monaco-editor a:hover {
|
|
204
|
+
border-bottom: none;
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ShikiMagicMovePrecompiled } from 'shiki-magic-move/vue'
|
|
3
3
|
import type { KeyedTokensInfo } from 'shiki-magic-move/types'
|
|
4
|
-
import { onMounted, onUnmounted, ref,
|
|
4
|
+
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
5
5
|
import lz from 'lz-string'
|
|
6
6
|
import { useSlideContext } from '../context'
|
|
7
|
-
import { makeId } from '../logic/utils'
|
|
8
|
-
|
|
9
|
-
import 'shiki-magic-move/style.css'
|
|
7
|
+
import { makeId, updateCodeHighlightRange } from '../logic/utils'
|
|
10
8
|
|
|
11
9
|
const props = defineProps<{
|
|
12
|
-
stepsLz: string
|
|
13
10
|
at?: string | number
|
|
11
|
+
stepsLz: string
|
|
12
|
+
stepRanges: string[][]
|
|
14
13
|
}>()
|
|
15
14
|
|
|
16
15
|
const steps = JSON.parse(lz.decompressFromBase64(props.stepsLz)) as KeyedTokensInfo[]
|
|
17
16
|
const { $clicksContext: clicks, $scale: scale } = useSlideContext()
|
|
18
17
|
const id = makeId()
|
|
19
|
-
|
|
18
|
+
|
|
19
|
+
const stepIndex = ref(0)
|
|
20
|
+
const container = ref<HTMLElement>()
|
|
21
|
+
|
|
22
|
+
// Normalized the ranges, to at least have one range
|
|
23
|
+
const ranges = computed(() => props.stepRanges.map(i => i.length ? i : ['all']))
|
|
20
24
|
|
|
21
25
|
onUnmounted(() => {
|
|
22
26
|
clicks!.unregister(id)
|
|
@@ -26,25 +30,74 @@ onMounted(() => {
|
|
|
26
30
|
if (!clicks || clicks.disabled)
|
|
27
31
|
return
|
|
28
32
|
|
|
29
|
-
|
|
33
|
+
if (ranges.value.length !== steps.length)
|
|
34
|
+
throw new Error('[slidev] The length of stepRanges does not match the length of steps, this is an internal error.')
|
|
35
|
+
|
|
36
|
+
const clickCounts = ranges.value.map(s => s.length).reduce((a, b) => a + b, 0)
|
|
37
|
+
const { start, end, delta } = clicks.resolve(props.at ?? '+1', clickCounts - 1)
|
|
30
38
|
clicks.register(id, { max: end, delta })
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
watch(
|
|
41
|
+
() => clicks.current,
|
|
42
|
+
() => {
|
|
43
|
+
// Calculate the step and rangeStr based on the current click count
|
|
44
|
+
const clickCount = clicks.current - start
|
|
45
|
+
let step = steps.length - 1
|
|
46
|
+
let _currentClickSum = 0
|
|
47
|
+
let rangeStr = 'all'
|
|
48
|
+
for (let i = 0; i < ranges.value.length; i++) {
|
|
49
|
+
const current = ranges.value[i]
|
|
50
|
+
if (clickCount < _currentClickSum + current.length - 1) {
|
|
51
|
+
step = i
|
|
52
|
+
rangeStr = current[clickCount - _currentClickSum + 1]
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
_currentClickSum += current.length || 1
|
|
56
|
+
}
|
|
57
|
+
stepIndex.value = step
|
|
58
|
+
|
|
59
|
+
const pre = container.value?.querySelector('.shiki') as HTMLElement
|
|
60
|
+
if (!pre)
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
const children = (Array.from(pre.children) as HTMLElement[])
|
|
64
|
+
.slice(1) // Remove the first anchor
|
|
65
|
+
.filter(i => !i.className.includes('shiki-magic-move-leave')) // Filter the leaving elements
|
|
66
|
+
|
|
67
|
+
// Group to lines between `<br>`
|
|
68
|
+
const lines = children.reduce((acc, el) => {
|
|
69
|
+
if (el.tagName === 'BR')
|
|
70
|
+
acc.push([])
|
|
71
|
+
else
|
|
72
|
+
acc[acc.length - 1].push(el)
|
|
73
|
+
return acc
|
|
74
|
+
}, [[]] as HTMLElement[][])
|
|
75
|
+
|
|
76
|
+
// Update highlight range
|
|
77
|
+
updateCodeHighlightRange(
|
|
78
|
+
rangeStr,
|
|
79
|
+
lines.length,
|
|
80
|
+
1,
|
|
81
|
+
no => lines[no],
|
|
82
|
+
)
|
|
83
|
+
},
|
|
84
|
+
{ immediate: true },
|
|
85
|
+
)
|
|
38
86
|
})
|
|
39
87
|
</script>
|
|
40
88
|
|
|
41
89
|
<template>
|
|
42
|
-
<div class="slidev-code-wrapper slidev-code-magic-move">
|
|
90
|
+
<div ref="container" class="slidev-code-wrapper slidev-code-magic-move relative">
|
|
43
91
|
<ShikiMagicMovePrecompiled
|
|
44
92
|
class="slidev-code relative shiki overflow-visible"
|
|
45
93
|
:steps="steps"
|
|
46
|
-
:step="
|
|
47
|
-
:options="{
|
|
94
|
+
:step="stepIndex"
|
|
95
|
+
:options="{
|
|
96
|
+
globalScale: scale,
|
|
97
|
+
// TODO: make this configurable later
|
|
98
|
+
duration: 800,
|
|
99
|
+
stagger: 1,
|
|
100
|
+
}"
|
|
48
101
|
/>
|
|
49
102
|
</div>
|
|
50
103
|
</template>
|
package/builtin/Tweet.vue
CHANGED
|
@@ -22,7 +22,14 @@ const tweet = ref<HTMLElement | null>()
|
|
|
22
22
|
const loaded = ref(false)
|
|
23
23
|
const tweetNotFound = ref(false)
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
async function create(retries = 10) {
|
|
26
|
+
// @ts-expect-error global
|
|
27
|
+
if (!window.twttr?.widgets?.createTweet) {
|
|
28
|
+
if (retries <= 0)
|
|
29
|
+
return console.error('Failed to load Twitter widget after 10 retries.')
|
|
30
|
+
setTimeout(() => create(retries - 1), 1000)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
26
33
|
// @ts-expect-error global
|
|
27
34
|
const element = await window.twttr.widgets.createTweet(
|
|
28
35
|
props.id.toString(),
|
|
@@ -36,6 +43,10 @@ onMounted(async () => {
|
|
|
36
43
|
loaded.value = true
|
|
37
44
|
if (element === undefined)
|
|
38
45
|
tweetNotFound.value = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onMounted(() => {
|
|
49
|
+
create()
|
|
39
50
|
})
|
|
40
51
|
</script>
|
|
41
52
|
|
package/composables/useClicks.ts
CHANGED
|
@@ -23,6 +23,7 @@ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): C
|
|
|
23
23
|
},
|
|
24
24
|
relativeOffsets,
|
|
25
25
|
map,
|
|
26
|
+
onMounted() {},
|
|
26
27
|
resolve(at, size = 1) {
|
|
27
28
|
const [isRelative, value] = normalizeAtProp(at)
|
|
28
29
|
if (isRelative) {
|
|
@@ -57,8 +58,7 @@ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): C
|
|
|
57
58
|
get total() {
|
|
58
59
|
// eslint-disable-next-line no-unused-expressions
|
|
59
60
|
routeForceRefresh.value
|
|
60
|
-
return clicksOverrides
|
|
61
|
-
?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
|
|
61
|
+
return clicksOverrides ?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
|
|
62
62
|
},
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -68,6 +68,7 @@ const queryClicksRaw = useRouteQuery('clicks', '0')
|
|
|
68
68
|
export function usePrimaryClicks(route: SlideRoute): ClicksContext {
|
|
69
69
|
if (route?.meta?.__clicksContext)
|
|
70
70
|
return route.meta.__clicksContext
|
|
71
|
+
|
|
71
72
|
const thisNo = route.no
|
|
72
73
|
const current = computed({
|
|
73
74
|
get() {
|
|
@@ -92,6 +93,13 @@ export function usePrimaryClicks(route: SlideRoute): ClicksContext {
|
|
|
92
93
|
current,
|
|
93
94
|
route?.meta?.clicks,
|
|
94
95
|
)
|
|
96
|
+
|
|
97
|
+
// On slide mounted, make sure the query is not greater than the total
|
|
98
|
+
context.onMounted = () => {
|
|
99
|
+
if (queryClicksRaw.value)
|
|
100
|
+
queryClicksRaw.value = Math.min(queryClicksRaw.value, context.total)
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
if (route?.meta)
|
|
96
104
|
route.meta.__clicksContext = context
|
|
97
105
|
return context
|
package/composables/useNav.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { ComputedRef, Ref, TransitionGroupProps } from 'vue'
|
|
|
3
3
|
import { computed, ref, watch } from 'vue'
|
|
4
4
|
import type { Router } from 'vue-router'
|
|
5
5
|
import { getCurrentTransition } from '../logic/transition'
|
|
6
|
-
import { getSlidePath } from '../logic/slides'
|
|
6
|
+
import { getSlide, getSlidePath } from '../logic/slides'
|
|
7
|
+
import { CLICKS_MAX } from '../constants'
|
|
7
8
|
import { useTocTree } from './useTocTree'
|
|
8
9
|
import { skipTransition } from './hmr'
|
|
9
10
|
import { slides } from '#slidev/slides'
|
|
@@ -121,12 +122,12 @@ export function useNavBase(
|
|
|
121
122
|
async function prevSlide(lastClicks = true) {
|
|
122
123
|
clicksDirection.value = -1
|
|
123
124
|
const next = Math.max(1, currentSlideNo.value - 1)
|
|
124
|
-
await go(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
await go(
|
|
126
|
+
next,
|
|
127
|
+
lastClicks
|
|
128
|
+
? getSlide(next)?.meta.__clicksContext?.total ?? CLICKS_MAX
|
|
129
|
+
: undefined,
|
|
130
|
+
)
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
function goFirst() {
|
|
@@ -141,7 +142,10 @@ export function useNavBase(
|
|
|
141
142
|
skipTransition.value = false
|
|
142
143
|
await router?.push({
|
|
143
144
|
path: getSlidePath(page),
|
|
144
|
-
query: {
|
|
145
|
+
query: {
|
|
146
|
+
...router.currentRoute.value.query,
|
|
147
|
+
clicks: clicks || undefined,
|
|
148
|
+
},
|
|
145
149
|
})
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -9,14 +9,14 @@ export interface UseSlideInfo {
|
|
|
9
9
|
update: (data: SlidePatch) => Promise<SlideInfo | void>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function useSlideInfo(
|
|
13
|
-
if (
|
|
12
|
+
export function useSlideInfo(no: number): UseSlideInfo {
|
|
13
|
+
if (no == null) {
|
|
14
14
|
return {
|
|
15
15
|
info: ref() as Ref<SlideInfo | undefined>,
|
|
16
16
|
update: async () => {},
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
const url = `/@slidev/slide/${
|
|
19
|
+
const url = `/@slidev/slide/${no}.json`
|
|
20
20
|
const { data: info, execute } = useFetch(url).json().get()
|
|
21
21
|
|
|
22
22
|
execute()
|
|
@@ -37,11 +37,11 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
|
|
|
37
37
|
|
|
38
38
|
if (__DEV__) {
|
|
39
39
|
import.meta.hot?.on('slidev:update-slide', (payload) => {
|
|
40
|
-
if (payload.
|
|
40
|
+
if (payload.no === no)
|
|
41
41
|
info.value = payload.data
|
|
42
42
|
})
|
|
43
43
|
import.meta.hot?.on('slidev:update-note', (payload) => {
|
|
44
|
-
if (payload.
|
|
44
|
+
if (payload.no === no && info.value.note?.trim() !== payload.note?.trim())
|
|
45
45
|
info.value = { ...info.value, ...payload }
|
|
46
46
|
})
|
|
47
47
|
}
|
|
@@ -52,20 +52,17 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
const map: Record<
|
|
55
|
+
const map: Record<number, UseSlideInfo> = {}
|
|
56
56
|
|
|
57
|
-
export function useDynamicSlideInfo(
|
|
58
|
-
function get(
|
|
59
|
-
|
|
60
|
-
if (!map[i])
|
|
61
|
-
map[i] = useSlideInfo(id)
|
|
62
|
-
return map[i]
|
|
57
|
+
export function useDynamicSlideInfo(no: MaybeRef<number>) {
|
|
58
|
+
function get(no: number) {
|
|
59
|
+
return map[no] ??= useSlideInfo(no)
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
return {
|
|
66
|
-
info: computed(() => get(unref(
|
|
63
|
+
info: computed(() => get(unref(no)).info.value),
|
|
67
64
|
update: async (data: SlidePatch, newId?: number) => {
|
|
68
|
-
const info = get(newId ?? unref(
|
|
65
|
+
const info = get(newId ?? unref(no))
|
|
69
66
|
const newData = await info.update(data)
|
|
70
67
|
if (newData)
|
|
71
68
|
info.info.value = newData
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ref } from 'vue'
|
|
2
2
|
import { useRouter } from 'vue-router'
|
|
3
|
+
import { getSlide } from '../logic/nav'
|
|
3
4
|
|
|
4
5
|
export function useViewTransition() {
|
|
5
6
|
const router = useRouter()
|
|
@@ -11,13 +12,15 @@ export function useViewTransition() {
|
|
|
11
12
|
const supportViewTransition = typeof document !== 'undefined' && 'startViewTransition' in document
|
|
12
13
|
|
|
13
14
|
router.beforeResolve((to, from) => {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
15
|
+
const fromMeta = getSlide(from.params.no as string)?.meta
|
|
16
|
+
const toMeta = getSlide(to.params.no as string)?.meta
|
|
17
|
+
const fromNo = fromMeta?.slide?.no
|
|
18
|
+
const toNo = toMeta?.slide?.no
|
|
16
19
|
if (
|
|
17
20
|
!(
|
|
18
21
|
fromNo !== undefined && toNo !== undefined && (
|
|
19
|
-
(
|
|
20
|
-
|| (
|
|
22
|
+
(fromMeta?.transition === 'view-transition' && fromNo < toNo)
|
|
23
|
+
|| (toMeta?.transition === 'view-transition' && toNo < fromNo)
|
|
21
24
|
)
|
|
22
25
|
)
|
|
23
26
|
) {
|
package/context.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ref,
|
|
1
|
+
import { ref, toRef } from 'vue'
|
|
2
2
|
import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
|
|
3
|
-
import { useFixedClicks } from './composables/useClicks'
|
|
4
3
|
import {
|
|
5
4
|
FRONTMATTER_FIELDS,
|
|
6
5
|
HEADMATTER_FIELDS,
|
|
@@ -13,15 +12,13 @@ import {
|
|
|
13
12
|
injectionSlidevContext,
|
|
14
13
|
} from './constants'
|
|
15
14
|
|
|
16
|
-
const clicksContextFallback = shallowRef(useFixedClicks())
|
|
17
|
-
|
|
18
15
|
/**
|
|
19
16
|
* Get the current slide context, should be called inside the setup function of a component inside slide
|
|
20
17
|
*/
|
|
21
18
|
export function useSlideContext() {
|
|
22
19
|
const $slidev = injectLocal(injectionSlidevContext)!
|
|
23
20
|
const $nav = toRef($slidev, 'nav')
|
|
24
|
-
const $clicksContext = injectLocal(injectionClicksContext
|
|
21
|
+
const $clicksContext = injectLocal(injectionClicksContext)!.value
|
|
25
22
|
const $clicks = toRef($clicksContext, 'current')
|
|
26
23
|
const $page = injectLocal(injectionCurrentPage)!
|
|
27
24
|
const $renderContext = injectLocal(injectionRenderContext)!
|
|
@@ -42,6 +39,8 @@ export function useSlideContext() {
|
|
|
42
39
|
}
|
|
43
40
|
}
|
|
44
41
|
|
|
42
|
+
export type SlideContext = ReturnType<typeof useSlideContext>
|
|
43
|
+
|
|
45
44
|
export function provideFrontmatter(frontmatter: Record<string, any>) {
|
|
46
45
|
provideLocal(injectionFrontmatter, frontmatter)
|
|
47
46
|
|
package/env.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { computed } from 'vue'
|
|
1
|
+
import { computed, ref } from 'vue'
|
|
2
2
|
import { objectMap } from '@antfu/utils'
|
|
3
3
|
import configs from '#slidev/configs'
|
|
4
4
|
|
|
5
5
|
export { configs }
|
|
6
6
|
|
|
7
|
-
export const slideAspect = configs.aspectRatio ?? (16 / 9)
|
|
8
|
-
export const slideWidth = configs.canvasWidth ?? 980
|
|
7
|
+
export const slideAspect = ref(configs.aspectRatio ?? (16 / 9))
|
|
8
|
+
export const slideWidth = ref(configs.canvasWidth ?? 980)
|
|
9
9
|
|
|
10
10
|
// To honor the aspect ratio more as possible, we need to approximate the height to the next integer.
|
|
11
11
|
// Doing this, we will prevent on print, to create an additional empty white page after each page.
|
|
12
|
-
export const slideHeight = Math.ceil(slideWidth / slideAspect)
|
|
12
|
+
export const slideHeight = computed(() => Math.ceil(slideWidth.value / slideAspect.value))
|
|
13
13
|
|
|
14
14
|
export const themeVars = computed(() => {
|
|
15
15
|
return objectMap(configs.themeConfig || {}, (k, v) => [`--slidev-theme-${k}`, v])
|