@slidev/client 0.48.0-beta.1 → 0.48.0-beta.10

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 (52) hide show
  1. package/builtin/CodeBlockWrapper.vue +4 -3
  2. package/builtin/KaTexBlockWrapper.vue +4 -3
  3. package/builtin/RenderWhen.vue +3 -3
  4. package/builtin/SlideCurrentNo.vue +2 -3
  5. package/builtin/SlidesTotal.vue +3 -4
  6. package/builtin/SlidevVideo.vue +8 -6
  7. package/builtin/Toc.vue +3 -3
  8. package/builtin/Tweet.vue +4 -15
  9. package/builtin/VClickGap.vue +3 -5
  10. package/builtin/VClicks.ts +1 -1
  11. package/composables/useClicks.ts +16 -13
  12. package/composables/useTweetScript.ts +17 -0
  13. package/constants.ts +56 -8
  14. package/context.ts +70 -0
  15. package/internals/DrawingControls.vue +41 -9
  16. package/internals/DrawingLayer.vue +3 -2
  17. package/internals/Editor.vue +7 -3
  18. package/internals/IconButton.vue +4 -3
  19. package/internals/InfoDialog.vue +1 -1
  20. package/internals/Modal.vue +1 -1
  21. package/internals/NavControls.vue +1 -1
  22. package/internals/NoteDisplay.vue +1 -1
  23. package/internals/NoteEditor.vue +10 -6
  24. package/internals/NoteStatic.vue +5 -6
  25. package/internals/PrintContainer.vue +3 -2
  26. package/internals/PrintSlideClick.vue +3 -2
  27. package/internals/RecordingDialog.vue +2 -3
  28. package/internals/SlideContainer.vue +7 -6
  29. package/internals/SlideWrapper.ts +28 -12
  30. package/internals/SlidesOverview.vue +18 -7
  31. package/logic/drawings.ts +6 -3
  32. package/logic/nav.ts +1 -1
  33. package/logic/note.ts +7 -7
  34. package/main.ts +5 -3
  35. package/modules/context.ts +4 -3
  36. package/modules/{directives.ts → v-click.ts} +15 -15
  37. package/modules/v-mark.ts +159 -0
  38. package/package.json +21 -13
  39. package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
  40. package/{internals/NotesView.vue → pages/notes.vue} +3 -3
  41. package/pages/overview.vue +157 -0
  42. package/{internals/Play.vue → pages/play.vue} +7 -7
  43. package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +7 -5
  44. package/{internals/Presenter.vue → pages/presenter.vue} +16 -12
  45. package/{internals/Print.vue → pages/print.vue} +2 -2
  46. package/routes.ts +25 -19
  47. package/setup/codemirror.ts +7 -0
  48. package/state/index.ts +10 -10
  49. package/styles/index.css +5 -0
  50. package/styles/layouts-base.css +6 -4
  51. package/styles/vars.css +1 -0
  52. package/uno.config.ts +6 -2
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@slidev/client",
3
- "version": "0.48.0-beta.1",
3
+ "type": "module",
4
+ "version": "0.48.0-beta.10",
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,35 @@
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.2",
32
+ "@shikijs/vitepress-twoslash": "^1.1.7",
33
+ "@slidev/rough-notation": "^0.1.0",
26
34
  "@unhead/vue": "^1.8.10",
27
35
  "@unocss/reset": "^0.58.5",
28
- "@vueuse/core": "^10.7.2",
29
- "@vueuse/math": "^10.7.2",
30
- "@vueuse/motion": "^2.0.0",
31
- "codemirror": "^5.65.5",
36
+ "@vueuse/core": "^10.8.0",
37
+ "@vueuse/math": "^10.8.0",
38
+ "@vueuse/motion": "^2.1.0",
39
+ "codemirror": "^5.65.16",
32
40
  "defu": "^6.1.4",
33
- "drauu": "^0.3.7",
41
+ "drauu": "^0.4.0",
34
42
  "file-saver": "^2.0.5",
35
43
  "floating-vue": "^5.2.2",
36
44
  "fuse.js": "^7.0.0",
37
- "js-base64": "^3.7.6",
45
+ "js-base64": "^3.7.7",
38
46
  "js-yaml": "^4.1.0",
39
47
  "katex": "^0.16.9",
40
48
  "mermaid": "^10.8.0",
41
49
  "monaco-editor": "^0.37.1",
42
- "nanoid": "^5.0.5",
50
+ "nanoid": "^5.0.6",
43
51
  "prettier": "^3.2.5",
44
52
  "recordrtc": "^5.6.2",
45
53
  "resolve": "^1.22.8",
46
54
  "unocss": "^0.58.5",
47
55
  "vue": "^3.4.19",
48
- "vue-router": "^4.2.5",
49
- "@slidev/types": "0.48.0-beta.1",
50
- "@slidev/parser": "0.48.0-beta.1"
56
+ "vue-router": "^4.3.0",
57
+ "@slidev/types": "0.48.0-beta.10",
58
+ "@slidev/parser": "0.48.0-beta.10"
51
59
  },
52
60
  "devDependencies": {
53
- "vite": "^5.1.1"
61
+ "vite": "^5.1.4"
54
62
  }
55
63
  }
@@ -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({
@@ -53,7 +53,7 @@ function decreaseFontSize() {
53
53
  :placeholder="`No notes for Slide ${pageNo}.`"
54
54
  />
55
55
  </div>
56
- <div class="flex-none border-t border-gray-400 border-opacity-20">
56
+ <div class="flex-none border-t border-main">
57
57
  <div class="flex gap-1 items-center px-6 py-3">
58
58
  <IconButton :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
59
59
  <carbon:minimize v-if="isFullscreen" />
@@ -0,0 +1,157 @@
1
+ <script setup lang="ts">
2
+ import { nextTick, onMounted, reactive, ref } from 'vue'
3
+ import { useHead } from '@unhead/vue'
4
+ import { themeVars } from '../env'
5
+ import { rawRoutes } from '../logic/nav'
6
+ import { useFixedClicks } from '../composables/useClicks'
7
+ import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
8
+ import { getSlideClass } from '../utils'
9
+ import SlideContainer from '../internals/SlideContainer.vue'
10
+ import SlideWrapper from '../internals/SlideWrapper'
11
+ import DrawingPreview from '../internals/DrawingPreview.vue'
12
+ import IconButton from '../internals/IconButton.vue'
13
+ import NoteEditor from '../internals/NoteEditor.vue'
14
+
15
+ const cardWidth = 450
16
+
17
+ useHead({
18
+ title: 'List Overview',
19
+ })
20
+
21
+ const blocks: Map<number, HTMLElement> = reactive(new Map())
22
+ const activeBlocks = ref<number[]>([])
23
+ const edittingNote = ref<number | null>(null)
24
+
25
+ function isElementInViewport(el: HTMLElement) {
26
+ const rect = el.getBoundingClientRect()
27
+ const delta = 20
28
+ return (
29
+ rect.top >= 0 - delta
30
+ && rect.left >= 0 - delta
31
+ && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + delta
32
+ && rect.right <= (window.innerWidth || document.documentElement.clientWidth) + delta
33
+ )
34
+ }
35
+
36
+ function checkActiveBlocks() {
37
+ const active: number[] = []
38
+ Array.from(blocks.entries())
39
+ .forEach(([idx, el]) => {
40
+ if (isElementInViewport(el))
41
+ active.push(idx)
42
+ })
43
+ activeBlocks.value = active
44
+ }
45
+
46
+ function openSlideInNewTab(path: string) {
47
+ const a = document.createElement('a')
48
+ a.target = '_blank'
49
+ a.href = path
50
+ a.click()
51
+ }
52
+
53
+ function scrollToSlide(idx: number) {
54
+ const el = blocks.get(idx)
55
+ if (el)
56
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' })
57
+ }
58
+
59
+ onMounted(() => {
60
+ nextTick(() => {
61
+ checkActiveBlocks()
62
+ })
63
+ })
64
+ </script>
65
+
66
+ <template>
67
+ <div class="h-screen w-screen of-hidden flex">
68
+ <nav class="h-full flex flex-col border-r border-main p2 select-none">
69
+ <div class="flex flex-col flex-auto items-center justify-center group gap-1">
70
+ <div
71
+ v-for="(route, idx) of rawRoutes"
72
+ :key="route.path"
73
+ class="relative"
74
+ >
75
+ <button
76
+ class="relative transition duration-300 w-8 h-8 rounded hover:bg-active hover:op100"
77
+ :class="activeBlocks.includes(idx) ? 'op100 text-primary bg-gray:5' : 'op20'"
78
+ @click="scrollToSlide(idx)"
79
+ >
80
+ <div>{{ idx + 1 }}</div>
81
+ </button>
82
+ <div
83
+ v-if="route.meta?.slide?.title"
84
+ class="pointer-events-none select-none absolute left-110% bg-main top-50% translate-y--50% ws-nowrap z-10 px2 shadow-xl rounded border border-main transition duration-400 op0 group-hover:op100"
85
+ :class="activeBlocks.includes(idx) ? 'text-primary' : 'text-main important-text-op-50'"
86
+ >
87
+ {{ route.meta?.slide?.title }}
88
+ </div>
89
+ </div>
90
+ </div>
91
+ <IconButton
92
+ v-if="!isColorSchemaConfigured"
93
+ :title="isDark ? 'Switch to light mode theme' : 'Switch to dark mode theme'"
94
+ @click="toggleDark()"
95
+ >
96
+ <carbon-moon v-if="isDark" />
97
+ <carbon-sun v-else />
98
+ </IconButton>
99
+ </nav>
100
+ <main
101
+ class="flex-1 h-full of-auto"
102
+ :style="`grid-template-columns: repeat(auto-fit,minmax(${cardWidth}px,1fr))`"
103
+ @scroll="checkActiveBlocks"
104
+ >
105
+ <div
106
+ v-for="(route, idx) of rawRoutes"
107
+ :key="route.path"
108
+ :ref="el => blocks.set(idx, el as any)"
109
+ class="relative border-t border-main of-hidden flex gap-4 min-h-50 group"
110
+ >
111
+ <div class="select-none w-13 text-right my4">
112
+ <div class="text-3xl op20">
113
+ {{ idx + 1 }}
114
+ </div>
115
+ </div>
116
+ <div
117
+ class="border rounded border-main overflow-hidden bg-main my5 select-none h-max"
118
+ :style="themeVars"
119
+ @dblclick="openSlideInNewTab(route.path)"
120
+ >
121
+ <SlideContainer
122
+ :key="route.path"
123
+ :width="cardWidth"
124
+ :clicks-disabled="true"
125
+ class="pointer-events-none important:[&_*]:select-none"
126
+ >
127
+ <SlideWrapper
128
+ :is="route.component"
129
+ v-if="route?.component"
130
+ :clicks-context="useFixedClicks(route, 99999)[1]"
131
+ :class="getSlideClass(route)"
132
+ :route="route"
133
+ render-context="overview"
134
+ />
135
+ <DrawingPreview :page="+route.path" />
136
+ </SlideContainer>
137
+ </div>
138
+ <div class="py3 mt-0.5 mr--8 ml--4 op0 transition group-hover:op100">
139
+ <IconButton
140
+ title="Edit Note"
141
+ class="rounded-full w-9 h-9 text-sm"
142
+ :class="edittingNote === idx ? 'important:op0' : ''"
143
+ @click="edittingNote = idx"
144
+ >
145
+ <carbon:pen />
146
+ </IconButton>
147
+ </div>
148
+ <NoteEditor
149
+ :no="idx"
150
+ class="max-w-250 w-250 text-lg rounded p3"
151
+ :editing="edittingNote === idx"
152
+ @update:editing="edittingNote = null"
153
+ />
154
+ </div>
155
+ </main>
156
+ </div>
157
+ </template>
@@ -5,11 +5,11 @@ import { isEmbedded, isPrintMode, next, prev, useSwipeControls } from '../logic/
5
5
  import { isDrawing } from '../logic/drawings'
6
6
  import { registerShortcuts } from '../logic/shortcuts'
7
7
  import { configs, themeVars } from '../env'
8
- import Controls from './Controls.vue'
9
- import SlideContainer from './SlideContainer.vue'
10
- import NavControls from './NavControls.vue'
11
- import SlidesShow from './SlidesShow.vue'
12
- import PrintStyle from './PrintStyle.vue'
8
+ import Controls from '../internals/Controls.vue'
9
+ import SlideContainer from '../internals/SlideContainer.vue'
10
+ import NavControls from '../internals/NavControls.vue'
11
+ import SlidesShow from '../internals/SlidesShow.vue'
12
+ import PrintStyle from '../internals/PrintStyle.vue'
13
13
 
14
14
  registerShortcuts()
15
15
 
@@ -33,11 +33,11 @@ const persistNav = computed(() => isScreenVertical.value || showEditor.value)
33
33
 
34
34
  const Editor = shallowRef<any>()
35
35
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
36
- import('./Editor.vue').then(v => Editor.value = v.default)
36
+ import('../internals/Editor.vue').then(v => Editor.value = v.default)
37
37
 
38
38
  const DrawingControls = shallowRef<any>()
39
39
  if (__SLIDEV_FEATURE_DRAWINGS__)
40
- import('./DrawingControls.vue').then(v => DrawingControls.value = v.default)
40
+ import('../internals/DrawingControls.vue').then(v => DrawingControls.value = v.default)
41
41
  </script>
42
42
 
43
43
  <template>
@@ -2,9 +2,9 @@
2
2
  import { computed } from 'vue'
3
3
  import { useStyleTag } from '@vueuse/core'
4
4
  import { useHead } from '@unhead/vue'
5
- import { configs, themeVars } from '../env'
6
- import { rawRoutes, total } from '../logic/nav'
7
- import NoteDisplay from './NoteDisplay.vue'
5
+ import { configs, themeVars } from '../../env'
6
+ import { rawRoutes, total } from '../../logic/nav'
7
+ import NoteDisplay from '../../internals/NoteDisplay.vue'
8
8
 
9
9
  useStyleTag(`
10
10
  @page {
@@ -24,7 +24,9 @@ html #page-root {
24
24
  }
25
25
  `)
26
26
 
27
- useHead({ title: `Notes - ${configs.title}` })
27
+ useHead({
28
+ title: `Notes - ${configs.title}`,
29
+ })
28
30
 
29
31
  const slidesWithNote = computed(() => rawRoutes
30
32
  .map(route => route.meta?.slide)
@@ -56,7 +58,7 @@ const slidesWithNote = computed(() => rawRoutes
56
58
  </h2>
57
59
  <NoteDisplay :note-html="slide!.noteHTML" class="max-w-full" />
58
60
  </div>
59
- <hr v-if="index < slidesWithNote.length - 1" class="border-gray-400/50 mb-8">
61
+ <hr v-if="index < slidesWithNote.length - 1" class="border-main mb-8">
60
62
  </div>
61
63
  </div>
62
64
  </div>
@@ -2,7 +2,7 @@
2
2
  import { useHead } from '@unhead/vue'
3
3
  import { computed, onMounted, reactive, ref, shallowRef, watch } from 'vue'
4
4
  import { useMouse, useWindowFocus } from '@vueuse/core'
5
- import { clicksContext, currentPage, currentRoute, hasNext, nextRoute, queryClicks, rawRoutes, total, useSwipeControls } from '../logic/nav'
5
+ import { clicksContext, currentPage, currentRoute, currentSlideId, hasNext, nextRoute, queryClicks, rawRoutes, total, useSwipeControls } from '../logic/nav'
6
6
  import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showOverview, showPresenterCursor } from '../state'
7
7
  import { configs, themeVars } from '../env'
8
8
  import { sharedState } from '../state/shared'
@@ -11,16 +11,16 @@ import { getSlideClass } from '../utils'
11
11
  import { useTimer } from '../logic/utils'
12
12
  import { isDrawing } from '../logic/drawings'
13
13
  import { useFixedClicks } from '../composables/useClicks'
14
- import SlideContainer from './SlideContainer.vue'
15
- import NavControls from './NavControls.vue'
16
- import SlidesOverview from './SlidesOverview.vue'
17
- import NoteEditor from './NoteEditor.vue'
18
- import NoteStatic from './NoteStatic.vue'
19
- import Goto from './Goto.vue'
20
- import SlidesShow from './SlidesShow.vue'
21
- import SlideWrapper from './SlideWrapper'
22
- import DrawingControls from './DrawingControls.vue'
23
- import IconButton from './IconButton.vue'
14
+ import SlideWrapper from '../internals/SlideWrapper'
15
+ import SlideContainer from '../internals/SlideContainer.vue'
16
+ import NavControls from '../internals/NavControls.vue'
17
+ import SlidesOverview from '../internals/SlidesOverview.vue'
18
+ import NoteEditor from '../internals/NoteEditor.vue'
19
+ import NoteStatic from '../internals/NoteStatic.vue'
20
+ import Goto from '../internals/Goto.vue'
21
+ import SlidesShow from '../internals/SlidesShow.vue'
22
+ import DrawingControls from '../internals/DrawingControls.vue'
23
+ import IconButton from '../internals/IconButton.vue'
24
24
 
25
25
  const main = ref<HTMLDivElement>()
26
26
 
@@ -54,7 +54,7 @@ watch([currentRoute, queryClicks], () => {
54
54
 
55
55
  const Editor = shallowRef<any>()
56
56
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
57
- import('./Editor.vue').then(v => Editor.value = v.default)
57
+ import('../internals/Editor.vue').then(v => Editor.value = v.default)
58
58
 
59
59
  // sync presenter cursor
60
60
  onMounted(() => {
@@ -140,12 +140,16 @@ onMounted(() => {
140
140
  <div v-else class="grid-section note grid grid-rows-[1fr_min-content] overflow-hidden">
141
141
  <NoteEditor
142
142
  v-if="__DEV__"
143
+ :key="`edit-${currentSlideId}`"
144
+ :no="currentSlideId"
143
145
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
144
146
  :editing="notesEditing"
145
147
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
146
148
  />
147
149
  <NoteStatic
148
150
  v-else
151
+ :key="`static-${currentSlideId}`"
152
+ :no="currentSlideId"
149
153
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
150
154
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
151
155
  />
@@ -3,8 +3,8 @@ import { watchEffect } from 'vue'
3
3
  import { windowSize } from '../state'
4
4
  import { isPrintMode } from '../logic/nav'
5
5
  import { themeVars } from '../env'
6
- import PrintContainer from './PrintContainer.vue'
7
- import PrintStyle from './PrintStyle.vue'
6
+ import PrintContainer from '../internals/PrintContainer.vue'
7
+ import PrintStyle from '../internals/PrintStyle.vue'
8
8
 
9
9
  watchEffect(() => {
10
10
  if (isPrintMode)
package/routes.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
2
2
  import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
3
3
  import type { TransitionGroupProps } from 'vue'
4
- import type { ClicksContext } from '@slidev/types'
5
- import Play from './internals/Play.vue'
6
- import Print from './internals/Print.vue'
4
+ import type { ClicksContext, SlideInfo } from '@slidev/types'
7
5
 
8
6
  // @ts-expect-error missing types
9
7
  import _rawRoutes, { redirects } from '/@slidev/routes'
@@ -17,13 +15,19 @@ export const routes: RouteRecordRaw[] = [
17
15
  {
18
16
  name: 'play',
19
17
  path: '/',
20
- component: Play,
18
+ component: () => import('./pages/play.vue'),
21
19
  children: [
22
20
  ...rawRoutes,
23
21
  ...redirects,
24
22
  ],
25
23
  },
26
- { name: 'print', path: '/print', component: Print },
24
+ {
25
+ name: 'print',
26
+ path: '/print',
27
+ component: () => import('./pages/print.vue'),
28
+ },
29
+
30
+ // Redirects
27
31
  { path: '', redirect: { path: '/1' } },
28
32
  { path: '/:pathMatch(.*)', redirect: { path: '/1' } },
29
33
  ]
@@ -42,24 +46,33 @@ if (__SLIDEV_FEATURE_PRESENTER__) {
42
46
  return { path: `/${to.params.no}` }
43
47
  return { path: '' }
44
48
  }
45
- routes.push({ path: '/presenter/print', component: () => import('./internals/PresenterPrint.vue') })
49
+
50
+ routes.push({
51
+ path: '/presenter/print',
52
+ component: () => import('./pages/presenter/print.vue'),
53
+ })
46
54
  if (__SLIDEV_HAS_SERVER__) {
47
55
  routes.push({
48
56
  name: 'entry',
49
57
  path: '/entry',
50
- component: () => import('./internals/EntrySelect.vue'),
58
+ component: () => import('./pages/entry.vue'),
59
+ })
60
+ routes.push({
61
+ name: 'overview',
62
+ path: '/overview',
63
+ component: () => import('./pages/overview.vue'),
51
64
  })
52
65
  routes.push({
53
66
  name: 'notes',
54
67
  path: '/notes',
55
- component: () => import('./internals/NotesView.vue'),
68
+ component: () => import('./pages/notes.vue'),
56
69
  beforeEnter: passwordGuard,
57
70
  })
58
71
  }
59
72
  routes.push({
60
73
  name: 'presenter',
61
74
  path: '/presenter/:no',
62
- component: () => import('./internals/Presenter.vue'),
75
+ component: () => import('./pages/presenter.vue'),
63
76
  beforeEnter: passwordGuard,
64
77
  })
65
78
  routes.push({
@@ -86,19 +99,12 @@ declare module 'vue-router' {
86
99
  preload?: boolean
87
100
 
88
101
  // slide info
89
- slide?: {
102
+ slide?: Omit<SlideInfo, 'source'> & {
103
+ noteHTML: string
104
+ filepath: string
90
105
  start: number
91
- end: number
92
- note?: string
93
- noteHTML?: string
94
106
  id: number
95
107
  no: number
96
- filepath: string
97
- title?: string
98
- level?: number
99
- raw: string
100
- content: string
101
- frontmatter: Record<string, any>
102
108
  }
103
109
 
104
110
  // private fields
@@ -1,4 +1,5 @@
1
1
  import type { Ref, WritableComputedRef } from 'vue'
2
+ import { onClickOutside } from '@vueuse/core'
2
3
  import { watch } from 'vue'
3
4
  import * as _CodeMirror from 'codemirror'
4
5
  import 'codemirror/mode/javascript/javascript'
@@ -47,5 +48,11 @@ export async function useCodeMirror(
47
48
  { immediate: true },
48
49
  )
49
50
 
51
+ onClickOutside(cm.getWrapperElement(), () => {
52
+ const el = cm.getInputField()
53
+ if (document.activeElement === el)
54
+ el.blur()
55
+ })
56
+
50
57
  return cm
51
58
  }
package/state/index.ts CHANGED
@@ -20,19 +20,19 @@ export const activeElement = useActiveElement()
20
20
  export const isInputting = computed(() => ['INPUT', 'TEXTAREA'].includes(activeElement.value?.tagName || '') || activeElement.value?.classList.contains('CodeMirror-code'))
21
21
  export const isOnFocus = computed(() => ['BUTTON', 'A'].includes(activeElement.value?.tagName || ''))
22
22
 
23
- export const currentCamera = useLocalStorage<string>('slidev-camera', 'default')
24
- export const currentMic = useLocalStorage<string>('slidev-mic', 'default')
23
+ export const currentCamera = useLocalStorage<string>('slidev-camera', 'default', { listenToStorageChanges: false })
24
+ export const currentMic = useLocalStorage<string>('slidev-mic', 'default', { listenToStorageChanges: false })
25
25
  export const slideScale = useLocalStorage<number>('slidev-scale', 0)
26
26
 
27
- export const showOverview = useLocalStorage('slidev-show-overview', false)
28
- export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true)
29
- export const showEditor = useLocalStorage('slidev-show-editor', false)
30
- export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false)
31
- export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 318)
32
- export const editorHeight = useLocalStorage('slidev-editor-height', isClient ? window.innerHeight * 0.4 : 300)
27
+ export const showOverview = useLocalStorage('slidev-show-overview', false, { listenToStorageChanges: false })
28
+ export const showPresenterCursor = useLocalStorage('slidev-presenter-cursor', true, { listenToStorageChanges: false })
29
+ export const showEditor = useLocalStorage('slidev-show-editor', false, { listenToStorageChanges: false })
30
+ export const isEditorVertical = useLocalStorage('slidev-editor-vertical', false, { listenToStorageChanges: false })
31
+ export const editorWidth = useLocalStorage('slidev-editor-width', isClient ? window.innerWidth * 0.4 : 318, { listenToStorageChanges: false })
32
+ export const editorHeight = useLocalStorage('slidev-editor-height', isClient ? window.innerHeight * 0.4 : 300, { listenToStorageChanges: false })
33
33
 
34
- export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1)
35
- export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1)
34
+ export const presenterNotesFontSize = useLocalStorage('slidev-presenter-font-size', 1, { listenToStorageChanges: false })
35
+ export const presenterLayout = useLocalStorage('slidev-presenter-layout', 1, { listenToStorageChanges: false })
36
36
 
37
37
  export function togglePresenterLayout() {
38
38
  presenterLayout.value = presenterLayout.value + 1
package/styles/index.css CHANGED
@@ -55,3 +55,8 @@ html {
55
55
  .slidev-page {
56
56
  @apply absolute top-0 left-0 right-0 w-full relative;
57
57
  }
58
+
59
+ /* Transform the position back for Rough Notation (v-mark) */
60
+ .rough-annotation {
61
+ transform: scale(calc(1 / var(--slidev-slide-scale)));
62
+ }
@@ -54,7 +54,9 @@
54
54
  }
55
55
 
56
56
  blockquote {
57
- @apply text-sm px-2 py-1 bg-$prism-background text-$prism-foreground border-$slidev-theme-primary border-l rounded;
57
+ background: var(--slidev-code-background);
58
+ color: var(--slidev-code-foreground);
59
+ @apply text-sm px-2 py-1 border-primary border-l rounded;
58
60
  }
59
61
 
60
62
  blockquote > * {
@@ -66,7 +68,7 @@
66
68
  }
67
69
 
68
70
  tr {
69
- @apply border-b border-gray-400 border-opacity-20;
71
+ @apply border-b border-main;
70
72
  }
71
73
 
72
74
  th {
@@ -74,7 +76,7 @@
74
76
  }
75
77
 
76
78
  a {
77
- @apply border-current border-b border-dashed hover:text-$slidev-theme-primary hover:border-solid;
79
+ @apply border-current border-b border-dashed hover:text-primary hover:border-solid;
78
80
  }
79
81
 
80
82
  td, th {
@@ -86,7 +88,7 @@
86
88
  }
87
89
 
88
90
  kbd {
89
- @apply border border-gray-400 border-b-2 border-opacity-20 rounded;
91
+ @apply border border-main border-b-2 rounded;
90
92
  @apply bg-gray-400 bg-opacity-5 py-0.5 px-1 text-xs font-mono;
91
93
  }
92
94
  }
package/styles/vars.css CHANGED
@@ -7,6 +7,7 @@
7
7
  --slidev-code-line-height: 18px;
8
8
  --slidev-code-radius: 4px;
9
9
  --slidev-code-margin: 4px 0;
10
+ --slidev-theme-primary: #3AB9D5;
10
11
 
11
12
  --slidev-transition-duration: 0.5s;
12
13
  --slidev-slide-container-background: black;
package/uno.config.ts CHANGED
@@ -15,9 +15,13 @@ export default defineConfig({
15
15
  'prose',
16
16
  ],
17
17
  shortcuts: {
18
- 'bg-main': 'bg-white text-[#181818] dark:(bg-[#121212] text-[#ddd])',
18
+ 'bg-main': 'bg-white dark:bg-[#121212]',
19
19
  'bg-active': 'bg-gray-400/10',
20
- 'border-main': 'border-gray-400/20',
20
+ 'border-main': 'border-gray/20',
21
+ 'text-main': 'text-[#181818] dark:text-[#ddd]',
22
+ 'text-primary': 'color-$slidev-theme-primary',
23
+ 'bg-primary': 'bg-$slidev-theme-primary',
24
+ 'border-primary': 'border-$slidev-theme-primary',
21
25
  'abs-tl': 'absolute top-0 left-0',
22
26
  'abs-tr': 'absolute top-0 right-0',
23
27
  'abs-b': 'absolute bottom-0 left-0 right-0',