mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 17:31:26 +08:00
parent
ecc23a9cb0
commit
c959d185b2
@ -67,7 +67,7 @@
|
||||
"katex": "^0.15.2",
|
||||
"keyboard-layout": "^2.0.17",
|
||||
"keytar": "^7.7.0",
|
||||
"mermaid": "8.8.4",
|
||||
"mermaid": "^8.13.10",
|
||||
"minizlib": "^2.1.1",
|
||||
"plist": "^3.0.4",
|
||||
"popper.js": "^1.16.1",
|
||||
@ -78,9 +78,9 @@
|
||||
"turndown": "^7.1.1",
|
||||
"underscore": "^1.13.2",
|
||||
"unsplash-js": "^7.0.15",
|
||||
"vega": "^5.17.3",
|
||||
"vega-embed": "^6.14.2",
|
||||
"vega-lite": "^4.17.0",
|
||||
"vega": "^5.21.0",
|
||||
"vega-embed": "^6.20.5",
|
||||
"vega-lite": "^5.2.0",
|
||||
"vscode-ripgrep": "^1.12.1",
|
||||
"vue": "^2.6.14",
|
||||
"vue-electron": "^1.0.6",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import loadRenderer from '../../renderers'
|
||||
import { CLASS_OR_ID } from '../../config'
|
||||
import { conflict, mixins, camelToSnake } from '../../utils'
|
||||
import { CLASS_OR_ID, PREVIEW_DOMPURIFY_CONFIG } from '../../config'
|
||||
import { conflict, mixins, camelToSnake, sanitize } from '../../utils'
|
||||
import { patch, toVNode, toHTML, h, addNStoVNodeSvgChildren } from './snabbdom'
|
||||
import { beginRules } from '../rules'
|
||||
import renderInlines from './renderInlines'
|
||||
@ -99,6 +99,7 @@ class StateRender {
|
||||
if (this.mermaidCache.size) {
|
||||
const mermaid = await loadRenderer('mermaid')
|
||||
mermaid.initialize({
|
||||
securityLevel: 'strict',
|
||||
theme: this.muya.options.mermaidTheme
|
||||
})
|
||||
for (const [key, value] of this.mermaidCache.entries()) {
|
||||
@ -109,7 +110,7 @@ class StateRender {
|
||||
}
|
||||
try {
|
||||
mermaid.parse(code)
|
||||
target.innerHTML = code
|
||||
target.innerHTML = sanitize(code, PREVIEW_DOMPURIFY_CONFIG, true)
|
||||
mermaid.init(undefined, target)
|
||||
} catch (err) {
|
||||
target.innerHTML = '< Invalid Mermaid Codes >'
|
||||
|
@ -38,18 +38,20 @@ class ExportHtml {
|
||||
for (const code of codes) {
|
||||
const preEle = code.parentNode
|
||||
const mermaidContainer = document.createElement('div')
|
||||
mermaidContainer.innerHTML = code.innerHTML
|
||||
mermaidContainer.innerHTML = sanitize(unescapeHtml(code.innerHTML), EXPORT_DOMPURIFY_CONFIG, true)
|
||||
mermaidContainer.classList.add('mermaid')
|
||||
preEle.replaceWith(mermaidContainer)
|
||||
}
|
||||
const mermaid = await loadRenderer('mermaid')
|
||||
// We only export light theme, so set mermaid theme to `default`, in the future, we can choose whick theme to export.
|
||||
mermaid.initialize({
|
||||
securityLevel: 'strict',
|
||||
theme: 'default'
|
||||
})
|
||||
mermaid.init(undefined, this.exportContainer.querySelectorAll('div.mermaid'))
|
||||
if (this.muya) {
|
||||
mermaid.initialize({
|
||||
securityLevel: 'strict',
|
||||
theme: this.muya.options.mermaidTheme
|
||||
})
|
||||
}
|
||||
|
@ -220,7 +220,8 @@
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import fs from 'fs/promises'
|
||||
import fs from 'fs'
|
||||
import fsPromises from 'fs/promises'
|
||||
import path from 'path'
|
||||
import { isDirectory, isFile } from 'common/filesystem'
|
||||
import bus from '../../bus'
|
||||
@ -420,7 +421,7 @@ export default {
|
||||
const fullname = path.join(themeDir, filename)
|
||||
if (/.+\.css$/i.test(filename) && isFile(fullname)) {
|
||||
try {
|
||||
const content = await fs.readFile(fullname, 'utf8')
|
||||
const content = await fsPromises.readFile(fullname, 'utf8')
|
||||
|
||||
// Match comment with theme name in first line only.
|
||||
const match = content.match(/^(?:\/\*+[ \t]*([A-z0-9 -]+)[ \t]*(?:\*+\/|[\n\r])?)/)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import template from './index.html'
|
||||
import { getUniqueId } from '../../util'
|
||||
import { sanitize, EXPORT_DOMPURIFY_CONFIG } from '../../util/dompurify'
|
||||
import './index.css'
|
||||
|
||||
const INON_HASH = {
|
||||
@ -15,6 +16,13 @@ const TYPE_HASH = {
|
||||
info: 'mt-info'
|
||||
}
|
||||
|
||||
const fillTemplate = (type, title, message) => {
|
||||
return template
|
||||
.replace(/\{\{icon\}\}/, INON_HASH[type])
|
||||
.replace(/\{\{title\}\}/, sanitize(title, EXPORT_DOMPURIFY_CONFIG))
|
||||
.replace(/\{\{message\}\}/, sanitize(message, EXPORT_DOMPURIFY_CONFIG))
|
||||
}
|
||||
|
||||
const notification = {
|
||||
name: 'notify',
|
||||
noticeCache: {},
|
||||
@ -36,10 +44,7 @@ const notification = {
|
||||
const id = getUniqueId()
|
||||
|
||||
const fragment = document.createElement('div')
|
||||
fragment.innerHTML = template
|
||||
.replace(/\{\{icon\}\}/, INON_HASH[type])
|
||||
.replace(/\{\{title\}\}/, title)
|
||||
.replace(/\{\{message\}\}/, message)
|
||||
fragment.innerHTML = fillTemplate(type, title, message)
|
||||
|
||||
const noticeContainer = fragment.querySelector('.mt-notification')
|
||||
const bgNotice = noticeContainer.querySelector('.notice-bg')
|
||||
|
32
src/renderer/util/dompurify.js
Normal file
32
src/renderer/util/dompurify.js
Normal file
@ -0,0 +1,32 @@
|
||||
import runSanitize from 'muya/lib/utils/dompurify'
|
||||
|
||||
export const PREVIEW_DOMPURIFY_CONFIG = Object.freeze({
|
||||
FORBID_ATTR: ['style', 'contenteditable'],
|
||||
ALLOW_DATA_ATTR: false,
|
||||
USE_PROFILES: {
|
||||
html: true,
|
||||
svg: true,
|
||||
svgFilters: true,
|
||||
mathMl: false
|
||||
},
|
||||
RETURN_TRUSTED_TYPE: false
|
||||
})
|
||||
|
||||
export const EXPORT_DOMPURIFY_CONFIG = Object.freeze({
|
||||
FORBID_ATTR: ['contenteditable'],
|
||||
ALLOW_DATA_ATTR: false,
|
||||
ADD_ATTR: ['data-align'],
|
||||
USE_PROFILES: {
|
||||
html: true,
|
||||
svg: true,
|
||||
svgFilters: true,
|
||||
mathMl: false
|
||||
},
|
||||
RETURN_TRUSTED_TYPE: false,
|
||||
// Allow "file" protocol to export images on Windows (#1997).
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|file):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
|
||||
})
|
||||
|
||||
export const sanitize = (html, purifyOptions) => {
|
||||
return runSanitize(html, purifyOptions)
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import Slugger from 'muya/lib/parser/marked/slugger'
|
||||
import sanitize from 'muya/lib/utils/dompurify'
|
||||
import { isFile } from 'common/filesystem'
|
||||
import { escapeHtml, unescapeHtml } from 'muya/lib/utils'
|
||||
import academicTheme from '@/assets/themes/export/academic.theme.css'
|
||||
import liberTheme from '@/assets/themes/export/liber.theme.css'
|
||||
import { cloneObj } from '../util'
|
||||
import { sanitize, EXPORT_DOMPURIFY_CONFIG } from '../util/dompurify'
|
||||
|
||||
export const getCssForOptions = options => {
|
||||
const {
|
||||
@ -130,17 +130,6 @@ export const getHtmlToc = (toc, options = {}) => {
|
||||
return sanitize(html, EXPORT_DOMPURIFY_CONFIG)
|
||||
}
|
||||
|
||||
const EXPORT_DOMPURIFY_CONFIG = {
|
||||
FORBID_ATTR: ['contenteditable'],
|
||||
ALLOW_DATA_ATTR: false,
|
||||
USE_PROFILES: {
|
||||
html: true,
|
||||
svg: true,
|
||||
svgFilters: true,
|
||||
mathMl: true
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use "Noto Color Emoji" because it will result in PDF files with multiple MB and weird looking emojis.
|
||||
const FALLBACK_FONT_FAMILIES = '"Open Sans","Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user