@slidev/client 0.48.0-beta.2 → 0.48.0-beta.20
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/App.vue +7 -0
- package/builtin/Arrow.vue +2 -4
- package/builtin/CodeBlockWrapper.vue +14 -6
- package/builtin/KaTexBlockWrapper.vue +5 -4
- package/builtin/Mermaid.vue +4 -3
- package/builtin/Monaco.vue +109 -92
- package/builtin/RenderWhen.vue +3 -3
- package/builtin/ShikiMagicMove.vue +50 -0
- 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/TocList.vue +3 -2
- package/builtin/Tweet.vue +3 -22
- package/builtin/VClick.ts +2 -1
- package/builtin/VClickGap.vue +3 -5
- package/builtin/VClicks.ts +1 -1
- package/composables/useClicks.ts +34 -16
- package/constants.ts +58 -8
- package/context.ts +73 -0
- package/env.ts +3 -12
- package/internals/ClicksSlider.vue +93 -0
- package/internals/Controls.vue +2 -2
- package/internals/DrawingControls.vue +39 -9
- package/internals/DrawingLayer.vue +3 -3
- package/internals/Goto.vue +5 -4
- package/internals/IconButton.vue +7 -3
- package/internals/InfoDialog.vue +1 -1
- package/internals/Modal.vue +1 -1
- package/internals/NavControls.vue +4 -5
- package/internals/NoteDisplay.vue +131 -8
- package/internals/NoteEditable.vue +128 -0
- package/internals/NoteStatic.vue +8 -6
- package/internals/PrintContainer.vue +4 -3
- package/internals/PrintSlide.vue +8 -2
- package/internals/PrintSlideClick.vue +5 -7
- package/internals/{SlidesOverview.vue → QuickOverview.vue} +21 -10
- package/internals/RecordingControls.vue +1 -1
- package/internals/RecordingDialog.vue +5 -6
- package/internals/{Editor.vue → SideEditor.vue} +7 -3
- package/internals/SlideContainer.vue +12 -9
- package/internals/SlideWrapper.ts +28 -12
- package/internals/SlidesShow.vue +7 -8
- package/layouts/two-cols-header.vue +9 -3
- package/logic/drawings.ts +6 -3
- package/logic/nav.ts +11 -8
- package/logic/note.ts +7 -7
- package/main.ts +8 -4
- package/modules/context.ts +4 -3
- package/modules/mermaid.ts +6 -7
- package/modules/{directives.ts → v-click.ts} +15 -15
- package/modules/v-mark.ts +159 -0
- package/package.json +26 -16
- package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
- package/{internals/NotesView.vue → pages/notes.vue} +5 -3
- package/pages/overview.vue +229 -0
- package/{internals/Play.vue → pages/play.vue} +15 -12
- package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +12 -7
- package/{internals/Presenter.vue → pages/presenter.vue} +108 -100
- package/{internals/Print.vue → pages/print.vue} +3 -4
- package/routes.ts +27 -51
- package/setup/codemirror.ts +8 -3
- package/setup/monaco.ts +108 -44
- package/setup/root.ts +2 -2
- package/shim-vue.d.ts +35 -0
- package/shim.d.ts +1 -13
- package/state/index.ts +10 -10
- package/styles/code.css +7 -3
- package/styles/index.css +68 -7
- package/styles/katex.css +1 -1
- package/styles/layouts-base.css +17 -12
- package/styles/monaco.css +27 -0
- package/styles/vars.css +1 -0
- package/uno.config.ts +14 -2
- package/iframes/monaco/index.css +0 -28
- package/iframes/monaco/index.html +0 -7
- package/iframes/monaco/index.ts +0 -260
- package/internals/NoteEditor.vue +0 -88
package/internals/SlidesShow.vue
CHANGED
|
@@ -6,14 +6,11 @@ import { useViewTransition } from '../composables/useViewTransition'
|
|
|
6
6
|
import { skipTransition } from '../composables/hmr'
|
|
7
7
|
import { usePrimaryClicks } from '../composables/useClicks'
|
|
8
8
|
import SlideWrapper from './SlideWrapper'
|
|
9
|
-
|
|
10
|
-
// @ts-expect-error virtual module
|
|
11
|
-
import GlobalTop from '/@slidev/global-components/top'
|
|
12
|
-
|
|
13
|
-
// @ts-expect-error virtual module
|
|
14
|
-
import GlobalBottom from '/@slidev/global-components/bottom'
|
|
15
9
|
import PresenterMouse from './PresenterMouse.vue'
|
|
16
10
|
|
|
11
|
+
import GlobalTop from '#slidev/global-components/top'
|
|
12
|
+
import GlobalBottom from '#slidev/global-components/bottom'
|
|
13
|
+
|
|
17
14
|
defineProps<{
|
|
18
15
|
renderContext: 'slide' | 'presenter'
|
|
19
16
|
}>()
|
|
@@ -77,10 +74,12 @@ function onAfterLeave() {
|
|
|
77
74
|
|
|
78
75
|
<style scoped>
|
|
79
76
|
#slideshow {
|
|
80
|
-
|
|
77
|
+
height: 100%;
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
#slideshow > div {
|
|
84
|
-
|
|
81
|
+
position: absolute;
|
|
82
|
+
height: 100%;
|
|
83
|
+
width: 100%;
|
|
85
84
|
}
|
|
86
85
|
</style>
|
|
@@ -51,9 +51,15 @@ const props = defineProps({
|
|
|
51
51
|
grid-template-rows: repeat(2, 1fr);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
.col-header {
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
.col-header {
|
|
55
|
+
grid-area: 1 / 1 / 2 / 3;
|
|
56
|
+
}
|
|
57
|
+
.col-left {
|
|
58
|
+
grid-area: 2 / 1 / 3 / 2;
|
|
59
|
+
}
|
|
60
|
+
.col-right {
|
|
61
|
+
grid-area: 2 / 2 / 3 / 3;
|
|
62
|
+
}
|
|
57
63
|
.col-bottom {
|
|
58
64
|
align-self: end;
|
|
59
65
|
grid-area: 3 / 1 / 3 / 3;
|
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
|
@@ -7,6 +7,7 @@ import { rawRoutes, router } from '../routes'
|
|
|
7
7
|
import { configs } from '../env'
|
|
8
8
|
import { skipTransition } from '../composables/hmr'
|
|
9
9
|
import { usePrimaryClicks } from '../composables/useClicks'
|
|
10
|
+
import { CLICKS_MAX } from '../constants'
|
|
10
11
|
import { useRouteQuery } from './route'
|
|
11
12
|
import { isDrawing } from './drawings'
|
|
12
13
|
|
|
@@ -22,6 +23,7 @@ nextTick(() => {
|
|
|
22
23
|
})
|
|
23
24
|
|
|
24
25
|
export const navDirection = ref(0)
|
|
26
|
+
export const clicksDirection = ref(0)
|
|
25
27
|
|
|
26
28
|
export const route = computed(() => router.currentRoute.value)
|
|
27
29
|
|
|
@@ -38,7 +40,7 @@ export const queryClicks = computed({
|
|
|
38
40
|
get() {
|
|
39
41
|
// eslint-disable-next-line ts/no-use-before-define
|
|
40
42
|
if (clicksContext.value.disabled)
|
|
41
|
-
return
|
|
43
|
+
return CLICKS_MAX
|
|
42
44
|
let v = +(queryClicksRaw.value || 0)
|
|
43
45
|
if (Number.isNaN(v))
|
|
44
46
|
v = 0
|
|
@@ -84,6 +86,7 @@ watch(currentRoute, (next, prev) => {
|
|
|
84
86
|
})
|
|
85
87
|
|
|
86
88
|
export async function next() {
|
|
89
|
+
clicksDirection.value = 1
|
|
87
90
|
if (clicksTotal.value <= queryClicks.value)
|
|
88
91
|
await nextSlide()
|
|
89
92
|
else
|
|
@@ -91,6 +94,7 @@ export async function next() {
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
export async function prev() {
|
|
97
|
+
clicksDirection.value = -1
|
|
94
98
|
if (queryClicks.value <= 0)
|
|
95
99
|
await prevSlide()
|
|
96
100
|
else
|
|
@@ -102,11 +106,13 @@ export function getPath(no: number | string) {
|
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
export async function nextSlide() {
|
|
109
|
+
clicksDirection.value = 1
|
|
105
110
|
if (currentPage.value < rawRoutes.length)
|
|
106
111
|
await go(currentPage.value + 1)
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
export async function prevSlide(lastClicks = true) {
|
|
115
|
+
clicksDirection.value = -1
|
|
110
116
|
const next = Math.max(1, currentPage.value - 1)
|
|
111
117
|
await go(next)
|
|
112
118
|
if (lastClicks && clicksTotal.value)
|
|
@@ -129,16 +135,13 @@ export function go(page: number | string, clicks?: number) {
|
|
|
129
135
|
export function useSwipeControls(root: Ref<HTMLElement | undefined>) {
|
|
130
136
|
const swipeBegin = ref(0)
|
|
131
137
|
const { direction, distanceX, distanceY } = usePointerSwipe(root, {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return
|
|
138
|
+
pointerTypes: ['touch'],
|
|
139
|
+
onSwipeStart() {
|
|
135
140
|
if (isDrawing.value)
|
|
136
141
|
return
|
|
137
142
|
swipeBegin.value = timestamp()
|
|
138
143
|
},
|
|
139
|
-
onSwipeEnd(
|
|
140
|
-
if (e.pointerType !== 'touch')
|
|
141
|
-
return
|
|
144
|
+
onSwipeEnd() {
|
|
142
145
|
if (!swipeBegin.value)
|
|
143
146
|
return
|
|
144
147
|
if (isDrawing.value)
|
|
@@ -177,7 +180,7 @@ export async function downloadPDF() {
|
|
|
177
180
|
export async function openInEditor(url?: string) {
|
|
178
181
|
if (url == null) {
|
|
179
182
|
const slide = currentRoute.value?.meta?.slide
|
|
180
|
-
if (!slide
|
|
183
|
+
if (!slide)
|
|
181
184
|
return false
|
|
182
185
|
url = `${slide.filepath}:${slide.start}`
|
|
183
186
|
}
|
package/logic/note.ts
CHANGED
|
@@ -2,17 +2,17 @@ import type { MaybeRef } from '@vueuse/core'
|
|
|
2
2
|
import { useFetch } from '@vueuse/core'
|
|
3
3
|
import type { Ref } from 'vue'
|
|
4
4
|
import { computed, ref, unref } from 'vue'
|
|
5
|
-
import type { SlideInfo,
|
|
5
|
+
import type { SlideInfo, SlidePatch } from '@slidev/types'
|
|
6
6
|
|
|
7
7
|
export interface UseSlideInfo {
|
|
8
|
-
info: Ref<
|
|
9
|
-
update: (data:
|
|
8
|
+
info: Ref<SlideInfo | undefined>
|
|
9
|
+
update: (data: SlidePatch) => Promise<SlideInfo | void>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function useSlideInfo(id: number | undefined): UseSlideInfo {
|
|
13
13
|
if (id == null) {
|
|
14
14
|
return {
|
|
15
|
-
info: ref() as Ref<
|
|
15
|
+
info: ref() as Ref<SlideInfo | undefined>,
|
|
16
16
|
update: async () => {},
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -21,7 +21,7 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
|
|
|
21
21
|
|
|
22
22
|
execute()
|
|
23
23
|
|
|
24
|
-
const update = async (data:
|
|
24
|
+
const update = async (data: SlidePatch) => {
|
|
25
25
|
return await fetch(
|
|
26
26
|
url,
|
|
27
27
|
{
|
|
@@ -41,7 +41,7 @@ export function useSlideInfo(id: number | undefined): UseSlideInfo {
|
|
|
41
41
|
info.value = payload.data
|
|
42
42
|
})
|
|
43
43
|
import.meta.hot?.on('slidev-update-note', (payload) => {
|
|
44
|
-
if (payload.id === id && info.value.note
|
|
44
|
+
if (payload.id === id && info.value.note?.trim() !== payload.note?.trim())
|
|
45
45
|
info.value = { ...info.value, ...payload }
|
|
46
46
|
})
|
|
47
47
|
}
|
|
@@ -64,7 +64,7 @@ export function useDynamicSlideInfo(id: MaybeRef<number | undefined>) {
|
|
|
64
64
|
|
|
65
65
|
return {
|
|
66
66
|
info: computed(() => get(unref(id)).info.value),
|
|
67
|
-
update: async (data:
|
|
67
|
+
update: async (data: SlidePatch, newId?: number) => {
|
|
68
68
|
const info = get(newId ?? unref(id))
|
|
69
69
|
const newData = await info.update(data)
|
|
70
70
|
if (newData)
|
package/main.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
/// <reference types="@slidev/types/client" />
|
|
2
|
+
|
|
1
3
|
import { createApp } from 'vue'
|
|
2
4
|
import { createHead } from '@unhead/vue'
|
|
3
5
|
import App from './App.vue'
|
|
4
6
|
import setupMain from './setup/main'
|
|
5
7
|
import { router } from './routes'
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
+
import { createVClickDirectives } from './modules/v-click'
|
|
9
|
+
import { createVMarkDirective } from './modules/v-mark'
|
|
10
|
+
import { createSlidevContext } from './modules/context'
|
|
8
11
|
|
|
9
|
-
import '
|
|
12
|
+
import '#slidev/styles'
|
|
10
13
|
|
|
11
14
|
const app = createApp(App)
|
|
12
15
|
app.use(router)
|
|
13
16
|
app.use(createHead())
|
|
14
|
-
app.use(
|
|
17
|
+
app.use(createVClickDirectives())
|
|
18
|
+
app.use(createVMarkDirective())
|
|
15
19
|
app.use(createSlidevContext())
|
|
16
20
|
|
|
17
21
|
setupMain({ app, router })
|
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'
|
|
@@ -21,10 +21,11 @@ export interface SlidevContext {
|
|
|
21
21
|
themeConfigs: ComputedRef<typeof configs['themeConfig']>
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export
|
|
24
|
+
export 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/modules/mermaid.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import mermaid from 'mermaid/dist/mermaid.esm.mjs'
|
|
2
|
-
import
|
|
3
|
-
import { decode } from 'js-base64'
|
|
2
|
+
import lz from 'lz-string'
|
|
4
3
|
import { clearUndefined } from '@antfu/utils'
|
|
5
4
|
import setupMermaid from '../setup/mermaid'
|
|
5
|
+
import { makeId } from '../logic/utils'
|
|
6
6
|
|
|
7
7
|
mermaid.startOnLoad = false
|
|
8
8
|
mermaid.initialize({ startOnLoad: false })
|
|
9
9
|
|
|
10
|
-
const nanoid = customAlphabet('abcedfghicklmn', 10)
|
|
11
10
|
const cache = new Map<string, string>()
|
|
12
11
|
|
|
13
|
-
export async function renderMermaid(
|
|
14
|
-
const key =
|
|
12
|
+
export async function renderMermaid(lzEncoded: string, options: any) {
|
|
13
|
+
const key = lzEncoded + JSON.stringify(options)
|
|
15
14
|
const _cache = cache.get(key)
|
|
16
15
|
if (_cache)
|
|
17
16
|
return _cache
|
|
@@ -21,8 +20,8 @@ export async function renderMermaid(encoded: string, options: any) {
|
|
|
21
20
|
...clearUndefined(setupMermaid() || {}),
|
|
22
21
|
...clearUndefined(options),
|
|
23
22
|
})
|
|
24
|
-
const code =
|
|
25
|
-
const id =
|
|
23
|
+
const code = lz.decompressFromBase64(lzEncoded)
|
|
24
|
+
const id = makeId()
|
|
26
25
|
const { svg } = await mermaid.render(id, code)
|
|
27
26
|
cache.set(key, svg)
|
|
28
27
|
return svg
|
|
@@ -11,19 +11,21 @@ import {
|
|
|
11
11
|
injectionClicksContext,
|
|
12
12
|
} from '../constants'
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
export type VClickValue = string | [string | number, string | number] | boolean
|
|
15
|
+
|
|
16
|
+
export function dirInject<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, defaultValue?: T): T | undefined {
|
|
15
17
|
return (dir.instance?.$ as any).provides[key as any] ?? defaultValue
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export
|
|
20
|
+
export function createVClickDirectives() {
|
|
19
21
|
return {
|
|
20
22
|
install(app: App) {
|
|
21
|
-
app.directive('click', {
|
|
23
|
+
app.directive<HTMLElement, VClickValue>('click', {
|
|
22
24
|
// @ts-expect-error extra prop
|
|
23
25
|
name: 'v-click',
|
|
24
26
|
|
|
25
|
-
mounted(el
|
|
26
|
-
const resolved = resolveClick(el, dir)
|
|
27
|
+
mounted(el, dir) {
|
|
28
|
+
const resolved = resolveClick(el, dir, dir.value)
|
|
27
29
|
if (resolved == null)
|
|
28
30
|
return
|
|
29
31
|
|
|
@@ -55,12 +57,12 @@ export default function createDirectives() {
|
|
|
55
57
|
unmounted,
|
|
56
58
|
})
|
|
57
59
|
|
|
58
|
-
app.directive('after', {
|
|
60
|
+
app.directive<HTMLElement, VClickValue>('after', {
|
|
59
61
|
// @ts-expect-error extra prop
|
|
60
62
|
name: 'v-after',
|
|
61
63
|
|
|
62
|
-
mounted(el
|
|
63
|
-
const resolved = resolveClick(el, dir, true)
|
|
64
|
+
mounted(el, dir) {
|
|
65
|
+
const resolved = resolveClick(el, dir, dir.value, true)
|
|
64
66
|
if (resolved == null)
|
|
65
67
|
return
|
|
66
68
|
|
|
@@ -86,12 +88,12 @@ export default function createDirectives() {
|
|
|
86
88
|
unmounted,
|
|
87
89
|
})
|
|
88
90
|
|
|
89
|
-
app.directive('click-hide', {
|
|
91
|
+
app.directive<HTMLElement, VClickValue>('click-hide', {
|
|
90
92
|
// @ts-expect-error extra prop
|
|
91
93
|
name: 'v-click-hide',
|
|
92
94
|
|
|
93
|
-
mounted(el
|
|
94
|
-
const resolved = resolveClick(el, dir, false, true)
|
|
95
|
+
mounted(el, dir) {
|
|
96
|
+
const resolved = resolveClick(el, dir, dir.value, false, true)
|
|
95
97
|
if (resolved == null)
|
|
96
98
|
return
|
|
97
99
|
|
|
@@ -127,14 +129,12 @@ function isCurrent(thisClick: number | [number, number], clicks: number) {
|
|
|
127
129
|
: thisClick === clicks
|
|
128
130
|
}
|
|
129
131
|
|
|
130
|
-
function resolveClick(el: Element, dir: DirectiveBinding<any>, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
|
|
132
|
+
export function resolveClick(el: Element, dir: DirectiveBinding<any>, value: VClickValue, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
|
|
131
133
|
const ctx = dirInject(dir, injectionClicksContext)?.value
|
|
132
134
|
|
|
133
135
|
if (!el || !ctx || ctx.disabled)
|
|
134
136
|
return null
|
|
135
137
|
|
|
136
|
-
let value = dir.value
|
|
137
|
-
|
|
138
138
|
if (value === false || value === 'false')
|
|
139
139
|
return null
|
|
140
140
|
|
|
@@ -153,7 +153,7 @@ function resolveClick(el: Element, dir: DirectiveBinding<any>, clickAfter = fals
|
|
|
153
153
|
// range (absolute)
|
|
154
154
|
delta = 0
|
|
155
155
|
thisClick = value as [number, number]
|
|
156
|
-
maxClick = value[1]
|
|
156
|
+
maxClick = +value[1]
|
|
157
157
|
}
|
|
158
158
|
else {
|
|
159
159
|
({ start: thisClick, end: maxClick, delta } = ctx.resolve(value))
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { RoughAnnotationConfig } from '@slidev/rough-notation'
|
|
2
|
+
import { annotate } from '@slidev/rough-notation'
|
|
3
|
+
import type { App } from 'vue'
|
|
4
|
+
import { computed, watchEffect } from 'vue'
|
|
5
|
+
import type { VClickValue } from './v-click'
|
|
6
|
+
import { resolveClick } from './v-click'
|
|
7
|
+
|
|
8
|
+
export interface RoughDirectiveOptions extends Partial<RoughAnnotationConfig> {
|
|
9
|
+
at: VClickValue
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type RoughDirectiveValue = VClickValue | RoughDirectiveOptions
|
|
13
|
+
|
|
14
|
+
function addClass(options: RoughDirectiveOptions, cls: string) {
|
|
15
|
+
options.class = [options.class, cls].filter(Boolean).join(' ')
|
|
16
|
+
return options
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Definitions of supported modifiers
|
|
20
|
+
const vMarkModifiers: Record<string, (options: RoughDirectiveOptions, value?: any) => RoughDirectiveOptions> = {
|
|
21
|
+
// Types
|
|
22
|
+
'box': options => Object.assign(options, { type: 'box' }),
|
|
23
|
+
'circle': options => Object.assign(options, { type: 'circle' }),
|
|
24
|
+
'underline': options => Object.assign(options, { type: 'underline' }),
|
|
25
|
+
'highlight': options => Object.assign(options, { type: 'highlight' }),
|
|
26
|
+
'strike-through': options => Object.assign(options, { type: 'strike-through' }),
|
|
27
|
+
'crossed-off': options => Object.assign(options, { type: 'crossed-off' }),
|
|
28
|
+
'bracket': options => Object.assign(options, { type: 'bracket' }),
|
|
29
|
+
|
|
30
|
+
// Type Aliases
|
|
31
|
+
'strike': options => Object.assign(options, { type: 'strike-through' }),
|
|
32
|
+
'cross': options => Object.assign(options, { type: 'crossed-off' }),
|
|
33
|
+
'crossed': options => Object.assign(options, { type: 'crossed-off' }),
|
|
34
|
+
'linethrough': options => Object.assign(options, { type: 'strike-through' }),
|
|
35
|
+
'line-through': options => Object.assign(options, { type: 'strike-through' }),
|
|
36
|
+
|
|
37
|
+
// Colors
|
|
38
|
+
// @unocss-include
|
|
39
|
+
'black': options => addClass(options, 'text-black'),
|
|
40
|
+
'blue': options => addClass(options, 'text-blue'),
|
|
41
|
+
'cyan': options => addClass(options, 'text-cyan'),
|
|
42
|
+
'gray': options => addClass(options, 'text-gray'),
|
|
43
|
+
'green': options => addClass(options, 'text-green'),
|
|
44
|
+
'indigo': options => addClass(options, 'text-indigo'),
|
|
45
|
+
'lime': options => addClass(options, 'text-lime'),
|
|
46
|
+
'orange': options => addClass(options, 'text-orange'),
|
|
47
|
+
'pink': options => addClass(options, 'text-pink'),
|
|
48
|
+
'purple': options => addClass(options, 'text-purple'),
|
|
49
|
+
'red': options => addClass(options, 'text-red'),
|
|
50
|
+
'teal': options => addClass(options, 'text-teal'),
|
|
51
|
+
'white': options => addClass(options, 'text-white'),
|
|
52
|
+
'yellow': options => addClass(options, 'text-yellow'),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const vMarkModifiersDynamic: [RegExp, (match: RegExpMatchArray, options: RoughDirectiveOptions, value?: any) => RoughDirectiveOptions][] = [
|
|
56
|
+
// Support setting delay like `v-mark.delay300="1"`
|
|
57
|
+
[/^delay-?(\d+)?$/, (match, options, value) => {
|
|
58
|
+
const ms = (match[1] ? Number.parseInt(match[1]) : value) || 300
|
|
59
|
+
options.delay = ms
|
|
60
|
+
return options
|
|
61
|
+
}],
|
|
62
|
+
// Support setting opacity like `v-mark.op50="1"`
|
|
63
|
+
[/^(?:op|opacity)-?(\d+)?$/, (match, options, value) => {
|
|
64
|
+
const opacity = (match[1] ? Number.parseInt(match[1]) : value) || 100
|
|
65
|
+
options.opacity = opacity / 100
|
|
66
|
+
return options
|
|
67
|
+
}],
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* This supports v-mark directive to add notations to elements, powered by `rough-notation`.
|
|
72
|
+
*/
|
|
73
|
+
export function createVMarkDirective() {
|
|
74
|
+
return {
|
|
75
|
+
install(app: App) {
|
|
76
|
+
app.directive<HTMLElement, RoughDirectiveValue>('mark', {
|
|
77
|
+
// @ts-expect-error extra prop
|
|
78
|
+
name: 'v-mark',
|
|
79
|
+
|
|
80
|
+
mounted: (el, binding) => {
|
|
81
|
+
const options = computed(() => {
|
|
82
|
+
const bindingOptions = (typeof binding.value === 'object' && !Array.isArray(binding.value))
|
|
83
|
+
? { ...binding.value }
|
|
84
|
+
: { at: binding.value }
|
|
85
|
+
|
|
86
|
+
let modifierOptions: RoughDirectiveOptions = { at: bindingOptions.at }
|
|
87
|
+
const unknownModifiers = Object.entries(binding.modifiers)
|
|
88
|
+
.filter(([k, v]) => {
|
|
89
|
+
if (vMarkModifiers[k]) {
|
|
90
|
+
modifierOptions = vMarkModifiers[k](modifierOptions, v)
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
for (const [re, fn] of vMarkModifiersDynamic) {
|
|
94
|
+
const match = k.match(re)
|
|
95
|
+
if (match) {
|
|
96
|
+
modifierOptions = fn(match, modifierOptions, v)
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return true
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
if (unknownModifiers.length)
|
|
104
|
+
console.warn('[Slidev] Invalid modifiers for v-mark:', unknownModifiers)
|
|
105
|
+
|
|
106
|
+
const options = {
|
|
107
|
+
...modifierOptions,
|
|
108
|
+
...bindingOptions,
|
|
109
|
+
}
|
|
110
|
+
options.type ||= 'underline'
|
|
111
|
+
|
|
112
|
+
return options
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const annotation = annotate(el, options.value as RoughAnnotationConfig)
|
|
116
|
+
|
|
117
|
+
const resolvedClick = resolveClick(el, binding, options.value.at)
|
|
118
|
+
if (!resolvedClick) {
|
|
119
|
+
console.error('[Slidev] Invalid value for v-mark:', options.value.at)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
watchEffect(() => {
|
|
124
|
+
let shouldShow: boolean | undefined
|
|
125
|
+
|
|
126
|
+
if (options.value.class)
|
|
127
|
+
annotation.class = options.value.class
|
|
128
|
+
if (options.value.color)
|
|
129
|
+
annotation.color = options.value.color
|
|
130
|
+
|
|
131
|
+
const at = options.value.at
|
|
132
|
+
|
|
133
|
+
if (at === true) {
|
|
134
|
+
shouldShow = true
|
|
135
|
+
}
|
|
136
|
+
else if (at === false) {
|
|
137
|
+
shouldShow = false
|
|
138
|
+
}
|
|
139
|
+
else if (resolvedClick) {
|
|
140
|
+
shouldShow = resolvedClick.isActive.value
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.error('[Slidev] Invalid value for v-mark:', at)
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (shouldShow == null)
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
if (shouldShow)
|
|
151
|
+
annotation.show()
|
|
152
|
+
else
|
|
153
|
+
annotation.hide()
|
|
154
|
+
})
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
}
|
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.20",
|
|
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,34 +29,37 @@
|
|
|
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/
|
|
32
|
+
"@shikijs/monaco": "^1.1.7",
|
|
33
|
+
"@shikijs/vitepress-twoslash": "^1.1.7",
|
|
34
|
+
"@slidev/rough-notation": "^0.1.0",
|
|
35
|
+
"@typescript/ata": "^0.9.4",
|
|
26
36
|
"@unhead/vue": "^1.8.10",
|
|
27
37
|
"@unocss/reset": "^0.58.5",
|
|
28
|
-
"@vueuse/core": "^10.
|
|
29
|
-
"@vueuse/math": "^10.
|
|
30
|
-
"@vueuse/motion": "^2.
|
|
38
|
+
"@vueuse/core": "^10.9.0",
|
|
39
|
+
"@vueuse/math": "^10.9.0",
|
|
40
|
+
"@vueuse/motion": "^2.1.0",
|
|
31
41
|
"codemirror": "^5.65.16",
|
|
32
|
-
"
|
|
33
|
-
"drauu": "^0.3.7",
|
|
42
|
+
"drauu": "^0.4.0",
|
|
34
43
|
"file-saver": "^2.0.5",
|
|
35
44
|
"floating-vue": "^5.2.2",
|
|
36
45
|
"fuse.js": "^7.0.0",
|
|
37
|
-
"js-base64": "^3.7.6",
|
|
38
46
|
"js-yaml": "^4.1.0",
|
|
39
47
|
"katex": "^0.16.9",
|
|
48
|
+
"lz-string": "^1.5.0",
|
|
40
49
|
"mermaid": "^10.8.0",
|
|
41
|
-
"monaco-editor": "^0.
|
|
42
|
-
"nanoid": "^5.0.5",
|
|
50
|
+
"monaco-editor": "^0.46.0",
|
|
43
51
|
"prettier": "^3.2.5",
|
|
44
52
|
"recordrtc": "^5.6.2",
|
|
45
|
-
"
|
|
53
|
+
"shiki": "^1.1.7",
|
|
54
|
+
"shiki-magic-move": "^0.1.0",
|
|
55
|
+
"typescript": "^5.3.3",
|
|
46
56
|
"unocss": "^0.58.5",
|
|
47
|
-
"vue": "^3.4.
|
|
48
|
-
"vue-router": "^4.
|
|
49
|
-
"@slidev/
|
|
50
|
-
"@slidev/
|
|
57
|
+
"vue": "^3.4.20",
|
|
58
|
+
"vue-router": "^4.3.0",
|
|
59
|
+
"@slidev/types": "0.48.0-beta.20",
|
|
60
|
+
"@slidev/parser": "0.48.0-beta.20"
|
|
51
61
|
},
|
|
52
62
|
"devDependencies": {
|
|
53
|
-
"vite": "^5.1.
|
|
63
|
+
"vite": "^5.1.4"
|
|
54
64
|
}
|
|
55
65
|
}
|
|
@@ -21,5 +21,12 @@
|
|
|
21
21
|
<carbon:catalog class="text-3em op50" />
|
|
22
22
|
Notes
|
|
23
23
|
</RouterLink>
|
|
24
|
+
<RouterLink
|
|
25
|
+
to="/overview"
|
|
26
|
+
class="flex flex-col gap-2 items-center justify-center h-40 min-w-40 rounded bg-gray:10 p4 hover:bg-gray/20"
|
|
27
|
+
>
|
|
28
|
+
<carbon:list-boxes class="text-3em op50" />
|
|
29
|
+
Overview
|
|
30
|
+
</RouterLink>
|
|
24
31
|
</div>
|
|
25
32
|
</template>
|
|
@@ -7,8 +7,8 @@ import { sharedState } from '../state/shared'
|
|
|
7
7
|
import { fullscreen } from '../state'
|
|
8
8
|
import { total } from '../logic/nav'
|
|
9
9
|
import { rawRoutes } from '../routes'
|
|
10
|
-
import NoteDisplay from '
|
|
11
|
-
import IconButton from '
|
|
10
|
+
import NoteDisplay from '../internals/NoteDisplay.vue'
|
|
11
|
+
import IconButton from '../internals/IconButton.vue'
|
|
12
12
|
|
|
13
13
|
const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
|
|
14
14
|
useHead({
|
|
@@ -51,9 +51,11 @@ function decreaseFontSize() {
|
|
|
51
51
|
:note="currentRoute?.meta?.slide?.note"
|
|
52
52
|
:note-html="currentRoute?.meta?.slide?.noteHTML"
|
|
53
53
|
:placeholder="`No notes for Slide ${pageNo}.`"
|
|
54
|
+
:clicks-context="currentRoute?.meta?.__clicksContext"
|
|
55
|
+
:auto-scroll="true"
|
|
54
56
|
/>
|
|
55
57
|
</div>
|
|
56
|
-
<div class="flex-none border-t border-
|
|
58
|
+
<div class="flex-none border-t border-main">
|
|
57
59
|
<div class="flex gap-1 items-center px-6 py-3">
|
|
58
60
|
<IconButton :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
|
|
59
61
|
<carbon:minimize v-if="isFullscreen" />
|