Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3ca226c

Browse files
committed
feat: introduce notes editor
1 parent6979541 commit3ca226c

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<script setup lang="ts">
2+
importtype {SlideRoute }from'@slidev/types'
3+
import {useHead }from'@unhead/vue'
4+
import {debouncedWatch }from'@vueuse/core'
5+
import {ref }from'vue'
6+
import {useNav }from'../composables/useNav'
7+
import {useDynamicSlideInfo }from'../composables/useSlideInfo'
8+
import {slidesTitle }from'../env'
9+
importIconButtonfrom'../internals/IconButton.vue'
10+
importModalfrom'../internals/Modal.vue'
11+
12+
useHead({ title:`Notes Edit - ${slidesTitle}` })
13+
14+
const { slides }=useNav()
15+
16+
const showHelp=ref(false)
17+
const note=ref(serializeNotes(slides.value))
18+
19+
function serializeNotes(slides:SlideRoute[]) {
20+
const lines:string[]= []
21+
22+
for (const slideofslides) {
23+
if (!slide.meta.slide.note?.trim())
24+
continue
25+
lines.push(`--- #${slide.no}`)
26+
lines.push('')
27+
lines.push(slide.meta.slide.note)
28+
lines.push('')
29+
}
30+
31+
returnlines.join('\n')
32+
}
33+
34+
function deserializeNotes(notes:string,slides:SlideRoute[]) {
35+
const lines=notes.split(/^(---\s*#\d+\s*)$/gm)
36+
37+
lines.forEach((line,index)=> {
38+
const match=line.match(/^---\s*#(\d+)\s*$/)
39+
if (match) {
40+
const no=Number.parseInt(match[1])
41+
const note=lines[index+1].trim()
42+
const slide=slides.find(s=>s.no===no)
43+
if (slide) {
44+
slide.meta.slide.note=note
45+
useDynamicSlideInfo(no).update({note })
46+
}
47+
}
48+
})
49+
}
50+
51+
debouncedWatch(note, (value)=> {
52+
deserializeNotes(value,slides.value)
53+
}, { debounce:300 })
54+
</script>
55+
56+
<template>
57+
<Modalv-model="showHelp"class="px-6 py-4 flex flex-col gap-2">
58+
<divclass="flex gap-2 text-xl">
59+
<divclass="i-carbon:information my-auto" /> Help
60+
</div>
61+
<divclass="prose dark:prose-invert">
62+
<p>This is the batch notes editor. You can edit the notes for all the slides at once here.</p>
63+
64+
<p>The note for each slide are separated by <code>--- #[no]</code> lines, you might want to keep them while editing.</p>
65+
</div>
66+
<divclass="flex my-1">
67+
<buttonclass="slidev-form-button"@click="showHelp = false">
68+
Close
69+
</button>
70+
</div>
71+
</Modal>
72+
<divclass="h-full">
73+
<divclass="slidev-glass-effect fixed bottom-5 right-5 rounded-full border border-main">
74+
<IconButtontitle="Help"class="rounded-full"@click="showHelp = true">
75+
<divclass="i-carbon:help text-2xl" />
76+
</IconButton>
77+
</div>
78+
<textarea
79+
v-model="note"
80+
class="prose dark:prose-invert resize-none p5 outline-none bg-transparent block h-full w-full! max-w-full! max-h-full! min-h-full! min-w-full!"
81+
/>
82+
</div>
83+
</template>

‎packages/client/pages/notes.vue‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { computed, ref, watch } from 'vue'
55
import {createClicksContextBase }from'../composables/useClicks'
66
import {useNav }from'../composables/useNav'
77
import {slidesTitle }from'../env'
8-
98
importClicksSliderfrom'../internals/ClicksSlider.vue'
109
importIconButtonfrom'../internals/IconButton.vue'
10+
importModalfrom'../internals/Modal.vue'
1111
importNoteDisplayfrom'../internals/NoteDisplay.vue'
1212
import {fullscreen }from'../state'
1313
import {sharedState }from'../state/shared'
@@ -20,6 +20,7 @@ const { isFullscreen, toggle: toggleFullscreen } = fullscreen
2020
const scroller=ref<HTMLDivElement>()
2121
const fontSize=useLocalStorage('slidev-notes-font-size',18)
2222
const pageNo=computed(()=>sharedState.page)
23+
const showHelp=ref(false)
2324
const currentRoute=computed(()=>slides.value.find(i=>i.no===pageNo.value))
2425
2526
watch(pageNo, ()=> {
@@ -43,6 +44,20 @@ const clicksContext = computed(() => {
4344
</script>
4445

4546
<template>
47+
<Modalv-model="showHelp"class="px-6 py-4 flex flex-col gap-2">
48+
<divclass="flex gap-2 text-xl">
49+
<divclass="i-carbon:information my-auto" /> Help
50+
</div>
51+
<divclass="prose dark:prose-invert">
52+
<p>This is the hands-free live notes viewer.</p>
53+
<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>
54+
</div>
55+
<divclass="flex my-1">
56+
<buttonclass="slidev-form-button"@click="showHelp = false">
57+
Close
58+
</button>
59+
</div>
60+
</Modal>
4661
<div
4762
class="fixed top-0 left-0 h-3px bg-primary transition-all duration-500"
4863
:style="{ width: `${(pageNo - 1) / (total - 1) * 100 + 1}%` }"
@@ -76,6 +91,12 @@ const clicksContext = computed(() => {
7691
<IconButtontitle="Decrease font size"@click="decreaseFontSize">
7792
<divclass="i-carbon:zoom-out" />
7893
</IconButton>
94+
<IconButtontitle="Edit notes"to="/notes-edit"target="_blank">
95+
<divclass="i-carbon:edit" />
96+
</IconButton>
97+
<IconButtontitle="Help"class="rounded-full"@click="showHelp = true">
98+
<divclass="i-carbon:help" />
99+
</IconButton>
79100
<divclass="flex-auto" />
80101
<divclass="p2 text-center">
81102
{{ pageNo }} / {{ total }}

‎packages/client/setup/routes.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ export default function setupRoutes() {
3939
component:()=>import('../pages/notes.vue'),
4040
beforeEnter:passwordGuard,
4141
},
42+
{
43+
name:'notes-edit',
44+
path:'/notes-edit',
45+
component:()=>import('../pages/notes-edit.vue'),
46+
beforeEnter:passwordGuard,
47+
},
4248
{
4349
name:'presenter',
4450
path:'/presenter/:no',

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp