@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.
Files changed (78) hide show
  1. package/App.vue +7 -0
  2. package/builtin/Arrow.vue +2 -4
  3. package/builtin/CodeBlockWrapper.vue +14 -6
  4. package/builtin/KaTexBlockWrapper.vue +5 -4
  5. package/builtin/Mermaid.vue +4 -3
  6. package/builtin/Monaco.vue +109 -92
  7. package/builtin/RenderWhen.vue +3 -3
  8. package/builtin/ShikiMagicMove.vue +50 -0
  9. package/builtin/SlideCurrentNo.vue +2 -3
  10. package/builtin/SlidesTotal.vue +3 -4
  11. package/builtin/SlidevVideo.vue +8 -6
  12. package/builtin/Toc.vue +3 -3
  13. package/builtin/TocList.vue +3 -2
  14. package/builtin/Tweet.vue +3 -22
  15. package/builtin/VClick.ts +2 -1
  16. package/builtin/VClickGap.vue +3 -5
  17. package/builtin/VClicks.ts +1 -1
  18. package/composables/useClicks.ts +34 -16
  19. package/constants.ts +58 -8
  20. package/context.ts +73 -0
  21. package/env.ts +3 -12
  22. package/internals/ClicksSlider.vue +93 -0
  23. package/internals/Controls.vue +2 -2
  24. package/internals/DrawingControls.vue +39 -9
  25. package/internals/DrawingLayer.vue +3 -3
  26. package/internals/Goto.vue +5 -4
  27. package/internals/IconButton.vue +7 -3
  28. package/internals/InfoDialog.vue +1 -1
  29. package/internals/Modal.vue +1 -1
  30. package/internals/NavControls.vue +4 -5
  31. package/internals/NoteDisplay.vue +131 -8
  32. package/internals/NoteEditable.vue +128 -0
  33. package/internals/NoteStatic.vue +8 -6
  34. package/internals/PrintContainer.vue +4 -3
  35. package/internals/PrintSlide.vue +8 -2
  36. package/internals/PrintSlideClick.vue +5 -7
  37. package/internals/{SlidesOverview.vue → QuickOverview.vue} +21 -10
  38. package/internals/RecordingControls.vue +1 -1
  39. package/internals/RecordingDialog.vue +5 -6
  40. package/internals/{Editor.vue → SideEditor.vue} +7 -3
  41. package/internals/SlideContainer.vue +12 -9
  42. package/internals/SlideWrapper.ts +28 -12
  43. package/internals/SlidesShow.vue +7 -8
  44. package/layouts/two-cols-header.vue +9 -3
  45. package/logic/drawings.ts +6 -3
  46. package/logic/nav.ts +11 -8
  47. package/logic/note.ts +7 -7
  48. package/main.ts +8 -4
  49. package/modules/context.ts +4 -3
  50. package/modules/mermaid.ts +6 -7
  51. package/modules/{directives.ts → v-click.ts} +15 -15
  52. package/modules/v-mark.ts +159 -0
  53. package/package.json +26 -16
  54. package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
  55. package/{internals/NotesView.vue → pages/notes.vue} +5 -3
  56. package/pages/overview.vue +229 -0
  57. package/{internals/Play.vue → pages/play.vue} +15 -12
  58. package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +12 -7
  59. package/{internals/Presenter.vue → pages/presenter.vue} +108 -100
  60. package/{internals/Print.vue → pages/print.vue} +3 -4
  61. package/routes.ts +27 -51
  62. package/setup/codemirror.ts +8 -3
  63. package/setup/monaco.ts +108 -44
  64. package/setup/root.ts +2 -2
  65. package/shim-vue.d.ts +35 -0
  66. package/shim.d.ts +1 -13
  67. package/state/index.ts +10 -10
  68. package/styles/code.css +7 -3
  69. package/styles/index.css +68 -7
  70. package/styles/katex.css +1 -1
  71. package/styles/layouts-base.css +17 -12
  72. package/styles/monaco.css +27 -0
  73. package/styles/vars.css +1 -0
  74. package/uno.config.ts +14 -2
  75. package/iframes/monaco/index.css +0 -28
  76. package/iframes/monaco/index.html +0 -7
  77. package/iframes/monaco/index.ts +0 -260
  78. package/internals/NoteEditor.vue +0 -88
@@ -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
- @apply h-full;
77
+ height: 100%;
81
78
  }
82
79
 
83
80
  #slideshow > div {
84
- @apply h-full w-full absolute;
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 { grid-area: 1 / 1 / 2 / 3; }
55
- .col-left { grid-area: 2 / 1 / 3 / 2; }
56
- .col-right { grid-area: 2 / 2 / 3 / 3; }
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
- brush.mode = 'line'
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
- brush.mode = v
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 99999
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
- onSwipeStart(e) {
133
- if (e.pointerType !== 'touch')
134
- return
138
+ pointerTypes: ['touch'],
139
+ onSwipeStart() {
135
140
  if (isDrawing.value)
136
141
  return
137
142
  swipeBegin.value = timestamp()
138
143
  },
139
- onSwipeEnd(e) {
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?.filepath)
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, SlideInfoExtended } from '@slidev/types'
5
+ import type { SlideInfo, SlidePatch } from '@slidev/types'
6
6
 
7
7
  export interface UseSlideInfo {
8
- info: Ref<SlideInfoExtended | undefined>
9
- update: (data: Partial<SlideInfo>) => Promise<SlideInfoExtended | void>
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<SlideInfoExtended | undefined>,
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: Partial<SlideInfo>) => {
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.trim() !== payload.note.trim())
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: Partial<SlideInfo>, newId?: number) => {
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 createDirectives from './modules/directives'
7
- import createSlidevContext from './modules/context'
8
+ import { createVClickDirectives } from './modules/v-click'
9
+ import { createVMarkDirective } from './modules/v-mark'
10
+ import { createSlidevContext } from './modules/context'
8
11
 
9
- import '/@slidev/styles'
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(createDirectives())
17
+ app.use(createVClickDirectives())
18
+ app.use(createVMarkDirective())
15
19
  app.use(createSlidevContext())
16
20
 
17
21
  setupMain({ app, router })
@@ -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 default function createSlidevContext() {
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
 
@@ -1,17 +1,16 @@
1
1
  import mermaid from 'mermaid/dist/mermaid.esm.mjs'
2
- import { customAlphabet } from 'nanoid'
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(encoded: string, options: any) {
14
- const key = encoded + JSON.stringify(options)
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 = decode(encoded)
25
- const id = nanoid()
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
- function dirInject<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, defaultValue?: T): T | undefined {
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 default function createDirectives() {
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: HTMLElement, dir) {
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: HTMLElement, dir) {
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: HTMLElement, dir) {
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
- "version": "0.48.0-beta.2",
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/vitepress-twoslash": "^1.1.3",
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.7.2",
29
- "@vueuse/math": "^10.7.2",
30
- "@vueuse/motion": "^2.0.0",
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
- "defu": "^6.1.4",
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.37.1",
42
- "nanoid": "^5.0.5",
50
+ "monaco-editor": "^0.46.0",
43
51
  "prettier": "^3.2.5",
44
52
  "recordrtc": "^5.6.2",
45
- "resolve": "^1.22.8",
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.19",
48
- "vue-router": "^4.2.5",
49
- "@slidev/parser": "0.48.0-beta.2",
50
- "@slidev/types": "0.48.0-beta.2"
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.3"
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 './NoteDisplay.vue'
11
- import IconButton from './IconButton.vue'
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-gray-400 border-opacity-20">
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" />