mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
CommonMark
This commit is contained in:
@@ -1,16 +1,34 @@
|
|||||||
var React = require('react')
|
import React, { PropTypes } from 'react'
|
||||||
var { PropTypes } = React
|
|
||||||
import markdown from '../lib/markdown'
|
import markdown from '../lib/markdown'
|
||||||
var ReactDOM = require('react-dom')
|
import ReactDOM from 'react-dom'
|
||||||
|
import sanitizeHtml from '@rokt33r/sanitize-html'
|
||||||
|
import hljs from 'highlight.js'
|
||||||
|
|
||||||
const electron = require('electron')
|
const electron = require('electron')
|
||||||
const shell = electron.shell
|
const shell = electron.shell
|
||||||
|
|
||||||
const katex = window.katex
|
const katex = window.katex
|
||||||
|
|
||||||
|
const sanitizeOpts = {
|
||||||
|
allowedTags: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
|
||||||
|
'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
|
||||||
|
'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'img', 'span', 'cite', 'del', 'u' ],
|
||||||
|
allowedClasses: {
|
||||||
|
'a': ['lineAnchor'],
|
||||||
|
'div': ['math'],
|
||||||
|
'span': ['math', 'hljs-*'],
|
||||||
|
'code': ['language-*']
|
||||||
|
},
|
||||||
|
allowedAttributes: {
|
||||||
|
a: ['href', 'data-key'],
|
||||||
|
img: [ 'src' ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleAnchorClick (e) {
|
function handleAnchorClick (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
console.log(e.target.href)
|
||||||
shell.openExternal(e.target.href)
|
shell.openExternal(e.target.href)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,11 +52,13 @@ function math2Katex (display) {
|
|||||||
export default class MarkdownPreview extends React.Component {
|
export default class MarkdownPreview extends React.Component {
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this.addListener()
|
this.addListener()
|
||||||
|
// this.renderCode()
|
||||||
this.renderMath()
|
this.renderMath()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate () {
|
||||||
this.addListener()
|
this.addListener()
|
||||||
|
// this.renderCode()
|
||||||
this.renderMath()
|
this.renderMath()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +70,20 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
this.removeListener()
|
this.removeListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderCode () {
|
||||||
|
let codes = ReactDOM.findDOMNode(this).querySelectorAll('code:not(.rendered)')
|
||||||
|
Array.prototype.forEach.call(codes, el => {
|
||||||
|
let matched = el.className.match(/language-(.+)/)
|
||||||
|
let lang = matched ? matched[1] : null
|
||||||
|
try {
|
||||||
|
let result = hljs.highlight(lang, el.innerHTML)
|
||||||
|
el.innerHTML = result.value
|
||||||
|
el.className += ' rendered'
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
renderMath () {
|
renderMath () {
|
||||||
let inline = ReactDOM.findDOMNode(this).querySelectorAll('span.math')
|
let inline = ReactDOM.findDOMNode(this).querySelectorAll('span.math')
|
||||||
Array.prototype.forEach.call(inline, math2Katex(false))
|
Array.prototype.forEach.call(inline, math2Katex(false))
|
||||||
@@ -112,6 +146,9 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
let content = isEmpty
|
let content = isEmpty
|
||||||
? '(Empty content)'
|
? '(Empty content)'
|
||||||
: this.props.content
|
: this.props.content
|
||||||
|
content = markdown(content)
|
||||||
|
content = sanitizeHtml(content, sanitizeOpts)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={'MarkdownPreview' + (this.props.className != null ? ' ' + this.props.className : '') + (isEmpty ? ' empty' : '')}
|
className={'MarkdownPreview' + (this.props.className != null ? ' ' + this.props.className : '') + (isEmpty ? ' empty' : '')}
|
||||||
@@ -120,7 +157,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
onMouseDown={e => this.handleMouseDown(e)}
|
onMouseDown={e => this.handleMouseDown(e)}
|
||||||
onMouseMove={e => this.handleMouseMove(e)}
|
onMouseMove={e => this.handleMouseMove(e)}
|
||||||
onMouseUp={e => this.handleMouseUp(e)}
|
onMouseUp={e => this.handleMouseUp(e)}
|
||||||
dangerouslySetInnerHTML={{__html: ' ' + markdown(content)}}
|
dangerouslySetInnerHTML={{__html: ' ' + content}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
import markdownit from 'markdown-it'
|
import markdownit from 'markdown-it'
|
||||||
import hljs from 'highlight.js'
|
|
||||||
import emoji from 'markdown-it-emoji'
|
import emoji from 'markdown-it-emoji'
|
||||||
import math from 'markdown-it-math'
|
import math from 'markdown-it-math'
|
||||||
|
import hljs from 'highlight.js'
|
||||||
|
|
||||||
var md = markdownit({
|
var md = markdownit({
|
||||||
typographer: true,
|
typographer: true,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
|
html: true,
|
||||||
|
xhtmlOut: true,
|
||||||
highlight: function (str, lang) {
|
highlight: function (str, lang) {
|
||||||
if (lang && hljs.getLanguage(lang)) {
|
if (lang && hljs.getLanguage(lang)) {
|
||||||
try {
|
try {
|
||||||
return hljs.highlight(lang, str).value
|
return hljs.highlight(lang, str).value
|
||||||
} catch (__) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
return str
|
||||||
try {
|
|
||||||
return hljs.highlightAuto(str).value
|
|
||||||
} catch (__) {}
|
|
||||||
|
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
md.use(emoji)
|
md.use(emoji)
|
||||||
@@ -33,6 +30,7 @@ md.use(math, {
|
|||||||
let originalRenderToken = md.renderer.renderToken
|
let originalRenderToken = md.renderer.renderToken
|
||||||
md.renderer.renderToken = function renderToken (tokens, idx, options) {
|
md.renderer.renderToken = function renderToken (tokens, idx, options) {
|
||||||
let token = tokens[idx]
|
let token = tokens[idx]
|
||||||
|
|
||||||
let result = originalRenderToken.call(md.renderer, tokens, idx, options)
|
let result = originalRenderToken.call(md.renderer, tokens, idx, options)
|
||||||
if (token.map != null) {
|
if (token.map != null) {
|
||||||
return result + '<a class=\'lineAnchor\' data-key=\'' + token.map[0] + '\'></a>'
|
return result + '<a class=\'lineAnchor\' data-key=\'' + token.map[0] + '\'></a>'
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ export default class ArticleEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
if (this.props.mode === 'markdown' && this.state.status === PREVIEW_MODE) {
|
let showPreview = this.props.mode === 'markdown' && this.state.status === PREVIEW_MODE
|
||||||
|
if (showPreview) {
|
||||||
return (
|
return (
|
||||||
<div className='ArticleEditor'>
|
<div className='ArticleEditor'>
|
||||||
<MarkdownPreview
|
<MarkdownPreview
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ export default class ArticleNavigator extends React.Component {
|
|||||||
<div key={modifiedArticle.key} onClick={e => this.handleUnsavedItemClick(combinedArticle)(e)} className={className}>
|
<div key={modifiedArticle.key} onClick={e => this.handleUnsavedItemClick(combinedArticle)(e)} className={className}>
|
||||||
<div className='ArticleNavigator-unsaved-list-item-label'>
|
<div className='ArticleNavigator-unsaved-list-item-label'>
|
||||||
<ModeIcon mode={combinedArticle.mode}/>
|
<ModeIcon mode={combinedArticle.mode}/>
|
||||||
{combinedArticle.title}
|
{combinedArticle.title.trim().length > 0
|
||||||
|
? combinedArticle.title
|
||||||
|
: <span className='ArticleNavigator-unsaved-list-item-label-untitled'>(Untitled)</span>}
|
||||||
</div>
|
</div>
|
||||||
<button onClick={e => this.handleUncacheButtonClick(combinedArticle)(e)} className='ArticleNavigator-unsaved-list-item-discard-button'><i className='fa fa-times'/></button>
|
<button onClick={e => this.handleUncacheButtonClick(combinedArticle)(e)} className='ArticleNavigator-unsaved-list-item-discard-button'><i className='fa fa-times'/></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -144,9 +144,8 @@ function remap (state) {
|
|||||||
|
|
||||||
articles.sort((a, b) => {
|
articles.sort((a, b) => {
|
||||||
let match = new Date(b.updatedAt) - new Date(a.updatedAt)
|
let match = new Date(b.updatedAt) - new Date(a.updatedAt)
|
||||||
if (match === 0) match = new Date(b.createdAt) - new Date(a.createdAt)
|
if (match === 0) match = b.title.localeCompare(a.title)
|
||||||
if (match === 0) match = b.title.compare(a.title)
|
if (match === 0) match = b.key.localeCompare(a.key)
|
||||||
if (match === 0) match = b.key.compare(a.key)
|
|
||||||
return match
|
return match
|
||||||
})
|
})
|
||||||
let allArticles = articles.slice()
|
let allArticles = articles.slice()
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ marked()
|
|||||||
padding 0
|
padding 0
|
||||||
margin 0
|
margin 0
|
||||||
display inline
|
display inline
|
||||||
|
font-size 0
|
||||||
hr
|
hr
|
||||||
border-top none
|
border-top none
|
||||||
border-bottom solid 1px borderColor
|
border-bottom solid 1px borderColor
|
||||||
@@ -33,7 +34,7 @@ marked()
|
|||||||
h1, h2, h3, h4, h5, h6
|
h1, h2, h3, h4, h5, h6
|
||||||
margin 0 0 15px
|
margin 0 0 15px
|
||||||
font-weight 600
|
font-weight 600
|
||||||
* + h1, * + h2, * + h3, * + h4, * + h5, * + h6
|
*:not(a.lineAnchor) + h1, *:not(a.lineAnchor) + h2, *:not(a.lineAnchor) + h3, *:not(a.lineAnchor) + h4, *:not(a.lineAnchor) + h5, *:not(a.lineAnchor) + h6
|
||||||
margin-top 25px
|
margin-top 25px
|
||||||
h1
|
h1
|
||||||
font-size 2em
|
font-size 2em
|
||||||
@@ -55,26 +56,28 @@ marked()
|
|||||||
font-size 0.8em
|
font-size 0.8em
|
||||||
line-height 1em
|
line-height 1em
|
||||||
|
|
||||||
* + p, * + blockquote, * + ul, * + ol, * + pre
|
*:not(a.lineAnchor) + p, *:not(a.lineAnchor) + blockquote, *:not(a.lineAnchor) + ul, *:not(a.lineAnchor) + ol, *:not(a.lineAnchor) + pre
|
||||||
margin-top 15px
|
margin-top 15px
|
||||||
p
|
p
|
||||||
line-height 1.9em
|
line-height 1.9em
|
||||||
margin 0 0 15px
|
margin 0 0 15px
|
||||||
img
|
img
|
||||||
max-width 100%
|
max-width 100%
|
||||||
strong
|
strong, b
|
||||||
font-weight bold
|
font-weight bold
|
||||||
em
|
em, i
|
||||||
font-style italic
|
font-style italic
|
||||||
s
|
s
|
||||||
text-decoration line-through
|
text-decoration line-through
|
||||||
|
u
|
||||||
|
text-decoration underline
|
||||||
blockquote
|
blockquote
|
||||||
border-left solid 4px brandBorderColor
|
border-left solid 4px brandBorderColor
|
||||||
margin 0 0 15px
|
margin 0 0 15px
|
||||||
padding 0 25px
|
padding 0 25px
|
||||||
ul
|
ul
|
||||||
list-style-type disc
|
list-style-type disc
|
||||||
padding-left 35px
|
padding-left 25px
|
||||||
margin-bottom 15px
|
margin-bottom 15px
|
||||||
li
|
li
|
||||||
display list-item
|
display list-item
|
||||||
@@ -87,7 +90,7 @@ marked()
|
|||||||
list-style-type square
|
list-style-type square
|
||||||
ol
|
ol
|
||||||
list-style-type decimal
|
list-style-type decimal
|
||||||
padding-left 35px
|
padding-left 25px
|
||||||
margin-bottom 15px
|
margin-bottom 15px
|
||||||
li
|
li
|
||||||
display list-item
|
display list-item
|
||||||
@@ -97,18 +100,18 @@ marked()
|
|||||||
code
|
code
|
||||||
font-family Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace
|
font-family Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace
|
||||||
padding 2px 4px
|
padding 2px 4px
|
||||||
border solid 1px borderColor
|
border solid 1px alpha(borderColor, 0.3)
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
font-size 0.9em
|
font-size 0.9em
|
||||||
color black
|
color black
|
||||||
text-decoration none
|
text-decoration none
|
||||||
background-color #F6F6F6
|
background-color #F6F6F6
|
||||||
margin-right 2px
|
margin-right 2px
|
||||||
* + code
|
*:not(a.lineAnchor) + code
|
||||||
margin-left 2px
|
margin-left 2px
|
||||||
pre
|
pre
|
||||||
padding 5px
|
padding 5px
|
||||||
border solid 1px borderColor
|
border solid 1px alpha(borderColor, 0.3)
|
||||||
border-radius 5px
|
border-radius 5px
|
||||||
overflow-x auto
|
overflow-x auto
|
||||||
margin 0 0 15px
|
margin 0 0 15px
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"homepage": "https://github.com/Rokt33r/codexen-app#readme",
|
"homepage": "https://github.com/Rokt33r/codexen-app#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rokt33r/node-ipc": "^5.0.4",
|
"@rokt33r/node-ipc": "^5.0.4",
|
||||||
|
"@rokt33r/sanitize-html": "^1.11.2",
|
||||||
"devicon": "^2.0.0",
|
"devicon": "^2.0.0",
|
||||||
"electron-gh-releases": "^2.0.2",
|
"electron-gh-releases": "^2.0.2",
|
||||||
"font-awesome": "^4.3.0",
|
"font-awesome": "^4.3.0",
|
||||||
|
|||||||
BIN
resources/fonts/Lato-Regular.ttf
Normal file
BIN
resources/fonts/Lato-Regular.ttf
Normal file
Binary file not shown.
BIN
resources/fonts/Lato-Regular.woff
Normal file
BIN
resources/fonts/Lato-Regular.woff
Normal file
Binary file not shown.
BIN
resources/fonts/Lato-Regular.woff2
Normal file
BIN
resources/fonts/Lato-Regular.woff2
Normal file
Binary file not shown.
@@ -31,7 +31,8 @@ var config = {
|
|||||||
'highlight.js',
|
'highlight.js',
|
||||||
'markdown-it-emoji',
|
'markdown-it-emoji',
|
||||||
'fs-jetpack',
|
'fs-jetpack',
|
||||||
'markdown-it-math'
|
'markdown-it-math',
|
||||||
|
'@rokt33r/sanitize-html'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user