From 8eb7e57203c0206df508a6d9f1dfcf0d6b2a0127 Mon Sep 17 00:00:00 2001 From: Marco Crapts Date: Sat, 13 May 2023 08:09:52 +0200 Subject: [PATCH] update note content --- package-lock.json | 40 ++++++++++++++++++++++++++++++ package.json | 2 ++ src/components/Note.vue | 11 +++++--- src/components/Note/NoteEditor.vue | 11 +++++--- src/composables/useNotes.ts | 1 + src/utils/markdown.ts | 28 ++++++++++++++++++++- 6 files changed, 85 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1fbc02..efccb48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,12 +31,14 @@ "lodash-es": "^4.17.21", "marked": "^4.3.0", "shortid": "^2.2.16", + "turndown": "^7.1.2", "vue": "^3.3.1" }, "devDependencies": { "@types/dompurify": "^3.0.2", "@types/lodash-es": "^4.17.7", "@types/shortid": "^0.0.29", + "@types/turndown": "^5.0.1", "@vitejs/plugin-vue": "^4.2.2", "autoprefixer": "^10.4.14", "postcss": "^8.4.23", @@ -1545,6 +1547,12 @@ "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "dev": true }, + "node_modules/@types/turndown": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.1.tgz", + "integrity": "sha512-N8Ad4e3oJxh9n9BiZx9cbe/0M3kqDpOTm2wzj13wdDUxDPjfjloWIJaquZzWE1cYTAHpjOH3rcTnXQdpEfS/SQ==", + "dev": true + }, "node_modules/@types/web-bluetooth": { "version": "0.0.17", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", @@ -2255,6 +2263,11 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" + }, "node_modules/dompurify": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.2.tgz", @@ -3585,6 +3598,14 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, + "node_modules/turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "dependencies": { + "domino": "^2.1.6" + } + }, "node_modules/typescript": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", @@ -5021,6 +5042,12 @@ "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", "dev": true }, + "@types/turndown": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.1.tgz", + "integrity": "sha512-N8Ad4e3oJxh9n9BiZx9cbe/0M3kqDpOTm2wzj13wdDUxDPjfjloWIJaquZzWE1cYTAHpjOH3rcTnXQdpEfS/SQ==", + "dev": true + }, "@types/web-bluetooth": { "version": "0.0.17", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", @@ -5518,6 +5545,11 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" + }, "dompurify": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.2.tgz", @@ -6392,6 +6424,14 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, + "turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "requires": { + "domino": "^2.1.6" + } + }, "typescript": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", diff --git a/package.json b/package.json index f519fda..4b9fe37 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,14 @@ "lodash-es": "^4.17.21", "marked": "^4.3.0", "shortid": "^2.2.16", + "turndown": "^7.1.2", "vue": "^3.3.1" }, "devDependencies": { "@types/dompurify": "^3.0.2", "@types/lodash-es": "^4.17.7", "@types/shortid": "^0.0.29", + "@types/turndown": "^5.0.1", "@vitejs/plugin-vue": "^4.2.2", "autoprefixer": "^10.4.14", "postcss": "^8.4.23", diff --git a/src/components/Note.vue b/src/components/Note.vue index 40e5a98..61bb1d3 100644 --- a/src/components/Note.vue +++ b/src/components/Note.vue @@ -20,13 +20,15 @@ const emit = defineEmits<{ const noteTitle = ref(props.note.title) watch(noteTitle, () => { - const updatedNote: Note = { - ...props.note, - title: noteTitle.value, - } + const updatedNote: Note = { ...props.note, title: noteTitle.value } emit('update', updatedNote) }) +const updateNoteContent = (content: string) => { + const updatedNote: Note = { ...props.note, content } + emit('update', updatedNote) +} + const references = computed(() => { const relations = notesRelations.value[props.note.id] return relations @@ -67,6 +69,7 @@ const setRoot = async (closeModal: () => Promise) => { diff --git a/src/components/Note/NoteEditor.vue b/src/components/Note/NoteEditor.vue index 2c4829e..112cd22 100644 --- a/src/components/Note/NoteEditor.vue +++ b/src/components/Note/NoteEditor.vue @@ -12,16 +12,20 @@ import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph' import ListPlugin from '@ckeditor/ckeditor5-list/src/list' import AutoformatPlugin from '@ckeditor/ckeditor5-autoformat/src/autoformat' import ContextedPlugin from '@/ckeditor/ContextedPlugin' -import { mdToHtml } from '@/utils/markdown' +import { mdToHtml, htmlToMd } from '@/utils/markdown' import { getNoteByTitle, setActiveNote } from '@/composables/useNotes' import Autocomplete from '@/components/Autocomplete.vue' const props = defineProps<{ note: Note }>() +const emit = defineEmits<{ + update: [mdText: string] +}>() + const html = mdToHtml(props.note.content) const editor = BalloonEditor -const editorData = html +const editorData = ref(html) const editorConfig = { plugins: [ EssentialsPlugin, @@ -36,7 +40,6 @@ const editorConfig = { AutoformatPlugin, ContextedPlugin, ], - toolbar: { items: [ 'bold', @@ -51,9 +54,11 @@ const editorConfig = { 'numberedList', ], }, + placeholder: 'Click here to start typing...', } const editorElement = ref(null) +watch(editorData, () => emit('update', htmlToMd(editorData.value))) const handleClick = ({ data }: { data: any }) => { const noteTitle = data.domTarget.textContent as string diff --git a/src/composables/useNotes.ts b/src/composables/useNotes.ts index ced2cfd..509eaa0 100644 --- a/src/composables/useNotes.ts +++ b/src/composables/useNotes.ts @@ -91,6 +91,7 @@ export const updateNote = (noteId: string, note: BaseNote) => { modified: new Date().getTime(), } baseNotes[noteId] = updatedNote + console.log(note) } export const addNote = (title: string, content: string, goToNote: boolean = false) => { diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts index 3cabb92..01bda81 100644 --- a/src/utils/markdown.ts +++ b/src/utils/markdown.ts @@ -1,7 +1,8 @@ import { marked } from 'marked' import DOMPurify from 'dompurify' +import Turndown from 'turndown' -export function mdToHtml(mdText: string) { +export function mdToHtml(mdText: string): string { const renderer = new marked.Renderer() const html = DOMPurify.sanitize(marked.parse(mdText, { renderer })) @@ -14,3 +15,28 @@ export function mdToHtml(mdText: string) { }) return doc.body.innerHTML } + +export function htmlToMd(htmlText: string): string { + const turndown = new Turndown({ headingStyle: 'atx' }) + const escapes = [ + [/\\/g, '\\\\'], + [/\*/g, '\\*'], + [/^-/g, '\\-'], + [/^\+ /g, '\\+ '], + [/^(=+)/g, '\\$1'], + [/^(#{1,6}) /g, '\\$1 '], + [/`/g, '\\`'], + [/^~~~/g, '\\~~~'], + // [/\[/g, '\\['], + // [/\]/g, '\\]'], + [/^>/g, '\\>'], + [/_/g, '\\_'], + [/^(\d+)\. /g, '$1\\. '], + ] + turndown.escape = (string) => + escapes.reduce((accumulator, escape) => { + return accumulator.replace(escape[0], escape[1] as string) + }, string) + const md = turndown.turndown(htmlText) + return md +}