From df0af2a11f66a8e5fe85b15fecf7d0b6c948cbcf Mon Sep 17 00:00:00 2001 From: Dick Choi Date: Mon, 24 Oct 2016 18:05:01 +0900 Subject: [PATCH] Render codefence by codemirror rather than by hljs --- browser/components/CodeEditor.js | 10 +- browser/components/MarkdownEditor.js | 2 + browser/components/MarkdownPreview.js | 134 ++++++++++++------ browser/components/markdown.styl | 30 ++-- browser/finder/NoteDetail.js | 2 + browser/finder/NoteItem.js | 4 +- browser/lib/customMeta.js | 2 +- browser/lib/hljsThemes.js | 78 ---------- browser/lib/markdown.js | 20 +-- .../main/modals/PreferencesModal/ConfigTab.js | 6 +- lib/finder.html | 9 +- lib/main.html | 1 - package.json | 1 - webpack-skeleton.js | 1 - 14 files changed, 137 insertions(+), 163 deletions(-) delete mode 100644 browser/lib/hljsThemes.js diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index dde3d3c8..fe0b35b1 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -37,6 +37,9 @@ export default class CodeEditor extends React.Component { } this.props.onBlur != null && this.props.onBlur(e) } + this.loadStyleHandler = (e) => { + this.editor.refresh() + } } componentDidMount () { @@ -72,11 +75,16 @@ export default class CodeEditor extends React.Component { this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) + + let editorTheme = document.getElementById('editorTheme') + editorTheme.addEventListener('load', this.loadStyleHandler) } componentWillUnmount () { this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) + let editorTheme = document.getElementById('editorTheme') + editorTheme.removeEventListener('load', this.loadStyleHandler) } componentDidUpdate (prevProps, prevState) { @@ -86,7 +94,7 @@ export default class CodeEditor extends React.Component { } if (prevProps.theme !== this.props.theme) { this.editor.setOption('theme', this.props.theme) - needRefresh = true + // editor should be refreshed after css loaded } if (prevProps.fontSize !== this.props.fontSize) { needRefresh = true diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index f61a69e4..b92ddae3 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -114,6 +114,7 @@ class MarkdownEditor extends React.Component { render () { let { className, value, config } = this.props + let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 let editorIndentSize = parseInt(config.editor.indentSize, 10) @@ -153,6 +154,7 @@ class MarkdownEditor extends React.Component { codeBlockTheme={config.preview.codeBlockTheme} codeBlockFontFamily={config.editor.fontFamily} lineNumber={config.preview.lineNumber} + indentSize={editorIndentSize} ref='preview' onContextMenu={(e) => this.handleContextMenu(e)} tabIndex='0' diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index b052c765..b848c775 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -1,11 +1,45 @@ import React, { PropTypes } from 'react' import markdown from 'browser/lib/markdown' import _ from 'lodash' -import hljsTheme from 'browser/lib/hljsThemes' +import CodeMirror from 'codemirror' +import consts from 'browser/lib/consts' + +const { remote } = require('electron') +const { app } = remote +const path = require('path') const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1] -const { shell } = require('electron') +const appPath = 'file://' + (process.env.NODE_ENV === 'production' + ? app.getAppPath() + : path.resolve()) +function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber) { + return ` +@font-face { + font-family: 'Lato'; + src: url('${appPath}/resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */ + url('${appPath}/resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */ + url('${appPath}/resources/fonts/Lato-Regular.ttf') format('truetype'); + font-style: normal; + font-weight: normal; + text-rendering: optimizeLegibility; +} +${markdownStyle} +body { + font-family: ${fontFamily.join(', ')}; + font-size: ${fontSize}px; +} +code { + font-family: ${codeBlockFontFamily.join(', ')}; +} +.lineNumber { + ${lineNumber && 'display: block !important;'} + font-family: ${codeBlockFontFamily.join(', ')}; +} +` +} + +const { shell } = require('electron') const OSX = global.process.platform === 'darwin' const defaultFontFamily = ['helvetica', 'arial', 'sans-serif'] @@ -68,9 +102,17 @@ export default class MarkdownPreview extends React.Component { } componentDidMount () { - this.refs.root.setAttribute('sandbox', 'allow-same-origin') + this.refs.root.setAttribute('sandbox', 'allow-scripts') this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler) + + this.refs.root.contentWindow.document.head.innerHTML = ` + + + + + ` this.rewriteIframe() + this.applyStyle() this.refs.root.contentWindow.document.addEventListener('mousedown', this.mouseDownHandler) this.refs.root.contentWindow.document.addEventListener('mouseup', this.mouseUpHandler) @@ -83,14 +125,36 @@ export default class MarkdownPreview extends React.Component { } componentDidUpdate (prevProps) { - if (prevProps.value !== this.props.value || - prevProps.fontFamily !== this.props.fontFamily || + if (prevProps.value !== this.props.value) this.rewriteIframe() + if (prevProps.fontFamily !== this.props.fontFamily || prevProps.fontSize !== this.props.fontSize || prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily || prevProps.codeBlockTheme !== this.props.codeBlockTheme || prevProps.lineNumber !== this.props.lineNumber || - prevProps.theme !== this.props.theme - ) this.rewriteIframe() + prevProps.theme !== this.props.theme) { + this.applyStyle() + this.rewriteIframe() + } + } + + applyStyle () { + let { fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme } = this.props + fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0 + ? [fontFamily].concat(defaultFontFamily) + : defaultFontFamily + codeBlockFontFamily = _.isString(codeBlockFontFamily) && codeBlockFontFamily.trim().length > 0 + ? [codeBlockFontFamily].concat(defaultCodeBlockFontFamily) + : defaultCodeBlockFontFamily + + this.setCodeTheme(codeBlockTheme) + this.getWindow().document.getElementById('style').innerHTML = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber) + } + + setCodeTheme (theme) { + theme = consts.THEMES.some((_theme) => _theme === theme) + ? theme + : 'default' + this.getWindow().document.getElementById('codeTheme').href = `${appPath}/node_modules/codemirror/theme/${theme}.css` } rewriteIframe () { @@ -101,43 +165,7 @@ export default class MarkdownPreview extends React.Component { el.removeEventListener('click', this.checkboxClickHandler) }) - let { value, fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, theme } = this.props - fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0 - ? [fontFamily].concat(defaultFontFamily) - : defaultFontFamily - codeBlockFontFamily = _.isString(codeBlockFontFamily) && codeBlockFontFamily.trim().length > 0 - ? [codeBlockFontFamily].concat(defaultCodeBlockFontFamily) - : defaultCodeBlockFontFamily - codeBlockTheme = hljsTheme().some((theme) => theme.name === codeBlockTheme) ? codeBlockTheme : 'xcode' - - this.refs.root.contentWindow.document.head.innerHTML = ` - - - - ` + let { value, theme, indentSize, codeBlockTheme } = this.props this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) @@ -145,9 +173,27 @@ export default class MarkdownPreview extends React.Component { Array.prototype.forEach.call(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => { el.addEventListener('click', this.anchorClickHandler) }) + Array.prototype.forEach.call(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => { el.addEventListener('click', this.checkboxClickHandler) }) + + codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme) + ? codeBlockTheme + : 'default' + + Array.prototype.forEach.call(this.refs.root.contentWindow.document.querySelectorAll('.code code'), (el) => { + let syntax = CodeMirror.findModeByName(el.className) + if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') + CodeMirror.requireMode(syntax.mode, () => { + let content = el.innerHTML + el.innerHTML = '' + el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror` + CodeMirror.runMode(content, syntax.mime, el, { + tabSize: indentSize + }) + }) + }) } focus () { diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index e6f33b2c..f8ee6345 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -191,7 +191,7 @@ code padding 0.2em 0.4em background-color #f7f7f7 border-radius 3px - font-size 0.85em + font-size 1em text-decoration none margin-right 2px pre @@ -200,26 +200,36 @@ pre border-radius 5px overflow-x auto margin 0 0 1em - line-height 1.35 + display flex + line-height 1.4em + &.CodeMirror + height initial + &>code + flex 1 + overflow-x auto code - margin 0 background-color inherit + margin 0 padding 0 border none border-radius 0 - pre - border none - margin -5px &>span.lineNumber display none - float left - font-size 0.85em - margin 0 0.5em 0 -0.5em + font-size 1em + padding 0.5em 0 + margin -0.5em 0.5em -0.5em -0.5em border-right 1px solid text-align right + border-top-left-radius 4px + border-bottom-left-radius 4px + &.CodeMirror-gutters + position initial + top initial + left initial + min-height 0 !important &>span display block - padding 0 .5em 0 1em + padding 0 .5em 0 table display block width 100% diff --git a/browser/finder/NoteDetail.js b/browser/finder/NoteDetail.js index 68d3d5ab..2abaaa96 100644 --- a/browser/finder/NoteDetail.js +++ b/browser/finder/NoteDetail.js @@ -185,11 +185,13 @@ class NoteDetail extends React.Component { return ( ) diff --git a/browser/finder/NoteItem.js b/browser/finder/NoteItem.js index 7f2ff1d9..a940338c 100644 --- a/browser/finder/NoteItem.js +++ b/browser/finder/NoteItem.js @@ -50,8 +50,8 @@ class NoteItem extends React.Component {
{note.type === 'SNIPPET_NOTE' - ? - : + ? + : } {note.title.trim().length > 0 ? note.title diff --git a/browser/lib/customMeta.js b/browser/lib/customMeta.js index b2ed8123..e4724fac 100644 --- a/browser/lib/customMeta.js +++ b/browser/lib/customMeta.js @@ -1,4 +1,4 @@ import CodeMirror from 'codemirror' import _ from 'lodash' -CodeMirror.modeInfo.push({name: 'Stylus', mime: 'text/x-styl', mode: 'stylus', ext: ['styl']}) +CodeMirror.modeInfo.push({name: 'Stylus', mime: 'text/x-styl', mode: 'stylus', ext: ['styl'], alias: ['styl']}) diff --git a/browser/lib/hljsThemes.js b/browser/lib/hljsThemes.js deleted file mode 100644 index 3a288c47..00000000 --- a/browser/lib/hljsThemes.js +++ /dev/null @@ -1,78 +0,0 @@ -const hljsThemeList = [ - {caption: 'Default', name: 'default'}, - {caption: 'Agate', name: 'agate'}, - {caption: 'Androidstudio', name: 'androidstudio'}, - {caption: 'Arduino Light', name: 'arduino-light'}, - {caption: 'Arta', name: 'arta'}, - {caption: 'Ascetic', name: 'ascetic'}, - {caption: 'Atelier Cave Dark', name: 'atelier-cave-dark'}, - {caption: 'Atelier Cave Light', name: 'atelier-cave-light'}, - {caption: 'Atelier Dune Dark', name: 'atelier-dune-dark'}, - {caption: 'Atelier Dune Light', name: 'atelier-dune-light'}, - {caption: 'Atelier Estuary Dark', name: 'atelier-estuary-dark'}, - {caption: 'Atelier Estuary Light', name: 'atelier-estuary-light'}, - {caption: 'Atelier Forest Dark', name: 'atelier-forest-dark'}, - {caption: 'Atelier Forest Light', name: 'atelier-forest-light'}, - {caption: 'Atelier Heath Dark', name: 'atelier-heath-dark'}, - {caption: 'Atelier Heath Light', name: 'atelier-heath-light'}, - {caption: 'Atelier Lakeside Dark', name: 'atelier-lakeside-dark'}, - {caption: 'Atelier Lakeside Light', name: 'atelier-lakeside-light'}, - {caption: 'Atelier Plateau Dark', name: 'atelier-plateau-dark'}, - {caption: 'Atelier Plateau Light', name: 'atelier-plateau-light'}, - {caption: 'Atelier Savanna Dark', name: 'atelier-savanna-dark'}, - {caption: 'Atelier Savanna Light', name: 'atelier-savanna-light'}, - {caption: 'Atelier Seaside Dark', name: 'atelier-seaside-dark'}, - {caption: 'Atelier Seaside Light', name: 'atelier-seaside-light'}, - {caption: 'Atelier Sulphurpool Dark', name: 'atelier-sulphurpool-dark'}, - {caption: 'Atelier Sulphurpool Light', name: 'atelier-sulphurpool-light'}, - {caption: 'Brown Paper', name: 'brown-paper'}, - {caption: 'Codepen Embed', name: 'codepen-embed'}, - {caption: 'Color Brewer', name: 'color-brewer'}, - {caption: 'Dark', name: 'dark'}, - {caption: 'Darkula', name: 'darkula'}, - {caption: 'Docco', name: 'docco'}, - {caption: 'Dracula', name: 'dracula'}, - {caption: 'Far', name: 'far'}, - {caption: 'Foundation', name: 'foundation'}, - {caption: 'Github Gist', name: 'github-gist'}, - {caption: 'Github', name: 'github'}, - {caption: 'Googlecode', name: 'googlecode'}, - {caption: 'Grayscale', name: 'grayscale'}, - {caption: 'Gruvbox Dark', name: 'gruvbox.dark'}, - {caption: 'Gruvbox Light', name: 'gruvbox.light'}, - {caption: 'Hopscotch', name: 'hopscotch'}, - {caption: 'Hybrid', name: 'hybrid'}, - {caption: 'Idea', name: 'idea'}, - {caption: 'Ir Black', name: 'ir-black'}, - {caption: 'Kimbie Dark', name: 'kimbie.dark'}, - {caption: 'Kimbie Light', name: 'kimbie.light'}, - {caption: 'Magula', name: 'magula'}, - {caption: 'Mono Blue', name: 'mono-blue'}, - {caption: 'Monokai Sublime', name: 'monokai-sublime'}, - {caption: 'Monokai', name: 'monokai'}, - {caption: 'Obsidian', name: 'obsidian'}, - {caption: 'Paraiso Dark', name: 'paraiso-dark'}, - {caption: 'Paraiso Light', name: 'paraiso-light'}, - {caption: 'Pojoaque', name: 'pojoaque'}, - {caption: 'Qtcreator Dark', name: 'qtcreator_dark'}, - {caption: 'Qtcreator Light', name: 'qtcreator_light'}, - {caption: 'Railscasts', name: 'railscasts'}, - {caption: 'Rainbow', name: 'rainbow'}, - {caption: 'School Book', name: 'school-book'}, - {caption: 'Solarized Dark', name: 'solarized-dark'}, - {caption: 'Solarized Light', name: 'solarized-light'}, - {caption: 'Sunburst', name: 'sunburst'}, - {caption: 'Tomorrow Night Blue', name: 'tomorrow-night-blue'}, - {caption: 'Tomorrow Night Bright', name: 'tomorrow-night-bright'}, - {caption: 'Tomorrow Night Eighties', name: 'tomorrow-night-eighties'}, - {caption: 'Tomorrow Night', name: 'tomorrow-night'}, - {caption: 'Tomorrow', name: 'tomorrow'}, - {caption: 'Vs', name: 'vs'}, - {caption: 'Xcode', name: 'xcode'}, - {caption: 'Xt 256', name: 'xt256'}, - {caption: 'Zenburn', name: 'zenburn'} -] - -export default function hljsTheme () { - return hljsThemeList -} diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 8a1c9a99..6985b2a2 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -1,7 +1,6 @@ import markdownit from 'markdown-it' import emoji from 'markdown-it-emoji' import math from '@rokt33r/markdown-it-math' -import hljs from 'highlight.js' import _ from 'lodash' const katex = window.katex @@ -10,9 +9,9 @@ function createGutter (str) { let lc = (str.match(/\n/g) || []).length let lines = [] for (let i = 1; i <= lc; i++) { - lines.push('' + i + '') + lines.push('' + i + '') } - return '' + lines.join('') + '' + return '' + lines.join('') + '' } var md = markdownit({ @@ -21,19 +20,10 @@ var md = markdownit({ html: true, xhtmlOut: true, highlight: function (str, lang) { - if (lang && hljs.getLanguage(lang)) { - try { - return '
' +
-        createGutter(str) +
-        '' +
-        hljs.highlight(lang, str).value +
-        '
' - } catch (e) {} - } - return '
' +
+    return '
' +
     createGutter(str) +
-    '' +
-    str.replace(/\&/g, '&').replace(/\/g, '>').replace(/\"/g, '"') +
+    '' +
+    str +
     '
' } }) diff --git a/browser/main/modals/PreferencesModal/ConfigTab.js b/browser/main/modals/PreferencesModal/ConfigTab.js index 2f72dc4c..4091a749 100644 --- a/browser/main/modals/PreferencesModal/ConfigTab.js +++ b/browser/main/modals/PreferencesModal/ConfigTab.js @@ -1,7 +1,6 @@ import React, { PropTypes } from 'react' import CSSModules from 'browser/lib/CSSModules' import styles from './ConfigTab.styl' -import hljsTheme from 'browser/lib/hljsThemes' import ConfigManager from 'browser/main/lib/ConfigManager' import store from 'browser/main/store' import consts from 'browser/lib/consts' @@ -154,7 +153,6 @@ class ConfigTab extends React.Component {

: null let themes = consts.THEMES - let hljsThemeList = hljsTheme() let { config } = this.state return ( @@ -369,8 +367,8 @@ class ConfigTab extends React.Component { onChange={(e) => this.handleUIChange(e)} > { - hljsThemeList.map((theme) => { - return () + themes.map((theme) => { + return () }) } diff --git a/lib/finder.html b/lib/finder.html index 4ab996b0..1b7f12ea 100644 --- a/lib/finder.html +++ b/lib/finder.html @@ -8,7 +8,6 @@ - @@ -43,12 +42,12 @@ const electron = require('electron') electron.webFrame.setZoomLevelLimits(1, 1) const _ = require('lodash') - var scriptUrl = _.find(electron.remote.process.argv, a => a === '--hot') + var scriptUrl = _.find(electron.remote.process.argv, (a) => a === '--hot') ? 'http://localhost:8080/assets/finder.js' : '../compiled/finder.js' - var scriptEl=document.createElement('script') - scriptEl.setAttribute("type","text/javascript") - scriptEl.setAttribute("src", scriptUrl) + var scriptEl = document.createElement('script') + scriptEl.setAttribute('type', 'text/javascript') + scriptEl.setAttribute('src', scriptUrl) document.body.appendChild(scriptEl) diff --git a/lib/main.html b/lib/main.html index 1524fb1c..5cb40371 100644 --- a/lib/main.html +++ b/lib/main.html @@ -5,7 +5,6 @@ - Boostnote diff --git a/package.json b/package.json index 2a649023..068dbcef 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "codemirror": "^5.19.0", "electron-gh-releases": "^2.0.2", "font-awesome": "^4.3.0", - "highlight.js": "^9.3.0", "immutable": "^3.8.1", "lodash": "^4.11.1", "markdown-it": "^6.0.1", diff --git a/webpack-skeleton.js b/webpack-skeleton.js index 3727ce9b..96c7c6fc 100644 --- a/webpack-skeleton.js +++ b/webpack-skeleton.js @@ -35,7 +35,6 @@ var config = { 'lodash', 'markdown-it', 'moment', - 'highlight.js', 'markdown-it-emoji', 'fs-jetpack', '@rokt33r/markdown-it-math',