mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
added support for toggling smart quotes in preview
This commit is contained in:
@@ -279,6 +279,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
lineNumber={config.preview.lineNumber}
|
lineNumber={config.preview.lineNumber}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
scrollPastEnd={config.preview.scrollPastEnd}
|
scrollPastEnd={config.preview.scrollPastEnd}
|
||||||
|
smartQuotes={config.preview.smartQuotes}
|
||||||
ref='preview'
|
ref='preview'
|
||||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||||
onDoubleClick={(e) => this.handleDoubleClick(e)}
|
onDoubleClick={(e) => this.handleDoubleClick(e)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import markdown from 'browser/lib/markdown'
|
import Markdown from 'browser/lib/markdown'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import CodeMirror from 'codemirror'
|
import CodeMirror from 'codemirror'
|
||||||
import 'codemirror-mode-elixir'
|
import 'codemirror-mode-elixir'
|
||||||
@@ -130,6 +130,13 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
this.printHandler = () => this.handlePrint()
|
this.printHandler = () => this.handlePrint()
|
||||||
|
|
||||||
this.linkClickHandler = this.handlelinkClick.bind(this)
|
this.linkClickHandler = this.handlelinkClick.bind(this)
|
||||||
|
this.initMarkdown = this.initMarkdown.bind(this)
|
||||||
|
this.initMarkdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
initMarkdown () {
|
||||||
|
const { smartQuotes } = this.props
|
||||||
|
this.markdown = new Markdown({ typographer: smartQuotes })
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewAnchorClick (e) {
|
handlePreviewAnchorClick (e) {
|
||||||
@@ -198,7 +205,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams()
|
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams()
|
||||||
|
|
||||||
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber)
|
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber)
|
||||||
const body = markdown.render(noteContent)
|
const body = this.markdown.render(noteContent)
|
||||||
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
||||||
|
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
@@ -309,6 +316,10 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
if (prevProps.value !== this.props.value) this.rewriteIframe()
|
if (prevProps.value !== this.props.value) this.rewriteIframe()
|
||||||
|
if (prevProps.smartQuotes !== this.props.smartQuotes) {
|
||||||
|
this.initMarkdown()
|
||||||
|
this.rewriteIframe()
|
||||||
|
}
|
||||||
if (prevProps.fontFamily !== this.props.fontFamily ||
|
if (prevProps.fontFamily !== this.props.fontFamily ||
|
||||||
prevProps.fontSize !== this.props.fontSize ||
|
prevProps.fontSize !== this.props.fontSize ||
|
||||||
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
|
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
|
||||||
@@ -374,7 +385,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
|
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value)
|
this.refs.root.contentWindow.document.body.innerHTML = this.markdown.render(value)
|
||||||
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
|
||||||
this.fixDecodedURI(el)
|
this.fixDecodedURI(el)
|
||||||
@@ -390,9 +401,9 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
})
|
})
|
||||||
|
|
||||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
|
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
|
||||||
el.src = markdown.normalizeLinkText(el.src)
|
el.src = this.markdown.normalizeLinkText(el.src)
|
||||||
if (!/\/:storage/.test(el.src)) return
|
if (!/\/:storage/.test(el.src)) return
|
||||||
el.src = `file:///${markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
|
el.src = `file:///${this.markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
|
||||||
})
|
})
|
||||||
|
|
||||||
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
|
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
|
||||||
@@ -533,5 +544,6 @@ MarkdownPreview.propTypes = {
|
|||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
showCopyNotification: PropTypes.bool,
|
showCopyNotification: PropTypes.bool,
|
||||||
storagePath: PropTypes.string
|
storagePath: PropTypes.string,
|
||||||
|
smartQuotes: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
codeBlockFontFamily={config.editor.fontFamily}
|
codeBlockFontFamily={config.editor.fontFamily}
|
||||||
lineNumber={config.preview.lineNumber}
|
lineNumber={config.preview.lineNumber}
|
||||||
scrollPastEnd={config.preview.scrollPastEnd}
|
scrollPastEnd={config.preview.scrollPastEnd}
|
||||||
|
smartQuotes={config.preview.smartQuotes}
|
||||||
ref='preview'
|
ref='preview'
|
||||||
tabInde='0'
|
tabInde='0'
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ function createGutter (str, firstLineNumber) {
|
|||||||
return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
|
return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
|
||||||
}
|
}
|
||||||
|
|
||||||
var md = markdownit({
|
class Markdown {
|
||||||
|
constructor (options = {}) {
|
||||||
|
const defaultOptions = {
|
||||||
typographer: true,
|
typographer: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
html: true,
|
html: true,
|
||||||
@@ -45,11 +47,14 @@ var md = markdownit({
|
|||||||
str +
|
str +
|
||||||
'</code></pre>'
|
'</code></pre>'
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
md.use(emoji, {
|
|
||||||
|
const updatedOptions = Object.assign(defaultOptions, options)
|
||||||
|
this.md = markdownit(updatedOptions)
|
||||||
|
this.md.use(emoji, {
|
||||||
shortcuts: {}
|
shortcuts: {}
|
||||||
})
|
})
|
||||||
md.use(math, {
|
this.md.use(math, {
|
||||||
inlineOpen: config.preview.latexInlineOpen,
|
inlineOpen: config.preview.latexInlineOpen,
|
||||||
inlineClose: config.preview.latexInlineClose,
|
inlineClose: config.preview.latexInlineClose,
|
||||||
blockOpen: config.preview.latexBlockOpen,
|
blockOpen: config.preview.latexBlockOpen,
|
||||||
@@ -72,22 +77,22 @@ md.use(math, {
|
|||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
md.use(require('markdown-it-imsize'))
|
this.md.use(require('markdown-it-imsize'))
|
||||||
md.use(require('markdown-it-footnote'))
|
this.md.use(require('markdown-it-footnote'))
|
||||||
md.use(require('markdown-it-multimd-table'))
|
this.md.use(require('markdown-it-multimd-table'))
|
||||||
md.use(require('markdown-it-named-headers'), {
|
this.md.use(require('markdown-it-named-headers'), {
|
||||||
slugify: (header) => {
|
slugify: (header) => {
|
||||||
return encodeURI(header.trim()
|
return encodeURI(header.trim()
|
||||||
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
|
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
|
||||||
.replace(/\s+/g, '-'))
|
.replace(/\s+/g, '-'))
|
||||||
.replace(/\-+$/, '')
|
.replace(/\-+$/, '')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
md.use(require('markdown-it-kbd'))
|
this.md.use(require('markdown-it-kbd'))
|
||||||
|
|
||||||
const deflate = require('markdown-it-plantuml/lib/deflate')
|
const deflate = require('markdown-it-plantuml/lib/deflate')
|
||||||
md.use(require('markdown-it-plantuml'), '', {
|
this.md.use(require('markdown-it-plantuml'), '', {
|
||||||
generateSource: function (umlCode) {
|
generateSource: function (umlCode) {
|
||||||
const s = unescape(encodeURIComponent(umlCode))
|
const s = unescape(encodeURIComponent(umlCode))
|
||||||
const zippedCode = deflate.encode64(
|
const zippedCode = deflate.encode64(
|
||||||
@@ -95,10 +100,10 @@ md.use(require('markdown-it-plantuml'), '', {
|
|||||||
)
|
)
|
||||||
return `http://www.plantuml.com/plantuml/svg/${zippedCode}`
|
return `http://www.plantuml.com/plantuml/svg/${zippedCode}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Override task item
|
// Override task item
|
||||||
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
||||||
let content, terminate, i, l, token
|
let content, terminate, i, l, token
|
||||||
let nextLine = startLine + 1
|
let nextLine = startLine + 1
|
||||||
const terminatorRules = state.md.block.ruler.getRules('paragraph')
|
const terminatorRules = state.md.block.ruler.getRules('paragraph')
|
||||||
@@ -153,11 +158,11 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
|||||||
token = state.push('paragraph_close', 'p', -1)
|
token = state.push('paragraph_close', 'p', -1)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add line number attribute for scrolling
|
// Add line number attribute for scrolling
|
||||||
const originalRender = md.renderer.render
|
const originalRender = this.md.renderer.render
|
||||||
md.renderer.render = function render (tokens, options, env) {
|
this.md.renderer.render = (tokens, options, env) => {
|
||||||
tokens.forEach((token) => {
|
tokens.forEach((token) => {
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case 'heading_open':
|
case 'heading_open':
|
||||||
@@ -167,23 +172,22 @@ md.renderer.render = function render (tokens, options, env) {
|
|||||||
token.attrPush(['data-line', token.map[0]])
|
token.attrPush(['data-line', token.map[0]])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const result = originalRender.call(md.renderer, tokens, options, env)
|
const result = originalRender.call(this.md.renderer, tokens, options, env)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
// FIXME We should not depend on global variable.
|
// FIXME We should not depend on global variable.
|
||||||
window.md = md
|
window.md = this.md
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeLinkText (linkText) {
|
render (content) {
|
||||||
return md.normalizeLinkText(linkText)
|
|
||||||
}
|
|
||||||
|
|
||||||
const markdown = {
|
|
||||||
render: function markdown (content) {
|
|
||||||
if (!_.isString(content)) content = ''
|
if (!_.isString(content)) content = ''
|
||||||
const renderedContent = md.render(content)
|
return this.md.render(content)
|
||||||
return renderedContent
|
}
|
||||||
},
|
|
||||||
normalizeLinkText
|
normalizeLinkText (linkText) {
|
||||||
|
return this.md.normalizeLinkText(linkText)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default markdown
|
export default Markdown
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
latexInlineClose: '$',
|
latexInlineClose: '$',
|
||||||
latexBlockOpen: '$$',
|
latexBlockOpen: '$$',
|
||||||
latexBlockClose: '$$',
|
latexBlockClose: '$$',
|
||||||
scrollPastEnd: false
|
scrollPastEnd: false,
|
||||||
|
smartQuotes: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ class UiTab extends React.Component {
|
|||||||
latexInlineClose: this.refs.previewLatexInlineClose.value,
|
latexInlineClose: this.refs.previewLatexInlineClose.value,
|
||||||
latexBlockOpen: this.refs.previewLatexBlockOpen.value,
|
latexBlockOpen: this.refs.previewLatexBlockOpen.value,
|
||||||
latexBlockClose: this.refs.previewLatexBlockClose.value,
|
latexBlockClose: this.refs.previewLatexBlockClose.value,
|
||||||
scrollPastEnd: this.refs.previewScrollPastEnd.checked
|
scrollPastEnd: this.refs.previewScrollPastEnd.checked,
|
||||||
|
smartQuotes: this.refs.previewSmartQuotes.checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +403,16 @@ class UiTab extends React.Component {
|
|||||||
Show line numbers for preview code blocks
|
Show line numbers for preview code blocks
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div styleName='group-checkBoxSection'>
|
||||||
|
<label>
|
||||||
|
<input onChange={(e) => this.handleUIChange(e)}
|
||||||
|
checked={this.state.config.preview.smartQuotes}
|
||||||
|
ref='previewSmartQuotes'
|
||||||
|
type='checkbox'
|
||||||
|
/>
|
||||||
|
Enable smart quotes
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div styleName='group-section'>
|
<div styleName='group-section'>
|
||||||
<div styleName='group-section-label'>
|
<div styleName='group-section-label'>
|
||||||
LaTeX Inline Open Delimiter
|
LaTeX Inline Open Delimiter
|
||||||
|
|||||||
Reference in New Issue
Block a user