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

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.
@@ -69,7 +69,9 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
69
69
  const thisPath = +(route?.path ?? CLICKS_MAX)
70
70
  const current = computed({
71
71
  get() {
72
- const currentPath = +(currentRoute.value?.path ?? CLICKS_MAX)
72
+ const currentPath = +(currentRoute.value?.path ?? Number.NaN)
73
+ if (!currentPath || Number.isNaN(currentPath))
74
+ return 0
73
75
  if (currentPath === thisPath)
74
76
  return queryClicks.value
75
77
  else if (currentPath > thisPath)
@@ -78,7 +80,7 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
78
80
  return 0
79
81
  },
80
82
  set(v) {
81
- const currentPath = +(currentRoute.value?.path ?? CLICKS_MAX)
83
+ const currentPath = +(currentRoute.value?.path ?? Number.NaN)
82
84
  if (currentPath === thisPath)
83
85
  queryClicks.value = v
84
86
  },
@@ -2,7 +2,7 @@
2
2
  import { shallowRef } from 'vue'
3
3
  import { showInfoDialog, showOverview, showRecordingDialog } from '../state'
4
4
  import { configs } from '../env'
5
- import SlidesOverview from './SlidesOverview.vue'
5
+ import QuickOverview from './QuickOverview.vue'
6
6
  import InfoDialog from './InfoDialog.vue'
7
7
  import Goto from './Goto.vue'
8
8
 
@@ -15,7 +15,7 @@ if (__SLIDEV_FEATURE_RECORD__) {
15
15
  </script>
16
16
 
17
17
  <template>
18
- <SlidesOverview v-model="showOverview" />
18
+ <QuickOverview v-model="showOverview" />
19
19
  <Goto />
20
20
  <WebCamera v-if="WebCamera" />
21
21
  <RecordingDialog v-if="RecordingDialog" v-model="showRecordingDialog" />
@@ -2,14 +2,15 @@
2
2
  defineProps<{
3
3
  title: string
4
4
  icon?: string
5
+ as?: string
5
6
  }>()
6
7
  </script>
7
8
 
8
9
  <template>
9
- <button class="slidev-icon-btn" :title="title" v-bind="$attrs">
10
+ <component :is="as || 'button'" class="slidev-icon-btn" :title="title" v-bind="$attrs">
10
11
  <span class="sr-only">{{ title }}</span>
11
12
  <slot>
12
13
  <div :class="icon" />
13
14
  </slot>
14
- </button>
15
+ </component>
15
16
  </template>
@@ -114,13 +114,13 @@ if (__SLIDEV_FEATURE_DRAWINGS__)
114
114
  <IconButton
115
115
  v-if="__DEV__ && __SLIDEV_FEATURE_EDITOR__"
116
116
  :title="showEditor ? 'Hide editor' : 'Show editor'"
117
- class="<md:hidden"
117
+ class="lt-md:hidden"
118
118
  @click="showEditor = !showEditor"
119
119
  >
120
120
  <carbon:text-annotation-toggle />
121
121
  </IconButton>
122
122
 
123
- <IconButton v-if="isPresenter" title="Toggle Presenter Layout" @click="togglePresenterLayout">
123
+ <IconButton v-if="isPresenter" title="Toggle Presenter Layout" class="aspect-ratio-initial" @click="togglePresenterLayout">
124
124
  <carbon:template />
125
125
  {{ presenterLayout }}
126
126
  </IconButton>
@@ -9,6 +9,7 @@ const props = defineProps<{
9
9
  note?: string
10
10
  placeholder?: string
11
11
  clicksContext?: ClicksContext
12
+ autoScroll?: boolean
12
13
  }>()
13
14
 
14
15
  defineEmits(['click'])
@@ -96,6 +97,9 @@ function highlightNote() {
96
97
  e.stopPropagation()
97
98
  e.stopImmediatePropagation()
98
99
  })
100
+
101
+ if (props.autoScroll && clicks === current)
102
+ marker.scrollIntoView({ block: 'center', behavior: 'smooth' })
99
103
  }
100
104
  }
101
105
 
@@ -125,7 +129,7 @@ onMounted(() => {
125
129
  />
126
130
  <div
127
131
  v-else-if="note"
128
- class="prose overflow-auto outline-none"
132
+ class="prose overflow-auto outline-none slidev-note"
129
133
  :class="props.class"
130
134
  @click="$emit('click')"
131
135
  >
@@ -133,10 +137,16 @@ onMounted(() => {
133
137
  </div>
134
138
  <div
135
139
  v-else
136
- class="prose overflow-auto outline-none opacity-50 italic select-none"
140
+ class="prose overflow-auto outline-none opacity-50 italic select-none slidev-note"
137
141
  :class="props.class"
138
142
  @click="$emit('click')"
139
143
  >
140
144
  <p v-text="props.placeholder || 'No notes.'" />
141
145
  </div>
142
146
  </template>
147
+
148
+ <style>
149
+ .slidev-note :first-child {
150
+ margin-top: 0;
151
+ }
152
+ </style>
@@ -83,9 +83,7 @@ function calculateHeight() {
83
83
  }
84
84
 
85
85
  const inputHeight = ref<number | null>()
86
- watchEffect(() => {
87
- calculateHeight()
88
- })
86
+
89
87
  watch(
90
88
  note,
91
89
  () => {
@@ -93,19 +91,20 @@ watch(
93
91
  calculateHeight()
94
92
  })
95
93
  },
96
- { flush: 'post' },
94
+ { flush: 'post', immediate: true },
97
95
  )
98
96
  </script>
99
97
 
100
98
  <template>
101
99
  <NoteDisplay
102
100
  v-if="!editing"
103
- class="my--4 border-transparent border-2"
101
+ class="border-transparent border-2"
104
102
  :class="[props.class, note ? '' : 'opacity-25 italic select-none']"
105
103
  :style="props.style"
106
104
  :note="note || placeholder"
107
105
  :note-html="info?.noteHTML"
108
106
  :clicks-context="clicksContext"
107
+ :auto-scroll="!autoHeight"
109
108
  />
110
109
  <textarea
111
110
  v-else
@@ -116,7 +115,6 @@ watch(
116
115
  :style="[props.style, inputHeight != null ? { height: `${inputHeight}px` } : {}]"
117
116
  :class="props.class"
118
117
  :placeholder="placeholder"
119
- @keydown.esc=" editing = false"
120
- @focus="editing = true"
118
+ @keydown.esc="editing = false"
121
119
  />
122
120
  </template>
@@ -164,18 +164,19 @@ watchEffect(() => {
164
164
  </div>
165
165
  </div>
166
166
  </Transition>
167
- <div v-if="value" class="fixed top-4 right-4 text-gray-400 flex items-center gap-4">
168
- <RouterLink
169
- v-if="__DEV__"
167
+ <div v-if="value" class="fixed top-4 right-4 text-gray-400 flex flex-col items-center gap-2">
168
+ <IconButton title="Close" class="text-2xl" @click="close">
169
+ <carbon:close />
170
+ </IconButton>
171
+ <IconButton
172
+ as="a"
173
+ title="Slides Overview"
170
174
  target="_blank"
171
- to="/overview"
175
+ href="/overview"
172
176
  tab-index="-1"
173
- class="border-main border px3 py1 rounded hover:bg-gray/5 hover:text-primary"
177
+ class="text-2xl"
174
178
  >
175
- List overview
176
- </RouterLink>
177
- <IconButton title="Close" class="text-2xl" @click="close">
178
- <carbon:close />
179
+ <carbon:list-boxes />
179
180
  </IconButton>
180
181
  </div>
181
182
  </template>
@@ -54,7 +54,7 @@ onMounted(() => {
54
54
  </IconButton>
55
55
  <MenuButton :disabled="recording">
56
56
  <template #button>
57
- <IconButton title="Select recording device" class="h-full !text-sm !px-0">
57
+ <IconButton title="Select recording device" class="h-full !text-sm !px-0 aspect-initial">
58
58
  <carbon:chevron-up class="opacity-50" />
59
59
  </IconButton>
60
60
  </template>
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.13",
4
+ "version": "0.48.0-beta.14",
5
5
  "description": "Presentation slides for developers",
6
6
  "author": "antfu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -55,8 +55,8 @@
55
55
  "unocss": "^0.58.5",
56
56
  "vue": "^3.4.20",
57
57
  "vue-router": "^4.3.0",
58
- "@slidev/types": "0.48.0-beta.13",
59
- "@slidev/parser": "0.48.0-beta.13"
58
+ "@slidev/types": "0.48.0-beta.14",
59
+ "@slidev/parser": "0.48.0-beta.14"
60
60
  },
61
61
  "devDependencies": {
62
62
  "vite": "^5.1.4"
package/pages/notes.vue CHANGED
@@ -52,6 +52,7 @@ function decreaseFontSize() {
52
52
  :note-html="currentRoute?.meta?.slide?.noteHTML"
53
53
  :placeholder="`No notes for Slide ${pageNo}.`"
54
54
  :clicks-context="currentRoute?.meta?.__clicksContext"
55
+ :auto-scroll="true"
55
56
  />
56
57
  </div>
57
58
  <div class="flex-none border-t border-main">
@@ -3,7 +3,7 @@ import { computed, nextTick, onMounted, reactive, ref } from 'vue'
3
3
  import { useHead } from '@unhead/vue'
4
4
  import type { RouteRecordRaw } from 'vue-router'
5
5
  import type { ClicksContext } from 'packages/types'
6
- import { themeVars } from '../env'
6
+ import { configs, themeVars } from '../env'
7
7
  import { openInEditor, rawRoutes } from '../logic/nav'
8
8
  import { useFixedClicks } from '../composables/useClicks'
9
9
  import { isColorSchemaConfigured, isDark, toggleDark } from '../logic/dark'
@@ -18,8 +18,9 @@ import { CLICKS_MAX } from '../constants'
18
18
 
19
19
  const cardWidth = 450
20
20
 
21
+ const slideTitle = configs.titleTemplate.replace('%s', configs.title || 'Slidev')
21
22
  useHead({
22
- title: 'List Overview',
23
+ title: `Overview - ${slideTitle}`,
23
24
  })
24
25
 
25
26
  const blocks: Map<number, HTMLElement> = reactive(new Map())
@@ -56,7 +56,10 @@ const slidesWithNote = computed(() => rawRoutes
56
56
  <div class="flex-auto" />
57
57
  </div>
58
58
  </h2>
59
- <NoteDisplay :note-html="slide!.noteHTML" class="max-w-full" />
59
+ <NoteDisplay
60
+ :note-html="slide!.noteHTML"
61
+ class="max-w-full"
62
+ />
60
63
  </div>
61
64
  <hr v-if="index < slidesWithNote.length - 1" class="border-main mb-8">
62
65
  </div>
@@ -14,7 +14,7 @@ import { useFixedClicks } from '../composables/useClicks'
14
14
  import SlideWrapper from '../internals/SlideWrapper'
15
15
  import SlideContainer from '../internals/SlideContainer.vue'
16
16
  import NavControls from '../internals/NavControls.vue'
17
- import SlidesOverview from '../internals/SlidesOverview.vue'
17
+ import QuickOverview from '../internals/QuickOverview.vue'
18
18
  import NoteEditor from '../internals/NoteEditor.vue'
19
19
  import NoteStatic from '../internals/NoteStatic.vue'
20
20
  import Goto from '../internals/Goto.vue'
@@ -45,12 +45,19 @@ const nextFrame = computed(() => {
45
45
  else
46
46
  return null
47
47
  })
48
+
48
49
  const nextFrameClicksCtx = computed(() => {
49
50
  return nextFrame.value && clicksCtxMap[+nextFrame.value[0].path - 1]
50
51
  })
51
- watch([currentRoute, queryClicks], () => {
52
- nextFrameClicksCtx.value && (nextFrameClicksCtx.value.current = nextFrame.value![1])
53
- }, { immediate: true })
52
+
53
+ watch(
54
+ [currentRoute, queryClicks],
55
+ () => {
56
+ if (nextFrameClicksCtx.value)
57
+ nextFrameClicksCtx.value.current = nextFrame.value![1]
58
+ },
59
+ { immediate: true },
60
+ )
54
61
 
55
62
  const SideEditor = shallowRef<any>()
56
63
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
@@ -86,21 +93,6 @@ onMounted(() => {
86
93
  <template>
87
94
  <div class="bg-main h-full slidev-presenter">
88
95
  <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
96
  <div ref="main" class="relative grid-section main flex flex-col p-2 lg:p-4" :style="themeVars">
105
97
  <SlideContainer
106
98
  key="main"
@@ -110,8 +102,8 @@ onMounted(() => {
110
102
  <SlidesShow render-context="presenter" />
111
103
  </template>
112
104
  </SlideContainer>
113
- <div class="context">
114
- current
105
+ <div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
106
+ Current
115
107
  </div>
116
108
  </div>
117
109
  <div class="relative grid-section next flex flex-col p-2 lg:p-4" :style="themeVars">
@@ -129,8 +121,8 @@ onMounted(() => {
129
121
  render-context="previewNext"
130
122
  />
131
123
  </SlideContainer>
132
- <div class="context">
133
- next
124
+ <div class="absolute left-0 top-0 bg-main border-b border-r border-main px2 py1 op50 text-sm">
125
+ Next
134
126
  </div>
135
127
  </div>
136
128
  <!-- Notes -->
@@ -141,9 +133,9 @@ onMounted(() => {
141
133
  <NoteEditor
142
134
  v-if="__DEV__"
143
135
  :key="`edit-${currentSlideId}`"
136
+ v-model:editing="notesEditing"
144
137
  :no="currentSlideId"
145
138
  class="w-full max-w-full h-full overflow-auto p-2 lg:p-4"
146
- :editing="notesEditing"
147
139
  :clicks-context="clicksContext"
148
140
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
149
141
  />
@@ -171,8 +163,20 @@ onMounted(() => {
171
163
  </IconButton>
172
164
  </div>
173
165
  </div>
174
- <div class="grid-section bottom">
166
+ <div class="grid-section bottom flex">
175
167
  <NavControls :persist="true" />
168
+ <div flex-auto />
169
+ <div
170
+ class="timer-btn my-auto relative w-22px h-22px cursor-pointer text-lg"
171
+ opacity="50 hover:100"
172
+ @click="resetTimer"
173
+ >
174
+ <carbon:time class="absolute" />
175
+ <carbon:renew class="absolute opacity-0" />
176
+ </div>
177
+ <div class="text-2xl pl-2 pr-6 my-auto tabular-nums">
178
+ {{ timer }}
179
+ </div>
176
180
  </div>
177
181
  <DrawingControls v-if="__SLIDEV_FEATURE_DRAWINGS__" />
178
182
  </div>
@@ -184,7 +188,7 @@ onMounted(() => {
184
188
  </div>
185
189
  </div>
186
190
  <Goto />
187
- <SlidesOverview v-model="showOverview" />
191
+ <QuickOverview v-model="showOverview" />
188
192
  </template>
189
193
 
190
194
  <style scoped>
@@ -204,7 +208,7 @@ onMounted(() => {
204
208
  }
205
209
 
206
210
  .grid-container {
207
- --uno: bg-active;
211
+ --uno: bg-gray/20;
208
212
  height: 100%;
209
213
  width: 100%;
210
214
  display: grid;
@@ -213,9 +217,8 @@ onMounted(() => {
213
217
 
214
218
  .grid-container.layout1 {
215
219
  grid-template-columns: 1fr 1fr;
216
- grid-template-rows: min-content 2fr 1fr min-content;
220
+ grid-template-rows: 2fr 1fr min-content;
217
221
  grid-template-areas:
218
- 'top top'
219
222
  'main main'
220
223
  'note next'
221
224
  'bottom bottom';
@@ -223,9 +226,8 @@ onMounted(() => {
223
226
 
224
227
  .grid-container.layout2 {
225
228
  grid-template-columns: 3fr 2fr;
226
- grid-template-rows: min-content 2fr 1fr min-content;
229
+ grid-template-rows: 2fr 1fr min-content;
227
230
  grid-template-areas:
228
- 'top top'
229
231
  'note main'
230
232
  'note next'
231
233
  'bottom bottom';
@@ -234,9 +236,8 @@ onMounted(() => {
234
236
  @media (max-aspect-ratio: 3/5) {
235
237
  .grid-container.layout1 {
236
238
  grid-template-columns: 1fr;
237
- grid-template-rows: min-content 1fr 1fr 1fr min-content;
239
+ grid-template-rows: 1fr 1fr 1fr min-content;
238
240
  grid-template-areas:
239
- 'top'
240
241
  'main'
241
242
  'note'
242
243
  'next'
@@ -247,9 +248,8 @@ onMounted(() => {
247
248
  @media (min-aspect-ratio: 1/1) {
248
249
  .grid-container.layout1 {
249
250
  grid-template-columns: 1fr 1.1fr 0.9fr;
250
- grid-template-rows: min-content 1fr 2fr min-content;
251
+ grid-template-rows: 1fr 2fr min-content;
251
252
  grid-template-areas:
252
- 'top top top'
253
253
  'main main next'
254
254
  'main main note'
255
255
  'bottom bottom bottom';
@@ -279,10 +279,4 @@ onMounted(() => {
279
279
  .grid-section.bottom {
280
280
  grid-area: bottom;
281
281
  }
282
- .context {
283
- position: absolute;
284
- top: 0;
285
- left: 0;
286
- --uno: px-1 text-xs bg-gray-400 bg-opacity-50 opacity-75 rounded-br-md;
287
- }
288
282
  </style>