Skip to content

Commit 0f5b770

Browse files
feat(editor): add markdown presentaion mode (#552)
* feat(editor): add markdown presentation mode * feat(editor): enhance editor component with new CSS variable bindings and improved visibility logic * fix(editor): update font size handling to use reactive CSS variable * fix(editor): allow <del> tag in sanitized HTML output * fix(editor): improve list styling in markdown content * fix(editor): refactor scale handling to use CSS variable for markdown zoom * feat(editor): add markdown zoom controls
1 parent 81f8f89 commit 0f5b770

11 files changed

Lines changed: 411 additions & 109 deletions

File tree

src/main/store/module/preferences.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,8 @@ export default new Store<PreferencesStore>({
1919
language: 'en_US',
2020
theme: 'auto',
2121
editor: EDITOR_DEFAULTS,
22+
markdown: {
23+
scale: 1,
24+
},
2225
},
2326
})

src/main/store/types/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ export interface EditorSettings {
2929
matchBrackets: boolean
3030
}
3131

32+
export interface MarkdownSettings {
33+
scale: number
34+
}
35+
3236
export interface PreferencesStore {
3337
storagePath: string
3438
backupPath: string
3539
apiPort: number
3640
language: string
3741
theme: 'light' | 'dark' | 'auto'
3842
editor: EditorSettings
43+
markdown: MarkdownSettings
3944
}
4045

4146
export interface Store {

src/renderer/components/editor/Editor.vue

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
useSnippetUpdate,
88
} from '@/composables'
99
import { i18n, ipc } from '@/electron'
10-
import { useClipboard, useDark, useDebounceFn } from '@vueuse/core'
10+
import { useClipboard, useCssVar, useDark, useDebounceFn } from '@vueuse/core'
1111
import CodeMirror from 'codemirror'
1212
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'radix-vue'
1313
import { EDITOR_DEFAULTS } from '~/main/store/constants'
@@ -30,20 +30,36 @@ const {
3030
selectedSnippetIds,
3131
isAvailableToCodePreview,
3232
} = useSnippets()
33-
const { isShowMarkdown, isShowMindmap, isShowCodePreview, isShowCodeImage }
34-
= useApp()
33+
const {
34+
isShowMarkdown,
35+
isShowMindmap,
36+
isShowCodePreview,
37+
isShowCodeImage,
38+
isShowMarkdownPresentation,
39+
} = useApp()
3540
3641
const { addToUpdateContentQueue } = useSnippetUpdate()
3742
3843
const isDark = useDark()
3944
let editor: CodeMirror.Editor | null = null
4045
41-
const editorRef = ref()
42-
const scrollBarOpacity = ref(1)
4346
const isProgrammaticChange = ref(false)
4447
45-
const fontSize = computed(() => `${settings.fontSize}px`)
46-
const fontFamily = computed(() => settings.fontFamily)
48+
const fontSize = useCssVar('--editor-font-size', document.body, {
49+
initialValue: `${settings.fontSize}px`,
50+
})
51+
52+
useCssVar('--editor-font-family', document.body, {
53+
initialValue: settings.fontFamily,
54+
})
55+
56+
const scrollBarOpacity = useCssVar(
57+
'--editor-scrollbar-opacity',
58+
document.body,
59+
{
60+
initialValue: '1',
61+
},
62+
)
4763
4864
const isShowHeader = computed(() => {
4965
return (
@@ -57,6 +73,7 @@ const isShowEditor = computed(() => {
5773
!isShowMarkdown.value
5874
&& !isShowMindmap.value
5975
&& !isShowCodeImage.value
76+
&& !isShowMarkdownPresentation.value
6077
&& !isEmpty.value
6178
&& selectedSnippetIds.value.length === 1
6279
)
@@ -82,11 +99,16 @@ function getCursorPosition() {
8299
}
83100
84101
const hideScrollbar = useDebounceFn(() => {
85-
scrollBarOpacity.value = 0
102+
scrollBarOpacity.value = '0'
86103
}, 1000)
87104
88105
async function init() {
89-
editor = CodeMirror(editorRef.value, {
106+
const el = document.getElementById('editor')
107+
108+
if (!el)
109+
return
110+
111+
editor = CodeMirror(el, {
90112
value: selectedSnippetContent.value?.value || ' ',
91113
mode: selectedSnippetContent.value?.language || 'plain_text',
92114
theme: isDark.value ? 'oceanic-next' : 'neo',
@@ -122,22 +144,25 @@ async function init() {
122144
editor.on('cursorActivity', getCursorPosition)
123145
124146
editor.on('scroll', () => {
125-
scrollBarOpacity.value = 1
147+
scrollBarOpacity.value = '1'
126148
editor?.setOption('scrollbarStyle', 'overlay')
127149
})
128150
129151
editor.on('scroll', hideScrollbar)
130152
131153
ipc.on('main-menu:font-size-increase', () => {
132154
settings.fontSize++
155+
fontSize.value = `${settings.fontSize}px`
133156
})
134157
135158
ipc.on('main-menu:font-size-decrease', () => {
136159
settings.fontSize--
160+
fontSize.value = `${settings.fontSize}px`
137161
})
138162
139163
ipc.on('main-menu:font-size-reset', () => {
140164
settings.fontSize = EDITOR_DEFAULTS.fontSize
165+
fontSize.value = `${settings.fontSize}px`
141166
})
142167
143168
ipc.on('main-menu:copy-snippet', () => {
@@ -277,12 +302,14 @@ onMounted(() => {
277302
<EditorHeader v-if="isShowHeader" />
278303
<SplitterGroup
279304
v-show="isShowEditor"
280-
id="editor"
281305
direction="vertical"
282306
class="overflow-auto"
283307
>
284308
<SplitterPanel as-child>
285-
<div ref="editorRef" />
309+
<div
310+
id="editor"
311+
data-editor-mount
312+
/>
286313
</SplitterPanel>
287314
<SplitterResizeHandle class="relative cursor-none">
288315
<UiGutter orientation="horizontal" />
@@ -318,9 +345,9 @@ onMounted(() => {
318345
<style>
319346
@reference '../../styles.css';
320347
.CodeMirror {
321-
font-size: v-bind(fontSize);
322-
font-family: v-bind(fontFamily);
323-
line-height: calc(v-bind(fontSize) * 1.5);
348+
font-size: var(--editor-font-size);
349+
font-family: var(--editor-font-family);
350+
line-height: calc(var(--editor-font-size) * 1.5);
324351
height: 100%;
325352
background-color: var(--color-bg) !important;
326353
}
@@ -345,14 +372,14 @@ onMounted(() => {
345372
.CodeMirror-overlayscroll-vertical div {
346373
background-color: var(--color-scrollbar);
347374
width: 7px;
348-
opacity: v-bind(scrollBarOpacity);
375+
opacity: var(--editor-scrollbar-opacity);
349376
transition: opacity 0.3s;
350377
}
351378
352379
.CodeMirror-overlayscroll-horizontal div {
353380
background-color: var(--color-scrollbar);
354381
height: 7px;
355-
opacity: v-bind(scrollBarOpacity);
382+
opacity: var(--editor-scrollbar-opacity);
356383
transition: opacity 0.3s;
357384
}
358385

src/renderer/components/editor/header/Header.vue

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
<script setup lang="ts">
22
import { useApp, useSnippets, useSnippetUpdate } from '@/composables'
33
import { i18n } from '@/electron'
4-
import { Code, Eye, GitFork, Image, Plus, Type } from 'lucide-vue-next'
4+
import {
5+
Code,
6+
Eye,
7+
GitFork,
8+
Image,
9+
Plus,
10+
Presentation,
11+
Type,
12+
} from 'lucide-vue-next'
13+
import { useRouter } from 'vue-router'
514
615
const {
716
selectedSnippet,
@@ -14,11 +23,14 @@ const {
1423
state,
1524
isShowMarkdown,
1625
isShowMindmap,
26+
isShowMarkdownPresentation,
1727
isShowCodePreview,
1828
isShowCodeImage,
1929
} = useApp()
2030
const { addToUpdateQueue } = useSnippetUpdate()
2131
32+
const router = useRouter()
33+
2234
const isShowDescription = ref(false)
2335
2436
const name = computed({
@@ -53,27 +65,41 @@ function onClickTab(index: number) {
5365
function onMarkdownToggle() {
5466
isShowMarkdown.value = !isShowMarkdown.value
5567
isShowMindmap.value = false
68+
isShowMarkdownPresentation.value = false
69+
isShowCodePreview.value = false
70+
isShowCodeImage.value = false
71+
}
72+
73+
function onMarkdownPresentationToggle() {
74+
isShowMarkdownPresentation.value = !isShowMarkdownPresentation.value
75+
isShowMarkdown.value = false
76+
isShowMindmap.value = false
5677
isShowCodePreview.value = false
5778
isShowCodeImage.value = false
79+
80+
router.push({ name: 'markdown-presentation' })
5881
}
5982
6083
function onMindmapToggle() {
6184
isShowMindmap.value = !isShowMindmap.value
6285
isShowMarkdown.value = false
86+
isShowMarkdownPresentation.value = false
6387
isShowCodePreview.value = false
6488
isShowCodeImage.value = false
6589
}
6690
6791
function onCodePreviewToggle() {
6892
isShowCodePreview.value = !isShowCodePreview.value
6993
isShowMarkdown.value = false
94+
isShowMarkdownPresentation.value = false
7095
isShowMindmap.value = false
7196
isShowCodeImage.value = false
7297
}
7398
7499
function onCodeImageToggle() {
75100
isShowCodeImage.value = !isShowCodeImage.value
76101
isShowMarkdown.value = false
102+
isShowMarkdownPresentation.value = false
77103
isShowMindmap.value = false
78104
isShowCodePreview.value = false
79105
}
@@ -111,6 +137,18 @@ function onCodeImageToggle() {
111137
>
112138
<Code class="h-3 w-3" />
113139
</UiActionButton>
140+
<UiActionButton
141+
v-if="isShowMarkdownAction"
142+
:tooltip="
143+
isShowMarkdownPresentation
144+
? i18n.t('action.hide')
145+
: i18n.t('menu:markdown.presentationMode')
146+
"
147+
:active="isShowMarkdownPresentation"
148+
@click="onMarkdownPresentationToggle"
149+
>
150+
<Presentation class="h-3 w-3" />
151+
</UiActionButton>
114152
<UiActionButton
115153
v-if="isShowMarkdownAction"
116154
:tooltip="

0 commit comments

Comments
 (0)