返回 slidev
notes.vue
根目录 / packages / client / pages / notes.vue
1 <script setup lang="ts">
2 import { useHead } from '@unhead/vue'
3 import { useLocalStorage } from '@vueuse/core'
4 import { computed, ref, watch } from 'vue'
5 import { createClicksContextBase } from '../composables/useClicks'
6 import { useNav } from '../composables/useNav'
7 import { slidesTitle } from '../env'
8 import ClicksSlider from '../internals/ClicksSlider.vue'
9 import CurrentProgressBar from '../internals/CurrentProgressBar.vue'
10 import IconButton from '../internals/IconButton.vue'
11 import Modal from '../internals/Modal.vue'
12 import NoteDisplay from '../internals/NoteDisplay.vue'
13 import TimerBar from '../internals/TimerBar.vue'
14 import { fullscreen } from '../state'
15 import { sharedState } from '../state/shared'
16
17 useHead({ title: `Notes - ${slidesTitle}` })
18
19 const { slides, total } = useNav()
20 const { isFullscreen, toggle: toggleFullscreen } = fullscreen
21
22 const scroller = ref<HTMLDivElement>()
23 const fontSize = useLocalStorage('slidev-notes-font-size', 18)
24 const pageNo = computed(() => sharedState.page)
25 const showHelp = ref(false)
26 const currentRoute = computed(() => slides.value.find(i => i.no === pageNo.value))
27
28 watch(pageNo, () => {
29 scroller.value?.scrollTo({ left: 0, top: 0, behavior: 'smooth' })
30 window.scrollTo({ left: 0, top: 0, behavior: 'smooth' })
31 })
32
33 function increaseFontSize() {
34 fontSize.value = fontSize.value + 1
35 }
36
37 function decreaseFontSize() {
38 fontSize.value = fontSize.value - 1
39 }
40
41 const clicksContext = computed(() => {
42 const clicks = sharedState.clicks
43 const total = sharedState.clicksTotal
44 return createClicksContextBase(ref(clicks), undefined, total)
45 })
46 </script>
47
48 <template>
49 <Modal v-model="showHelp" class="px-6 py-4 flex flex-col gap-2">
50 <div class="flex gap-2 text-xl">
51 <div class="i-carbon:information my-auto" /> Help
52 </div>
53 <div class="prose dark:prose-invert">
54 <p>This is the hands-free live notes viewer.</p>
55 <p>It's designed to be used in a separate view or device. The progress is controlled by and auto synced with the main presenter or slide.</p>
56 </div>
57 <div class="flex my-1">
58 <button class="slidev-form-button" @click="showHelp = false">
59 Close
60 </button>
61 </div>
62 </Modal>
63 <div class="h-full flex flex-col">
64 <CurrentProgressBar :clicks-context="clicksContext" :current="pageNo" />
65 <TimerBar />
66 <div
67 ref="scroller"
68 class="px-5 py-3 flex-auto h-full overflow-auto"
69 :style="{ fontSize: `${fontSize}px` }"
70 >
71 <NoteDisplay
72 :note="currentRoute?.meta.slide.note"
73 :note-html="currentRoute?.meta.slide.noteHTML"
74 :placeholder="`No notes for Slide ${pageNo}.`"
75 :clicks-context="clicksContext"
76 :auto-scroll="true"
77 />
78 </div>
79 <div class="flex-none border-t border-main" px3 py2>
80 <ClicksSlider :clicks-context="clicksContext" readonly />
81 </div>
82 <div class="flex-none border-t border-main">
83 <div class="flex gap-1 items-center px-6 py-3">
84 <IconButton :title="isFullscreen ? 'Close fullscreen' : 'Enter fullscreen'" @click="toggleFullscreen">
85 <div v-if="isFullscreen" class="i-carbon:minimize" />
86 <div v-else class="i-carbon:maximize" />
87 </IconButton>
88 <IconButton title="Increase font size" @click="increaseFontSize">
89 <div class="i-carbon:zoom-in" />
90 </IconButton>
91 <IconButton title="Decrease font size" @click="decreaseFontSize">
92 <div class="i-carbon:zoom-out" />
93 </IconButton>
94 <IconButton title="Edit notes" to="/notes-edit" target="_blank">
95 <div class="i-carbon:edit" />
96 </IconButton>
97 <IconButton title="Help" class="rounded-full" @click="showHelp = true">
98 <div class="i-carbon:help" />
99 </IconButton>
100 <div class="flex-auto" />
101 <div class="px2 my-auto">
102 <span class="text-lg">{{ pageNo }}</span>
103 <span class="opacity-50 text-sm"> / {{ total }}</span>
104 </div>
105 </div>
106 </div>
107 </div>
108 </template>
109
109 lines Plain Text