@slidev/client 0.48.0-beta.12 → 0.48.0-beta.13

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.
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import { ShikiMagicMovePrecompiled } from 'shiki-magic-move/vue'
3
+ import type { KeyedTokensInfo } from 'shiki-magic-move/types'
4
+ import { onMounted, onUnmounted, ref, watchEffect } from 'vue'
5
+ import { useSlideContext } from '../context'
6
+ import { makeId } from '../logic/utils'
7
+
8
+ import 'shiki-magic-move/style.css'
9
+
10
+ const props = defineProps<{
11
+ steps: KeyedTokensInfo[]
12
+ at?: string | number
13
+ }>()
14
+
15
+ const { $clicksContext: clicks, $scale: scale } = useSlideContext()
16
+ const id = makeId()
17
+ const index = ref(0)
18
+
19
+ onUnmounted(() => {
20
+ clicks!.unregister(id)
21
+ })
22
+
23
+ onMounted(() => {
24
+ if (!clicks || clicks.disabled)
25
+ return
26
+
27
+ const { start, end, delta } = clicks.resolve(props.at || '+1', props.steps.length - 1)
28
+ clicks.register(id, { max: end, delta })
29
+
30
+ watchEffect(() => {
31
+ if (clicks.disabled)
32
+ index.value = props.steps.length - 1
33
+ else
34
+ index.value = Math.min(Math.max(0, clicks.current - start + 1), props.steps.length - 1)
35
+ })
36
+ })
37
+ </script>
38
+
39
+ <template>
40
+ <div class="slidev-code-wrapper slidev-code-magic-move">
41
+ <ShikiMagicMovePrecompiled
42
+ class="slidev-code relative shiki"
43
+ :steps="steps"
44
+ :step="index"
45
+ :options="{ globalScale: scale }"
46
+ />
47
+ </div>
48
+ </template>
@@ -1,16 +1,13 @@
1
1
  import { sum } from '@antfu/utils'
2
2
  import type { ClicksContext } from '@slidev/types'
3
3
  import type { Ref } from 'vue'
4
- import { ref, shallowReactive } from 'vue'
4
+ import { computed, ref, shallowReactive } from 'vue'
5
5
  import type { RouteRecordRaw } from 'vue-router'
6
6
  import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
7
7
  import { normalizeAtProp } from '../logic/utils'
8
8
  import { CLICKS_MAX } from '../constants'
9
9
 
10
- /**
11
- * @internal
12
- */
13
- export function useClicksContextBase(getCurrent: () => number, clicksOverrides?: number): ClicksContext {
10
+ function useClicksContextBase(current: Ref<number>, clicksOverrides?: number): ClicksContext {
14
11
  const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
15
12
  const map: ClicksContext['map'] = shallowReactive(new Map())
16
13
 
@@ -19,7 +16,10 @@ export function useClicksContextBase(getCurrent: () => number, clicksOverrides?:
19
16
  return isPrintMode.value && !isPrintWithClicks.value
20
17
  },
21
18
  get current() {
22
- return getCurrent()
19
+ return current.value
20
+ },
21
+ set current(value) {
22
+ current.value = value
23
23
  },
24
24
  relativeOffsets,
25
25
  map,
@@ -67,8 +67,8 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
67
67
  if (route?.meta?.__clicksContext)
68
68
  return route.meta.__clicksContext
69
69
  const thisPath = +(route?.path ?? CLICKS_MAX)
70
- const context = useClicksContextBase(
71
- () => {
70
+ const current = computed({
71
+ get() {
72
72
  const currentPath = +(currentRoute.value?.path ?? CLICKS_MAX)
73
73
  if (currentPath === thisPath)
74
74
  return queryClicks.value
@@ -77,6 +77,14 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
77
77
  else
78
78
  return 0
79
79
  },
80
+ set(v) {
81
+ const currentPath = +(currentRoute.value?.path ?? CLICKS_MAX)
82
+ if (currentPath === thisPath)
83
+ queryClicks.value = v
84
+ },
85
+ })
86
+ const context = useClicksContextBase(
87
+ current,
80
88
  route?.meta?.clicks,
81
89
  )
82
90
  if (route?.meta)
@@ -84,7 +92,6 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
84
92
  return context
85
93
  }
86
94
 
87
- export function useFixedClicks(route?: RouteRecordRaw | undefined, currentInit = 0): [Ref<number>, ClicksContext] {
88
- const current = ref(currentInit)
89
- return [current, useClicksContextBase(() => current.value, route?.meta?.clicks)]
95
+ export function useFixedClicks(route?: RouteRecordRaw | undefined, currentInit = 0): ClicksContext {
96
+ return useClicksContextBase(ref(currentInit), route?.meta?.clicks)
90
97
  }
package/context.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { shallowRef, toRef } from 'vue'
1
+ import { ref, shallowRef, toRef } from 'vue'
2
2
  import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
3
3
  import { useFixedClicks } from './composables/useClicks'
4
4
  import {
@@ -9,10 +9,11 @@ import {
9
9
  injectionFrontmatter,
10
10
  injectionRenderContext,
11
11
  injectionRoute,
12
+ injectionSlideScale,
12
13
  injectionSlidevContext,
13
14
  } from './constants'
14
15
 
15
- const clicksContextFallback = shallowRef(useFixedClicks()[1])
16
+ const clicksContextFallback = shallowRef(useFixedClicks())
16
17
 
17
18
  /**
18
19
  * Get the current slide context, should be called inside the setup function of a component inside slide
@@ -26,6 +27,7 @@ export function useSlideContext() {
26
27
  const $renderContext = injectLocal(injectionRenderContext)!
27
28
  const $frontmatter = injectLocal(injectionFrontmatter, {})
28
29
  const $route = injectLocal(injectionRoute, undefined)
30
+ const $scale = injectLocal(injectionSlideScale, ref(1))!
29
31
 
30
32
  return {
31
33
  $slidev,
@@ -36,6 +38,7 @@ export function useSlideContext() {
36
38
  $route,
37
39
  $renderContext,
38
40
  $frontmatter,
41
+ $scale,
39
42
  }
40
43
  }
41
44
 
@@ -1,10 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
3
- import { injectLocal } from '@vueuse/core'
4
3
  import { drauu, drawingEnabled, loadCanvas } from '../logic/drawings'
5
- import { injectionSlideScale } from '../constants'
4
+ import { useSlideContext } from '../context'
6
5
 
7
- const scale = injectLocal(injectionSlideScale)!
6
+ const scale = useSlideContext().$scale
8
7
  const svg = ref<SVGSVGElement>()
9
8
 
10
9
  onMounted(() => {
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, defineEmits, defineProps, nextTick, onMounted, ref, watch } from 'vue'
2
+ import { computed, nextTick, onMounted, ref, watch } from 'vue'
3
+ import type { ClicksContext } from '@slidev/types'
3
4
  import { CLICKS_MAX } from '../constants'
4
5
 
5
6
  const props = defineProps<{
@@ -7,45 +8,99 @@ const props = defineProps<{
7
8
  noteHtml?: string
8
9
  note?: string
9
10
  placeholder?: string
10
- clicks?: number | string
11
+ clicksContext?: ClicksContext
11
12
  }>()
12
13
 
13
14
  defineEmits(['click'])
14
15
 
15
- const withClicks = computed(() => props.clicks != null && props.noteHtml?.includes('slidev-note-click-mark'))
16
+ const withClicks = computed(() => props.clicksContext?.current != null && props.noteHtml?.includes('slidev-note-click-mark'))
16
17
  const noteDisplay = ref<HTMLElement | null>(null)
17
18
 
19
+ const CLASS_FADE = 'slidev-note-fade'
20
+ const CLASS_MARKER = 'slidev-note-click-mark'
21
+
18
22
  function highlightNote() {
19
- if (!noteDisplay.value || !withClicks.value || props.clicks == null)
23
+ if (!noteDisplay.value || !withClicks.value || props.clicksContext?.current == null)
20
24
  return
21
25
 
22
- const children = Array.from(noteDisplay.value.querySelectorAll('*'))
23
-
24
- const disabled = +props.clicks < 0 || +props.clicks >= CLICKS_MAX
26
+ const current = +props.clicksContext?.current ?? CLICKS_MAX
27
+ const disabled = current < 0 || current >= CLICKS_MAX
25
28
  if (disabled) {
26
- children.forEach(el => el.classList.remove('slidev-note-fade'))
29
+ Array.from(noteDisplay.value.querySelectorAll('*'))
30
+ .forEach(el => el.classList.remove(CLASS_FADE))
27
31
  return
28
32
  }
29
33
 
30
- let count = 0
34
+ const nodeToIgnores = new Set<Element>()
35
+ function ignoreParent(node: Element) {
36
+ if (!node || node === noteDisplay.value)
37
+ return
38
+ nodeToIgnores.add(node)
39
+ if (node.parentElement)
40
+ ignoreParent(node.parentElement)
41
+ }
31
42
 
32
- const groups = new Map<number, Element[]>()
43
+ const markers = Array.from(noteDisplay.value.querySelectorAll(`.${CLASS_MARKER}`)) as HTMLElement[]
44
+ const markersMap = new Map<number, HTMLElement>()
33
45
 
34
- for (const child of children) {
35
- if (!groups.has(count))
36
- groups.set(count, [])
46
+ // Convert all sibling text nodes to spans, so we attach classes to them
47
+ for (const marker of markers) {
48
+ const parent = marker.parentElement!
49
+ const clicks = Number(marker.dataset!.clicks)
50
+ markersMap.set(clicks, marker)
51
+ // Ignore the parents of the marker, so the class only applies to the children
52
+ ignoreParent(parent)
53
+ Array.from(parent!.childNodes)
54
+ .forEach((node) => {
55
+ if (node.nodeType === 3) { // text node
56
+ const span = document.createElement('span')
57
+ span.textContent = node.textContent
58
+ parent.insertBefore(span, node)
59
+ node.remove()
60
+ }
61
+ })
62
+ }
63
+ const children = Array.from(noteDisplay.value.querySelectorAll('*'))
37
64
 
38
- groups.get(count)!.push(child)
39
- if (child.classList.contains('slidev-note-click-mark'))
65
+ let count = 0
66
+
67
+ // Segmenting notes by clicks
68
+ const segments = new Map<number, Element[]>()
69
+ for (const child of children) {
70
+ if (!segments.has(count))
71
+ segments.set(count, [])
72
+ segments.get(count)!.push(child)
73
+ // Update count when reach marker
74
+ if (child.classList.contains(CLASS_MARKER))
40
75
  count = Number((child as HTMLElement).dataset.clicks) || (count + 1)
41
76
  }
42
77
 
43
- for (const [count, els] of groups)
44
- els.forEach(el => el.classList.toggle('slidev-note-fade', +count !== +props.clicks!))
78
+ // Apply
79
+ for (const [count, els] of segments) {
80
+ els.forEach(el => el.classList.toggle(
81
+ CLASS_FADE,
82
+ nodeToIgnores.has(el)
83
+ ? false
84
+ : count !== current,
85
+ ))
86
+ }
87
+
88
+ for (const [clicks, marker] of markersMap) {
89
+ marker.classList.remove(CLASS_FADE)
90
+ marker.classList.toggle(`${CLASS_MARKER}-past`, clicks < current)
91
+ marker.classList.toggle(`${CLASS_MARKER}-active`, clicks === current)
92
+ marker.classList.toggle(`${CLASS_MARKER}-next`, clicks === current + 1)
93
+ marker.classList.toggle(`${CLASS_MARKER}-future`, clicks > current + 1)
94
+ marker.addEventListener('dblclick', (e) => {
95
+ props.clicksContext!.current = clicks
96
+ e.stopPropagation()
97
+ e.stopImmediatePropagation()
98
+ })
99
+ }
45
100
  }
46
101
 
47
102
  watch(
48
- () => [props.noteHtml, props.clicks],
103
+ () => [props.noteHtml, props.clicksContext?.current],
49
104
  () => {
50
105
  nextTick(() => {
51
106
  highlightNote()
@@ -1,6 +1,8 @@
1
1
  <script setup lang="ts">
2
+ import type { PropType } from 'vue'
2
3
  import { nextTick, ref, watch, watchEffect } from 'vue'
3
4
  import { ignorableWatch, onClickOutside, useVModel } from '@vueuse/core'
5
+ import type { ClicksContext } from '@slidev/types'
4
6
  import { useDynamicSlideInfo } from '../logic/note'
5
7
  import NoteDisplay from './NoteDisplay.vue'
6
8
 
@@ -20,8 +22,8 @@ const props = defineProps({
20
22
  placeholder: {
21
23
  default: 'No notes for this slide',
22
24
  },
23
- clicks: {
24
- type: [Number, String],
25
+ clicksContext: {
26
+ type: Object as PropType<ClicksContext>,
25
27
  },
26
28
  autoHeight: {
27
29
  default: false,
@@ -103,7 +105,7 @@ watch(
103
105
  :style="props.style"
104
106
  :note="note || placeholder"
105
107
  :note-html="info?.noteHTML"
106
- :clicks="props.clicks"
108
+ :clicks-context="clicksContext"
107
109
  />
108
110
  <textarea
109
111
  v-else
@@ -1,11 +1,12 @@
1
1
  <script setup lang="ts">
2
+ import type { ClicksContext } from 'packages/types'
2
3
  import { useSlideInfo } from '../logic/note'
3
4
  import NoteDisplay from './NoteDisplay.vue'
4
5
 
5
6
  const props = defineProps<{
6
7
  no?: number
7
8
  class?: string
8
- clicks?: number | string
9
+ clicksContext?: ClicksContext
9
10
  }>()
10
11
 
11
12
  const { info } = useSlideInfo(props.no)
@@ -16,6 +17,6 @@ const { info } = useSlideInfo(props.no)
16
17
  :class="props.class"
17
18
  :note="info?.note"
18
19
  :note-html="info?.noteHTML"
19
- :clicks="props.clicks"
20
+ :clicks-context="clicksContext"
20
21
  />
21
22
  </template>
@@ -1,21 +1,20 @@
1
1
  <script setup lang="ts">
2
2
  import type { ClicksContext } from '@slidev/types'
3
- import type { Ref } from 'vue'
4
3
  import { computed } from 'vue'
5
4
  import { CLICKS_MAX } from '../constants'
6
5
 
7
6
  const props = defineProps<{
8
- clickContext: [Ref<number>, ClicksContext]
7
+ clicksContext: ClicksContext
9
8
  }>()
10
9
 
11
- const total = computed(() => props.clickContext[1].total)
10
+ const total = computed(() => props.clicksContext.total)
12
11
  const current = computed({
13
12
  get() {
14
- return props.clickContext[0].value > total.value ? -1 : props.clickContext[0].value
13
+ return props.clicksContext.current > total.value ? -1 : props.clicksContext.current
15
14
  },
16
15
  set(value: number) {
17
16
  // eslint-disable-next-line vue/no-mutating-props
18
- props.clickContext[0].value = value
17
+ props.clicksContext.current = value
19
18
  },
20
19
  })
21
20
 
@@ -9,7 +9,7 @@ const props = defineProps<{ route: RouteRecordRaw }>()
9
9
 
10
10
  const route = computed(() => props.route)
11
11
  const nav = useNav(route)
12
- const clicks0 = useFixedClicks(route.value, 0)[1]
12
+ const clicks0 = useFixedClicks(route.value, 0)
13
13
  </script>
14
14
 
15
15
  <template>
@@ -19,6 +19,12 @@ const clicks0 = useFixedClicks(route.value, 0)[1]
19
19
  :route="route"
20
20
  />
21
21
  <template v-if="!clicks0.disabled">
22
- <PrintSlideClick v-for="i of clicks0.total" :key="i" :clicks-context="useFixedClicks(route, i)[1]" :nav="nav" :route="route" />
22
+ <PrintSlideClick
23
+ v-for="i of clicks0.total"
24
+ :key="i"
25
+ :clicks-context="useFixedClicks(route, i)"
26
+ :nav="nav"
27
+ :route="route"
28
+ />
23
29
  </template>
24
30
  </template>
@@ -140,7 +140,7 @@ watchEffect(() => {
140
140
  <SlideWrapper
141
141
  :is="route.component"
142
142
  v-if="route?.component"
143
- :clicks-context="useFixedClicks(route, CLICKS_MAX)[1]"
143
+ :clicks-context="useFixedClicks(route, CLICKS_MAX)"
144
144
  :class="getSlideClass(route)"
145
145
  :route="route"
146
146
  render-context="overview"
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.12",
4
+ "version": "0.48.0-beta.13",
5
5
  "description": "Presentation slides for developers",
6
6
  "author": "antfu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -51,11 +51,12 @@
51
51
  "prettier": "^3.2.5",
52
52
  "recordrtc": "^5.6.2",
53
53
  "resolve": "^1.22.8",
54
+ "shiki-magic-move": "^0.1.0",
54
55
  "unocss": "^0.58.5",
55
- "vue": "^3.4.19",
56
+ "vue": "^3.4.20",
56
57
  "vue-router": "^4.3.0",
57
- "@slidev/parser": "0.48.0-beta.12",
58
- "@slidev/types": "0.48.0-beta.12"
58
+ "@slidev/types": "0.48.0-beta.13",
59
+ "@slidev/parser": "0.48.0-beta.13"
59
60
  },
60
61
  "devDependencies": {
61
62
  "vite": "^5.1.4"
package/pages/notes.vue CHANGED
@@ -51,6 +51,7 @@ 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"
54
55
  />
55
56
  </div>
56
57
  <div class="flex-none border-t border-main">
@@ -1,11 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { Ref } from 'vue'
3
2
  import { computed, nextTick, onMounted, reactive, ref } from 'vue'
4
3
  import { useHead } from '@unhead/vue'
5
4
  import type { RouteRecordRaw } from 'vue-router'
6
5
  import type { ClicksContext } from 'packages/types'
7
6
  import { themeVars } from '../env'
8
- import { rawRoutes } from '../logic/nav'
7
+ import { openInEditor, rawRoutes } from '../logic/nav'
9
8
  import { useFixedClicks } from '../composables/useClicks'
10
9
  import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
11
10
  import { getSlideClass } from '../utils'
@@ -30,8 +29,8 @@ const wordCounts = computed(() => rawRoutes.map(route => wordCount(route.meta?.s
30
29
  const totalWords = computed(() => wordCounts.value.reduce((a, b) => a + b, 0))
31
30
  const totalClicks = computed(() => rawRoutes.map(route => getSlideClicks(route)).reduce((a, b) => a + b, 0))
32
31
 
33
- const clicksContextMap = new WeakMap<RouteRecordRaw, [Ref<number>, ClicksContext]>()
34
- function getClickContext(route: RouteRecordRaw) {
32
+ const clicksContextMap = new WeakMap<RouteRecordRaw, ClicksContext>()
33
+ function getClicksContext(route: RouteRecordRaw) {
35
34
  // We create a local clicks context to calculate the total clicks of the slide
36
35
  if (!clicksContextMap.has(route))
37
36
  clicksContextMap.set(route, useFixedClicks(route, CLICKS_MAX))
@@ -39,7 +38,7 @@ function getClickContext(route: RouteRecordRaw) {
39
38
  }
40
39
 
41
40
  function getSlideClicks(route: RouteRecordRaw) {
42
- return route.meta?.clicks || getClickContext(route)?.[1]?.total
41
+ return route.meta?.clicks || getClicksContext(route)?.total
43
42
  }
44
43
 
45
44
  function wordCount(str: string) {
@@ -132,10 +131,25 @@ onMounted(() => {
132
131
  :ref="el => blocks.set(idx, el as any)"
133
132
  class="relative border-t border-main of-hidden flex gap-4 min-h-50 group"
134
133
  >
135
- <div class="select-none w-13 text-right my4">
134
+ <div class="select-none w-13 text-right my4 flex flex-col gap-1 items-end">
136
135
  <div class="text-3xl op20 mb2">
137
136
  {{ idx + 1 }}
138
137
  </div>
138
+ <IconButton
139
+ class="mr--3 op0 group-hover:op80"
140
+ title="Play in new tab"
141
+ @click="openSlideInNewTab(route.path)"
142
+ >
143
+ <carbon:presentation-file />
144
+ </IconButton>
145
+ <IconButton
146
+ v-if="route.meta?.slide"
147
+ class="mr--3 op0 group-hover:op80"
148
+ title="Open in editor"
149
+ @click="openInEditor(`${route.meta.slide.filepath}:${route.meta.slide.start}`)"
150
+ >
151
+ <carbon:cics-program />
152
+ </IconButton>
139
153
  </div>
140
154
  <div class="flex flex-col gap-2 my5">
141
155
  <div
@@ -152,7 +166,7 @@ onMounted(() => {
152
166
  <SlideWrapper
153
167
  :is="route.component"
154
168
  v-if="route?.component"
155
- :clicks-context="getClickContext(route)[1]"
169
+ :clicks-context="getClicksContext(route)"
156
170
  :class="getSlideClass(route)"
157
171
  :route="route"
158
172
  render-context="overview"
@@ -163,7 +177,7 @@ onMounted(() => {
163
177
  <OverviewClicksSlider
164
178
  v-if="getSlideClicks(route)"
165
179
  mt-2
166
- :click-context="getClickContext(route)"
180
+ :clicks-context="getClicksContext(route)"
167
181
  class="w-full"
168
182
  />
169
183
  </div>
@@ -182,7 +196,7 @@ onMounted(() => {
182
196
  class="max-w-250 w-250 text-lg rounded p3"
183
197
  :auto-height="true"
184
198
  :editing="edittingNote === idx"
185
- :clicks="getClickContext(route)[0].value"
199
+ :clicks-context="getClicksContext(route)"
186
200
  @dblclick="edittingNote !== idx ? edittingNote = idx : null"
187
201
  @update:editing="edittingNote = null"
188
202
  />
package/pages/play.vue CHANGED
@@ -31,9 +31,9 @@ useSwipeControls(root)
31
31
 
32
32
  const persistNav = computed(() => isScreenVertical.value || showEditor.value)
33
33
 
34
- const Editor = shallowRef<any>()
34
+ const SideEditor = shallowRef<any>()
35
35
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
36
- import('../internals/Editor.vue').then(v => Editor.value = v.default)
36
+ import('../internals/SideEditor.vue').then(v => SideEditor.value = v.default)
37
37
 
38
38
  const DrawingControls = shallowRef<any>()
39
39
  if (__SLIDEV_FEATURE_DRAWINGS__)
@@ -70,8 +70,8 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
70
70
  </template>
71
71
  </SlideContainer>
72
72
 
73
- <template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && Editor && showEditor">
74
- <Editor :resize="true" />
73
+ <template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && SideEditor && showEditor">
74
+ <SideEditor :resize="true" />
75
75
  </template>
76
76
  </div>
77
77
  <Controls />
@@ -49,12 +49,12 @@ const nextFrameClicksCtx = computed(() => {
49
49
  return nextFrame.value && clicksCtxMap[+nextFrame.value[0].path - 1]
50
50
  })
51
51
  watch([currentRoute, queryClicks], () => {
52
- nextFrameClicksCtx.value && (nextFrameClicksCtx.value[0].value = nextFrame.value![1])
52
+ nextFrameClicksCtx.value && (nextFrameClicksCtx.value.current = nextFrame.value![1])
53
53
  }, { immediate: true })
54
54
 
55
- const Editor = shallowRef<any>()
55
+ const SideEditor = shallowRef<any>()
56
56
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
57
- import('../internals/Editor.vue').then(v => Editor.value = v.default)
57
+ import('../internals/SideEditor.vue').then(v => SideEditor.value = v.default)
58
58
 
59
59
  // sync presenter cursor
60
60
  onMounted(() => {
@@ -121,9 +121,9 @@ onMounted(() => {
121
121
  class="h-full w-full"
122
122
  >
123
123
  <SlideWrapper
124
- :is="nextFrame[0].component as any"
124
+ :is="(nextFrame[0].component as any)"
125
125
  :key="nextFrame[0].path"
126
- :clicks-context="nextFrameClicksCtx[1]"
126
+ :clicks-context="nextFrameClicksCtx"
127
127
  :class="getSlideClass(nextFrame[0])"
128
128
  :route="nextFrame[0]"
129
129
  render-context="previewNext"
@@ -134,8 +134,8 @@ onMounted(() => {
134
134
  </div>
135
135
  </div>
136
136
  <!-- Notes -->
137
- <div v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && Editor && showEditor" class="grid-section note of-auto">
138
- <Editor />
137
+ <div v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && SideEditor && showEditor" class="grid-section note of-auto">
138
+ <SideEditor />
139
139
  </div>
140
140
  <div v-else class="grid-section note grid grid-rows-[1fr_min-content] overflow-hidden">
141
141
  <NoteEditor
@@ -144,7 +144,7 @@ onMounted(() => {
144
144
  :no="currentSlideId"
145
145
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
146
146
  :editing="notesEditing"
147
- :clicks="clicksContext.current"
147
+ :clicks-context="clicksContext"
148
148
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
149
149
  />
150
150
  <NoteStatic
@@ -153,7 +153,7 @@ onMounted(() => {
153
153
  :no="currentSlideId"
154
154
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
155
155
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
156
- :clicks="clicksContext.current"
156
+ :clicks-context="clicksContext"
157
157
  />
158
158
  <div class="border-t border-main py-1 px-2 text-sm">
159
159
  <IconButton title="Increase font size" @click="increasePresenterFontSize">
package/state/index.ts CHANGED
@@ -5,6 +5,7 @@ import { slideAspect } from '../env'
5
5
  export const showRecordingDialog = ref(false)
6
6
  export const showInfoDialog = ref(false)
7
7
  export const showGotoDialog = ref(false)
8
+ export const showOverview = ref(false)
8
9
 
9
10
  export const shortcutsEnabled = ref(true)
10
11
  export const breakpoints = useBreakpoints({
@@ -24,7 +25,6 @@ export const currentCamera = useLocalStorage<string>('slidev-camera', 'default',
24
25
  export const currentMic = useLocalStorage<string>('slidev-mic', 'default', { listenToStorageChanges: false })
25
26
  export const slideScale = useLocalStorage<number>('slidev-scale', 0)
26
27
 
27
- export const showOverview = useLocalStorage('slidev-show-overview', false, { listenToStorageChanges: false })
28
28
  export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true, { listenToStorageChanges: false })
29
29
  export const showEditor = useLocalStorage('slidev-show-editor', false, { listenToStorageChanges: false })
30
30
  export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false, { listenToStorageChanges: false })
package/styles/index.css CHANGED
@@ -16,7 +16,11 @@ html {
16
16
  }
17
17
 
18
18
  .slidev-icon-btn {
19
- @apply inline-block cursor-pointer select-none !outline-none;
19
+ aspect-ratio: 1;
20
+ display: inline-block;
21
+ user-select: none;
22
+ outline: none;
23
+ cursor: pointer;
20
24
  @apply opacity-75 transition duration-200 ease-in-out align-middle rounded p-1;
21
25
  @apply hover:(opacity-100 bg-gray-400 bg-opacity-10);
22
26
  @apply md:p-2;
@@ -70,9 +74,21 @@ html {
70
74
  }
71
75
 
72
76
  .slidev-note-click-mark {
73
- font-size: 0.8em;
74
- --uno: text-violet bg-violet/10 mx1 px1 font-mono rounded flex flex-inline
75
- items-center align-middle;
77
+ user-select: none;
78
+ font-size: 0.7em;
79
+ display: inline-flex;
80
+ --uno: text-violet bg-violet/10 px1 font-mono rounded items-center border
81
+ border-transparent;
82
+ }
83
+ .slidev-note-click-mark.slidev-note-click-mark-active {
84
+ --uno: border border-violet;
85
+ }
86
+ .slidev-note-click-mark.slidev-note-click-mark-past {
87
+ filter: saturate(0);
88
+ opacity: 0.5;
89
+ }
90
+ .slidev-note-click-mark.slidev-note-click-mark-future {
91
+ opacity: 0.5;
76
92
  }
77
93
 
78
94
  .slidev-note-click-mark::before {
File without changes