@slidev/client 0.48.0-beta.2 → 0.48.0-beta.21

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 (91) 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 +9 -7
  12. package/builtin/Toc.vue +4 -4
  13. package/builtin/TocList.vue +4 -3
  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 +39 -20
  19. package/composables/useContext.ts +4 -9
  20. package/composables/useNav.ts +182 -44
  21. package/composables/useSwipeControls.ts +40 -0
  22. package/composables/useTocTree.ts +63 -0
  23. package/constants.ts +59 -10
  24. package/context.ts +73 -0
  25. package/env.ts +3 -12
  26. package/internals/ClicksSlider.vue +93 -0
  27. package/internals/Controls.vue +2 -2
  28. package/internals/DrawingControls.vue +39 -9
  29. package/internals/DrawingLayer.vue +3 -3
  30. package/internals/Goto.vue +7 -6
  31. package/internals/IconButton.vue +7 -3
  32. package/internals/InfoDialog.vue +1 -1
  33. package/internals/Modal.vue +1 -1
  34. package/internals/NavControls.vue +11 -10
  35. package/internals/NoteDisplay.vue +131 -8
  36. package/internals/NoteEditable.vue +128 -0
  37. package/internals/NoteStatic.vue +8 -6
  38. package/internals/PrintContainer.vue +8 -6
  39. package/internals/PrintSlide.vue +10 -11
  40. package/internals/PrintSlideClick.vue +14 -18
  41. package/internals/{SlidesOverview.vue → QuickOverview.vue} +31 -20
  42. package/internals/RecordingControls.vue +1 -1
  43. package/internals/RecordingDialog.vue +5 -6
  44. package/internals/{Editor.vue → SideEditor.vue} +9 -5
  45. package/internals/SlideContainer.vue +12 -9
  46. package/internals/SlideLoading.vue +19 -0
  47. package/internals/SlideWrapper.ts +32 -16
  48. package/internals/SlidesShow.vue +20 -18
  49. package/layouts/error.vue +5 -0
  50. package/layouts/two-cols-header.vue +9 -3
  51. package/logic/drawings.ts +13 -10
  52. package/logic/nav-state.ts +20 -0
  53. package/logic/nav.ts +51 -258
  54. package/logic/note.ts +9 -9
  55. package/logic/overview.ts +2 -2
  56. package/logic/route.ts +10 -1
  57. package/logic/slides.ts +19 -0
  58. package/logic/transition.ts +50 -0
  59. package/main.ts +8 -4
  60. package/modules/context.ts +7 -13
  61. package/modules/mermaid.ts +6 -7
  62. package/modules/{directives.ts → v-click.ts} +15 -15
  63. package/modules/v-mark.ts +159 -0
  64. package/package.json +27 -16
  65. package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
  66. package/{internals/NotesView.vue → pages/notes.vue} +7 -6
  67. package/pages/overview.vue +227 -0
  68. package/{internals/Play.vue → pages/play.vue} +17 -13
  69. package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +13 -8
  70. package/{internals/Presenter.vue → pages/presenter.vue} +114 -105
  71. package/{internals/Print.vue → pages/print.vue} +3 -4
  72. package/routes.ts +28 -60
  73. package/setup/codemirror.ts +8 -3
  74. package/setup/monaco.ts +108 -44
  75. package/setup/root.ts +8 -9
  76. package/setup/shortcuts.ts +2 -1
  77. package/shim-vue.d.ts +38 -0
  78. package/shim.d.ts +1 -13
  79. package/state/index.ts +10 -10
  80. package/styles/code.css +7 -3
  81. package/styles/index.css +68 -7
  82. package/styles/katex.css +1 -1
  83. package/styles/layouts-base.css +17 -12
  84. package/styles/monaco.css +27 -0
  85. package/styles/vars.css +1 -0
  86. package/uno.config.ts +14 -2
  87. package/utils.ts +15 -2
  88. package/iframes/monaco/index.css +0 -28
  89. package/iframes/monaco/index.html +0 -7
  90. package/iframes/monaco/index.ts +0 -260
  91. package/internals/NoteEditor.vue +0 -88
@@ -1,15 +1,16 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, shallowRef } from 'vue'
3
3
  import { isEditorVertical, isScreenVertical, showEditor, slideScale, windowSize } from '../state'
4
- import { isEmbedded, isPrintMode, next, prev, useSwipeControls } from '../logic/nav'
4
+ import { isEmbedded, isPrintMode, next, prev } from '../logic/nav'
5
+ import { useSwipeControls } from '../composables/useSwipeControls'
5
6
  import { isDrawing } from '../logic/drawings'
6
7
  import { registerShortcuts } from '../logic/shortcuts'
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 { configs } from '../env'
9
+ import Controls from '../internals/Controls.vue'
10
+ import SlideContainer from '../internals/SlideContainer.vue'
11
+ import NavControls from '../internals/NavControls.vue'
12
+ import SlidesShow from '../internals/SlidesShow.vue'
13
+ import PrintStyle from '../internals/PrintStyle.vue'
13
14
 
14
15
  registerShortcuts()
15
16
 
@@ -31,18 +32,21 @@ useSwipeControls(root)
31
32
 
32
33
  const persistNav = computed(() => isScreenVertical.value || showEditor.value)
33
34
 
34
- const Editor = shallowRef<any>()
35
+ const SideEditor = shallowRef<any>()
35
36
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
36
- import('./Editor.vue').then(v => Editor.value = v.default)
37
+ import('../internals/SideEditor.vue').then(v => SideEditor.value = v.default)
37
38
 
38
39
  const DrawingControls = shallowRef<any>()
39
40
  if (__SLIDEV_FEATURE_DRAWINGS__)
40
- import('./DrawingControls.vue').then(v => DrawingControls.value = v.default)
41
+ import('../internals/DrawingControls.vue').then(v => DrawingControls.value = v.default)
41
42
  </script>
42
43
 
43
44
  <template>
44
45
  <PrintStyle v-if="isPrintMode" />
45
- <div id="page-root" ref="root" class="grid" :class="isEditorVertical ? 'grid-rows-[1fr_max-content]' : 'grid-cols-[1fr_max-content]'" :style="themeVars">
46
+ <div
47
+ id="page-root" ref="root" class="grid"
48
+ :class="isEditorVertical ? 'grid-rows-[1fr_max-content]' : 'grid-cols-[1fr_max-content]'"
49
+ >
46
50
  <SlideContainer
47
51
  class="w-full h-full"
48
52
  :style="{ background: 'var(--slidev-slide-container-background, black)' }"
@@ -70,8 +74,8 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
70
74
  </template>
71
75
  </SlideContainer>
72
76
 
73
- <template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && Editor && showEditor">
74
- <Editor :resize="true" />
77
+ <template v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && SideEditor && showEditor">
78
+ <SideEditor :resize="true" />
75
79
  </template>
76
80
  </div>
77
81
  <Controls />
@@ -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 } from '../../env'
6
+ import { slides, total } from '../../logic/nav'
7
+ import NoteDisplay from '../../internals/NoteDisplay.vue'
8
8
 
9
9
  useStyleTag(`
10
10
  @page {
@@ -24,15 +24,17 @@ 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
- const slidesWithNote = computed(() => rawRoutes
31
+ const slidesWithNote = computed(() => slides.value
30
32
  .map(route => route.meta?.slide)
31
33
  .filter(slide => slide !== undefined && slide.noteHTML !== ''))
32
34
  </script>
33
35
 
34
36
  <template>
35
- <div id="page-root" :style="themeVars">
37
+ <div id="page-root">
36
38
  <div class="m-4">
37
39
  <div class="mb-10">
38
40
  <h1 class="text-4xl font-bold mt-2">
@@ -54,9 +56,12 @@ const slidesWithNote = computed(() => rawRoutes
54
56
  <div class="flex-auto" />
55
57
  </div>
56
58
  </h2>
57
- <NoteDisplay :note-html="slide!.noteHTML" class="max-w-full" />
59
+ <NoteDisplay
60
+ :note-html="slide!.noteHTML"
61
+ class="max-w-full"
62
+ />
58
63
  </div>
59
- <hr v-if="index < slidesWithNote.length - 1" class="border-gray-400/50 mb-8">
64
+ <hr v-if="index < slidesWithNote.length - 1" class="border-main mb-8">
60
65
  </div>
61
66
  </div>
62
67
  </div>
@@ -2,25 +2,27 @@
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, currentSlideNo, currentSlideRoute, hasNext, nextRoute, queryClicks, slides, total } from '../logic/nav'
6
+ import { useSwipeControls } from '../composables/useSwipeControls'
6
7
  import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showOverview, showPresenterCursor } from '../state'
7
- import { configs, themeVars } from '../env'
8
+ import { configs } from '../env'
8
9
  import { sharedState } from '../state/shared'
9
10
  import { registerShortcuts } from '../logic/shortcuts'
10
11
  import { getSlideClass } from '../utils'
11
12
  import { useTimer } from '../logic/utils'
12
13
  import { isDrawing } from '../logic/drawings'
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 { useFixedClicks, usePrimaryClicks } from '../composables/useClicks'
15
+ import SlideWrapper from '../internals/SlideWrapper'
16
+ import SlideContainer from '../internals/SlideContainer.vue'
17
+ import NavControls from '../internals/NavControls.vue'
18
+ import QuickOverview from '../internals/QuickOverview.vue'
19
+ import NoteEditable from '../internals/NoteEditable.vue'
20
+ import NoteStatic from '../internals/NoteStatic.vue'
21
+ import Goto from '../internals/Goto.vue'
22
+ import SlidesShow from '../internals/SlidesShow.vue'
23
+ import DrawingControls from '../internals/DrawingControls.vue'
24
+ import IconButton from '../internals/IconButton.vue'
25
+ import ClicksSlider from '../internals/ClicksSlider.vue'
24
26
 
25
27
  const main = ref<HTMLDivElement>()
26
28
 
@@ -36,25 +38,32 @@ const notesEditing = ref(false)
36
38
 
37
39
  const { timer, resetTimer } = useTimer()
38
40
 
39
- const clicksCtxMap = rawRoutes.map(route => useFixedClicks(route))
41
+ const clicksCtxMap = computed(() => slides.value.map(route => useFixedClicks(route)))
40
42
  const nextFrame = computed(() => {
41
43
  if (clicksContext.value.current < clicksContext.value.total)
42
- return [currentRoute.value!, clicksContext.value.current + 1] as const
44
+ return [currentSlideRoute.value!, clicksContext.value.current + 1] as const
43
45
  else if (hasNext.value)
44
46
  return [nextRoute.value!, 0] as const
45
47
  else
46
48
  return null
47
49
  })
50
+
48
51
  const nextFrameClicksCtx = computed(() => {
49
- return nextFrame.value && clicksCtxMap[+nextFrame.value[0].path - 1]
52
+ return nextFrame.value && clicksCtxMap.value[nextFrame.value[0].no - 1]
50
53
  })
51
- watch([currentRoute, queryClicks], () => {
52
- nextFrameClicksCtx.value && (nextFrameClicksCtx.value[0].value = nextFrame.value![1])
53
- }, { immediate: true })
54
54
 
55
- const Editor = shallowRef<any>()
55
+ watch(
56
+ [currentSlideRoute, queryClicks],
57
+ () => {
58
+ if (nextFrameClicksCtx.value)
59
+ nextFrameClicksCtx.value.current = nextFrame.value![1]
60
+ },
61
+ { immediate: true },
62
+ )
63
+
64
+ const SideEditor = shallowRef<any>()
56
65
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
57
- import('./Editor.vue').then(v => Editor.value = v.default)
66
+ import('../internals/SideEditor.vue').then(v => SideEditor.value = v.default)
58
67
 
59
68
  // sync presenter cursor
60
69
  onMounted(() => {
@@ -86,68 +95,64 @@ onMounted(() => {
86
95
  <template>
87
96
  <div class="bg-main h-full slidev-presenter">
88
97
  <div class="grid-container" :class="`layout${presenterLayout}`">
89
- <div class="grid-section top flex">
90
- <img src="../assets/logo-title-horizontal.png" class="ml-2 my-auto h-10 py-1 lg:h-14 lg:py-2" style="height: 3.5rem;" alt="Slidev logo">
91
- <div class="flex-auto" />
92
- <div
93
- class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
94
- opacity="50 hover:100"
95
- @click="resetTimer"
96
- >
97
- <carbon:time class="absolute" />
98
- <carbon:renew class="absolute opacity-0" />
99
- </div>
100
- <div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
101
- {{ timer }}
102
- </div>
103
- </div>
104
- <div ref="main" class="relative grid-section main flex flex-col p-2 lg:p-4" :style="themeVars">
98
+ <div ref="main" class="relative grid-section main flex flex-col">
105
99
  <SlideContainer
106
100
  key="main"
107
- class="h-full w-full"
101
+ class="h-full w-full p-2 lg:p-4 flex-auto"
108
102
  >
109
103
  <template #default>
110
104
  <SlidesShow render-context="presenter" />
111
105
  </template>
112
106
  </SlideContainer>
113
- <div class="context">
114
- current
107
+ <ClicksSlider
108
+ :key="currentSlideRoute?.no"
109
+ :clicks-context="usePrimaryClicks(currentSlideRoute)"
110
+ class="w-full pb2 px4 flex-none"
111
+ />
112
+ <div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
113
+ Current
115
114
  </div>
116
115
  </div>
117
- <div class="relative grid-section next flex flex-col p-2 lg:p-4" :style="themeVars">
116
+ <div class="relative grid-section next flex flex-col p-2 lg:p-4">
118
117
  <SlideContainer
119
118
  v-if="nextFrame && nextFrameClicksCtx"
120
119
  key="next"
121
120
  class="h-full w-full"
122
121
  >
123
122
  <SlideWrapper
124
- :is="nextFrame[0].component as any"
125
- :key="nextFrame[0].path"
126
- :clicks-context="nextFrameClicksCtx[1]"
123
+ :is="nextFrame[0].component!"
124
+ :key="nextFrame[0].no"
125
+ :clicks-context="nextFrameClicksCtx"
127
126
  :class="getSlideClass(nextFrame[0])"
128
127
  :route="nextFrame[0]"
129
128
  render-context="previewNext"
130
129
  />
131
130
  </SlideContainer>
132
- <div class="context">
133
- next
131
+ <div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
132
+ Next
134
133
  </div>
135
134
  </div>
136
135
  <!-- Notes -->
137
- <div v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && Editor && showEditor" class="grid-section note of-auto">
138
- <Editor />
136
+ <div v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__ && SideEditor && showEditor" class="grid-section note of-auto">
137
+ <SideEditor />
139
138
  </div>
140
139
  <div v-else class="grid-section note grid grid-rows-[1fr_min-content] overflow-hidden">
141
- <NoteEditor
140
+ <NoteEditable
142
141
  v-if="__DEV__"
142
+ :key="`edit-${currentSlideNo}`"
143
+ v-model:editing="notesEditing"
144
+ :no="currentSlideNo"
143
145
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
144
- :editing="notesEditing"
146
+ :clicks-context="clicksContext"
145
147
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
146
148
  />
147
149
  <NoteStatic
148
150
  v-else
151
+ :key="`static-${currentSlideNo}`"
152
+ :no="currentSlideNo"
149
153
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
150
154
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
155
+ :clicks-context="clicksContext"
151
156
  />
152
157
  <div class="border-t border-main py-1 px-2 text-sm">
153
158
  <IconButton title="Increase font size" @click="increasePresenterFontSize">
@@ -165,116 +170,120 @@ onMounted(() => {
165
170
  </IconButton>
166
171
  </div>
167
172
  </div>
168
- <div class="grid-section bottom">
173
+ <div class="grid-section bottom flex">
169
174
  <NavControls :persist="true" />
175
+ <div flex-auto />
176
+ <div
177
+ class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
178
+ opacity="50 hover:100"
179
+ @click="resetTimer"
180
+ >
181
+ <carbon:time class="absolute" />
182
+ <carbon:renew class="absolute opacity-0" />
183
+ </div>
184
+ <div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
185
+ {{ timer }}
186
+ </div>
170
187
  </div>
171
188
  <DrawingControls v-if="__SLIDEV_FEATURE_DRAWINGS__" />
172
189
  </div>
173
190
  <div class="progress-bar">
174
191
  <div
175
- class="progress h-2px bg-primary transition-all"
176
- :style="{ width: `${(currentPage - 1) / (total - 1) * 100}%` }"
192
+ class="progress h-3px bg-primary transition-all"
193
+ :style="{ width: `${(currentSlideNo - 1) / (total - 1) * 100}%` }"
177
194
  />
178
195
  </div>
179
196
  </div>
180
197
  <Goto />
181
- <SlidesOverview v-model="showOverview" />
198
+ <QuickOverview v-model="showOverview" />
182
199
  </template>
183
200
 
184
- <style lang="postcss" scoped>
201
+ <style scoped>
185
202
  .slidev-presenter {
186
203
  --slidev-controls-foreground: current;
187
204
  }
188
205
 
189
- .timer-btn:hover {
190
- & > :first-child {
191
- @apply opacity-0;
192
- }
193
- & > :last-child {
194
- @apply opacity-100;
195
- }
206
+ .timer-btn:hover > :first-child {
207
+ opacity: 0;
208
+ }
209
+ .timer-btn:hover > :last-child {
210
+ opacity: 1;
196
211
  }
197
212
 
198
213
  .section-title {
199
- @apply px-4 py-2 text-xl;
214
+ --uno: px-4 py-2 text-xl;
200
215
  }
201
216
 
202
217
  .grid-container {
203
- @apply h-full w-full bg-gray-400 bg-opacity-15;
218
+ --uno: bg-gray/20;
219
+ height: 100%;
220
+ width: 100%;
204
221
  display: grid;
205
222
  gap: 1px 1px;
206
223
  }
207
224
 
208
225
  .grid-container.layout1 {
209
226
  grid-template-columns: 1fr 1fr;
210
- grid-template-rows: min-content 2fr 1fr min-content;
227
+ grid-template-rows: 2fr 1fr min-content;
211
228
  grid-template-areas:
212
- "top top"
213
- "main main"
214
- "note next"
215
- "bottom bottom";
229
+ 'main main'
230
+ 'note next'
231
+ 'bottom bottom';
216
232
  }
217
233
 
218
234
  .grid-container.layout2 {
219
235
  grid-template-columns: 3fr 2fr;
220
- grid-template-rows: min-content 2fr 1fr min-content;
236
+ grid-template-rows: 2fr 1fr min-content;
221
237
  grid-template-areas:
222
- "top top"
223
- "note main"
224
- "note next"
225
- "bottom bottom";
238
+ 'note main'
239
+ 'note next'
240
+ 'bottom bottom';
226
241
  }
227
242
 
228
243
  @media (max-aspect-ratio: 3/5) {
229
244
  .grid-container.layout1 {
230
245
  grid-template-columns: 1fr;
231
- grid-template-rows: min-content 1fr 1fr 1fr min-content;
246
+ grid-template-rows: 1fr 1fr 1fr min-content;
232
247
  grid-template-areas:
233
- "top"
234
- "main"
235
- "note"
236
- "next"
237
- "bottom";
248
+ 'main'
249
+ 'note'
250
+ 'next'
251
+ 'bottom';
238
252
  }
239
253
  }
240
254
 
241
255
  @media (min-aspect-ratio: 1/1) {
242
256
  .grid-container.layout1 {
243
257
  grid-template-columns: 1fr 1.1fr 0.9fr;
244
- grid-template-rows: min-content 1fr 2fr min-content;
258
+ grid-template-rows: 1fr 2fr min-content;
245
259
  grid-template-areas:
246
- "top top top"
247
- "main main next"
248
- "main main note"
249
- "bottom bottom bottom";
260
+ 'main main next'
261
+ 'main main note'
262
+ 'bottom bottom bottom';
250
263
  }
251
264
  }
252
265
 
253
266
  .progress-bar {
254
- @apply fixed left-0 right-0 bottom-0;
267
+ --uno: fixed left-0 right-0 top-0;
255
268
  }
256
269
 
257
270
  .grid-section {
258
- @apply bg-main;
259
-
260
- &.top {
261
- grid-area: top;
262
- }
263
- &.main {
264
- grid-area: main;
265
- }
266
- &.next {
267
- grid-area: next;
268
- }
269
- &.note {
270
- grid-area: note;
271
- }
272
- &.bottom {
273
- grid-area: bottom;
274
- }
271
+ --uno: bg-main;
275
272
  }
276
273
 
277
- .context {
278
- @apply absolute top-0 left-0 px-1 text-xs bg-gray-400 bg-opacity-50 opacity-75 rounded-br-md;
274
+ .grid-section.top {
275
+ grid-area: top;
276
+ }
277
+ .grid-section.main {
278
+ grid-area: main;
279
+ }
280
+ .grid-section.next {
281
+ grid-area: next;
282
+ }
283
+ .grid-section.note {
284
+ grid-area: note;
285
+ }
286
+ .grid-section.bottom {
287
+ grid-area: bottom;
279
288
  }
280
289
  </style>
@@ -2,9 +2,8 @@
2
2
  import { watchEffect } from 'vue'
3
3
  import { windowSize } from '../state'
4
4
  import { isPrintMode } from '../logic/nav'
5
- import { themeVars } from '../env'
6
- import PrintContainer from './PrintContainer.vue'
7
- import PrintStyle from './PrintStyle.vue'
5
+ import PrintContainer from '../internals/PrintContainer.vue'
6
+ import PrintStyle from '../internals/PrintStyle.vue'
8
7
 
9
8
  watchEffect(() => {
10
9
  if (isPrintMode)
@@ -16,7 +15,7 @@ watchEffect(() => {
16
15
 
17
16
  <template>
18
17
  <PrintStyle v-if="isPrintMode" />
19
- <div id="page-root" class="grid grid-cols-[1fr_max-content]" :style="themeVars">
18
+ <div id="page-root" class="grid grid-cols-[1fr_max-content]">
20
19
  <PrintContainer
21
20
  class="w-full h-full"
22
21
  :style="{ background: 'var(--slidev-slide-container-background, black)' }"
package/routes.ts CHANGED
@@ -1,65 +1,59 @@
1
1
  import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
2
2
  import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
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'
7
-
8
- // @ts-expect-error missing types
9
- import _rawRoutes, { redirects } from '/@slidev/routes'
10
-
11
- // @ts-expect-error missing types
12
- import _configs from '/@slidev/configs'
13
-
14
- export const rawRoutes = _rawRoutes as RouteRecordRaw[]
3
+ import configs from '#slidev/configs'
15
4
 
16
5
  export const routes: RouteRecordRaw[] = [
17
6
  {
18
- name: 'play',
19
- path: '/',
20
- component: Play,
21
- children: [
22
- ...rawRoutes,
23
- ...redirects,
24
- ],
7
+ name: 'print',
8
+ path: '/print',
9
+ component: () => import('./pages/print.vue'),
25
10
  },
26
- { name: 'print', path: '/print', component: Print },
11
+
12
+ // Redirects
27
13
  { path: '', redirect: { path: '/1' } },
28
- { path: '/:pathMatch(.*)', redirect: { path: '/1' } },
29
14
  ]
30
15
 
31
16
  if (__SLIDEV_FEATURE_PRESENTER__) {
32
17
  function passwordGuard(to: RouteLocationNormalized) {
33
- if (!_configs.remote || _configs.remote === to.query.password)
18
+ if (!configs.remote || configs.remote === to.query.password)
34
19
  return true
35
- if (_configs.remote && to.query.password === undefined) {
20
+ if (configs.remote && to.query.password === undefined) {
36
21
  // eslint-disable-next-line no-alert
37
22
  const password = prompt('Enter password')
38
- if (_configs.remote === password)
23
+ if (configs.remote === password)
39
24
  return true
40
25
  }
41
26
  if (to.params.no)
42
27
  return { path: `/${to.params.no}` }
43
28
  return { path: '' }
44
29
  }
45
- routes.push({ path: '/presenter/print', component: () => import('./internals/PresenterPrint.vue') })
30
+
31
+ routes.push({
32
+ path: '/presenter/print',
33
+ component: () => import('./pages/presenter/print.vue'),
34
+ })
46
35
  if (__SLIDEV_HAS_SERVER__) {
47
36
  routes.push({
48
37
  name: 'entry',
49
38
  path: '/entry',
50
- component: () => import('./internals/EntrySelect.vue'),
39
+ component: () => import('./pages/entry.vue'),
40
+ })
41
+ routes.push({
42
+ name: 'overview',
43
+ path: '/overview',
44
+ component: () => import('./pages/overview.vue'),
51
45
  })
52
46
  routes.push({
53
47
  name: 'notes',
54
48
  path: '/notes',
55
- component: () => import('./internals/NotesView.vue'),
49
+ component: () => import('./pages/notes.vue'),
56
50
  beforeEnter: passwordGuard,
57
51
  })
58
52
  }
59
53
  routes.push({
60
54
  name: 'presenter',
61
55
  path: '/presenter/:no',
62
- component: () => import('./internals/Presenter.vue'),
56
+ component: () => import('./pages/presenter.vue'),
63
57
  beforeEnter: passwordGuard,
64
58
  })
65
59
  routes.push({
@@ -68,41 +62,15 @@ if (__SLIDEV_FEATURE_PRESENTER__) {
68
62
  })
69
63
  }
70
64
 
65
+ routes.push({
66
+ name: 'play',
67
+ path: '/:no',
68
+ component: () => import('./pages/play.vue'),
69
+ })
70
+
71
71
  export const router = createRouter({
72
72
  history: __SLIDEV_HASH_ROUTE__
73
73
  ? createWebHashHistory(import.meta.env.BASE_URL)
74
74
  : createWebHistory(import.meta.env.BASE_URL),
75
75
  routes,
76
76
  })
77
-
78
- declare module 'vue-router' {
79
- interface RouteMeta {
80
- // inherited from frontmatter
81
- layout: string
82
- name?: string
83
- class?: string
84
- clicks?: number
85
- transition?: string | TransitionGroupProps | undefined
86
- preload?: boolean
87
-
88
- // slide info
89
- slide?: {
90
- start: number
91
- end: number
92
- note?: string
93
- noteHTML?: string
94
- id: number
95
- no: number
96
- filepath: string
97
- title?: string
98
- level?: number
99
- raw: string
100
- content: string
101
- frontmatter: Record<string, any>
102
- }
103
-
104
- // private fields
105
- __clicksContext: null | ClicksContext
106
- __preloaded?: boolean
107
- }
108
- }
@@ -1,6 +1,7 @@
1
1
  import type { Ref, WritableComputedRef } from 'vue'
2
+ import { onClickOutside } from '@vueuse/core'
2
3
  import { watch } from 'vue'
3
- import * as _CodeMirror from 'codemirror'
4
+ import CodeMirror from 'codemirror'
4
5
  import 'codemirror/mode/javascript/javascript'
5
6
  import 'codemirror/mode/css/css'
6
7
  import 'codemirror/mode/markdown/markdown'
@@ -9,8 +10,6 @@ import 'codemirror/mode/htmlmixed/htmlmixed'
9
10
  import 'codemirror/addon/display/placeholder'
10
11
  import 'codemirror/lib/codemirror.css'
11
12
 
12
- const CodeMirror = _CodeMirror.default ?? ('fromTextArea' in _CodeMirror ? _CodeMirror : globalThis.CodeMirror)
13
-
14
13
  export async function useCodeMirror(
15
14
  textarea: Ref<HTMLTextAreaElement | null | undefined>,
16
15
  input: Ref<string> | WritableComputedRef<string>,
@@ -47,5 +46,11 @@ export async function useCodeMirror(
47
46
  { immediate: true },
48
47
  )
49
48
 
49
+ onClickOutside(cm.getWrapperElement(), () => {
50
+ const el = cm.getInputField()
51
+ if (document.activeElement === el)
52
+ el.blur()
53
+ })
54
+
50
55
  return cm
51
56
  }