diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 5eb476ff..79fb0aa7 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -187,6 +187,7 @@ class MarkdownSplitEditor extends React.Component { codeBlockTheme={config.preview.codeBlockTheme} codeBlockFontFamily={config.editor.fontFamily} lineNumber={config.preview.lineNumber} + indentSize={editorIndentSize} scrollPastEnd={config.preview.scrollPastEnd} smartQuotes={config.preview.smartQuotes} smartArrows={config.preview.smartArrows} diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 2a7b66b0..a7de2abc 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -28,7 +28,8 @@ class Markdown { html: true, xhtmlOut: true, breaks: config.preview.breaks, - sanitize: 'STRICT' + sanitize: 'STRICT', + onFence: () => {} } const updatedOptions = Object.assign(defaultOptions, options) @@ -141,30 +142,40 @@ class Markdown { token.parameters.format = 'yaml' } + updatedOptions.onFence('chart', token.parameters.format) + return `
${token.fileName}
${token.content}
`
},
flowchart: token => {
+ updatedOptions.onFence('flowchart')
+
return `
${token.fileName}
${token.content}
`
},
mermaid: token => {
+ updatedOptions.onFence('mermaid')
+
return `
${token.fileName}
${token.content}
`
},
sequence: token => {
+ updatedOptions.onFence('sequence')
+
return `
${token.fileName}
${token.content}
`
}
}, token => {
+ updatedOptions.onFence('code', token.langType)
+
return `
${token.fileName}
${createGutter(token.content, token.firstLineNumber)}
diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js
index 595efd85..ae86e5be 100644
--- a/browser/main/SideNav/StorageItem.js
+++ b/browser/main/SideNav/StorageItem.js
@@ -230,20 +230,6 @@ class StorageItem extends React.Component {
folderKey: data.folderKey,
fileType: data.fileType
})
- return data
- })
- .then(data => {
- dialog.showMessageBox(remote.getCurrentWindow(), {
- type: 'info',
- message: 'Exported to "' + data.exportDir + '"'
- })
- })
- .catch(err => {
- dialog.showErrorBox(
- 'Export error',
- err ? err.message || err : 'Unexpected error during export'
- )
- throw err
})
.catch(error => {
dialog.showErrorBox(
diff --git a/browser/main/lib/dataApi/copyFile.js b/browser/main/lib/dataApi/copyFile.js
index 6f23aae2..f079c7db 100755
--- a/browser/main/lib/dataApi/copyFile.js
+++ b/browser/main/lib/dataApi/copyFile.js
@@ -1,5 +1,6 @@
-const fs = require('fs')
-const path = require('path')
+import fs from 'fs'
+import fx from 'fs-extra'
+import path from 'path'
/**
* @description Copy a file from source to destination
@@ -14,7 +15,8 @@ function copyFile (srcPath, dstPath) {
return new Promise((resolve, reject) => {
const dstFolder = path.dirname(dstPath)
- if (!fs.existsSync(dstFolder)) fs.mkdirSync(dstFolder)
+
+ fx.ensureDirSync(dstFolder)
const input = fs.createReadStream(decodeURI(srcPath))
const output = fs.createWriteStream(dstPath)
diff --git a/browser/main/lib/dataApi/exportFolder.js b/browser/main/lib/dataApi/exportFolder.js
index aef7bd8e..63b5c3a1 100644
--- a/browser/main/lib/dataApi/exportFolder.js
+++ b/browser/main/lib/dataApi/exportFolder.js
@@ -55,6 +55,7 @@ function exportFolder (storageKey, folderKey, fileType, exportDir, config) {
codeBlockTheme: config.preview.codeBlockTheme,
codeBlockFontFamily: config.editor.fontFamily,
lineNumber: config.preview.lineNumber,
+ indentSize: config.editor.indentSize,
scrollPastEnd: config.preview.scrollPastEnd,
smartQuotes: config.preview.smartQuotes,
breaks: config.preview.breaks,
diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js
index 2130e94d..f17e06c4 100755
--- a/browser/main/lib/dataApi/exportNote.js
+++ b/browser/main/lib/dataApi/exportNote.js
@@ -81,14 +81,14 @@ function rollbackExport (tasks) {
}
if (fs.existsSync(fullpath)) {
- fs.unlink(fullpath)
+ fs.unlinkSync(fullpath)
folders.add(path.dirname(fullpath))
}
})
folders.forEach((folder) => {
if (fs.readdirSync(folder).length === 0) {
- fs.rmdir(folder)
+ fs.rmdirSync(folder)
}
})
}
diff --git a/browser/main/lib/dataApi/exportStorage.js b/browser/main/lib/dataApi/exportStorage.js
index 1bcacd51..fab3aee2 100644
--- a/browser/main/lib/dataApi/exportStorage.js
+++ b/browser/main/lib/dataApi/exportStorage.js
@@ -54,6 +54,7 @@ function exportStorage (storageKey, fileType, exportDir, config) {
codeBlockTheme: config.preview.codeBlockTheme,
codeBlockFontFamily: config.editor.fontFamily,
lineNumber: config.preview.lineNumber,
+ indentSize: config.editor.indentSize,
scrollPastEnd: config.preview.scrollPastEnd,
smartQuotes: config.preview.smartQuotes,
breaks: config.preview.breaks,
diff --git a/browser/main/lib/dataApi/formatHTML.js b/browser/main/lib/dataApi/formatHTML.js
index de99421f..eb44e9ce 100644
--- a/browser/main/lib/dataApi/formatHTML.js
+++ b/browser/main/lib/dataApi/formatHTML.js
@@ -1,5 +1,6 @@
import path from 'path'
import fileUrl from 'file-url'
+import fs from 'fs'
import { remote } from 'electron'
import consts from 'browser/lib/consts'
import Markdown from 'browser/lib/markdown'
@@ -33,6 +34,14 @@ const defaultCodeBlockFontFamily = [
'monospace'
]
+function unprefix (file) {
+ if (global.process.platform === 'win32') {
+ return file.replace('file:///', '')
+ } else {
+ return file.replace('file://', '')
+ }
+}
+
/**
* ```
* {
@@ -49,7 +58,8 @@ const defaultCodeBlockFontFamily = [
* sanitize,
* breaks,
* storagePath,
- * export
+ * export,
+ * indentSize
* }
* ```
*/
@@ -76,29 +86,149 @@ export default function formatHTML (props) {
allowCustomCSS,
customCSS
)
+
const { smartQuotes, sanitize, breaks } = props
- const markdown = new Markdown({
- typographer: smartQuotes,
- sanitize,
- breaks
- })
+ let indentSize = parseInt(props.indentSize, 10)
+ if (!(indentSize > 0 && indentSize < 132)) {
+ indentSize = 4
+ }
const files = [getCodeThemeLink(codeBlockTheme), ...CSS_FILES]
return function (note, targetPath, exportTasks) {
+ let styles = files.map(file => ``).join('\n')
+
+ let inlineScripts = ''
+ let scripts = ''
+
+ let lodash = false
+ function addLodash () {
+ if (lodash) {
+ return
+ }
+
+ lodash = true
+
+ exportTasks.push({
+ src: unprefix(`${appPath}/node_modules/lodash/lodash.min.js`),
+ dst: 'js'
+ })
+
+ scripts += ``
+ }
+
+ let codemirror = false
+ function addCodeMirror () {
+ if (codemirror) {
+ return
+ }
+
+ codemirror = true
+
+ addLodash()
+
+ exportTasks.push({
+ src: unprefix(`${appPath}/node_modules/codemirror/lib/codemirror.js`),
+ dst: 'js/codemirror'
+ }, {
+ src: unprefix(`${appPath}/node_modules/codemirror/mode/meta.js`),
+ dst: 'js/codemirror/mode'
+ }, {
+ src: unprefix(`${appPath}/node_modules/codemirror/addon/mode/loadmode.js`),
+ dst: 'js/codemirror/addon/mode'
+ }, {
+ src: unprefix(`${appPath}/node_modules/codemirror/addon/runmode/runmode.js`),
+ dst: 'js/codemirror/addon/runmode'
+ })
+
+ scripts += `
+
+
+
+
+`
+
+ let className = `cm-s-${codeBlockTheme}`
+ if (codeBlockTheme.indexOf('solarized') === 0) {
+ const [refThema, color] = codeBlockTheme.split(' ')
+ className = `cm-s-${refThema} cm-s-${color}`
+ }
+
+ inlineScripts += `
+CodeMirror.modeURL = 'js/codemirror/mode/%N/%N.js';
+
+function decodeEntities (text) {
+ var entities = [
+ ['apos', '\\''],
+ ['amp', '&'],
+ ['lt', '<'],
+ ['gt', '>'],
+ ['#63', '\\?'],
+ ['#36', '\\$']
+ ]
+
+ for (var i = 0, max = entities.length; i < max; ++i) {
+ text = text.replace(new RegExp(\`&\${entities[i][0]};\`, 'g'), entities[i][1])
+ }
+
+ return text
+}
+
+function displayCodeBlocks () {
+ _.forEach(
+ document.querySelectorAll('.code code'),
+ el => {
+ let syntax = CodeMirror.findModeByName(el.className)
+ if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
+ CodeMirror.requireMode(syntax.mode, () => {
+ const content = decodeEntities(el.innerHTML)
+ el.innerHTML = ''
+ el.parentNode.className += ' ${className}'
+ CodeMirror.runMode(content, syntax.mime, el, {
+ tabSize: ${indentSize}
+ })
+ })
+ }
+ )
+}
+
+document.addEventListener('DOMContentLoaded', displayCodeBlocks);
+`
+ }
+
+ const modes = {}
+ const markdown = new Markdown({
+ typographer: smartQuotes,
+ sanitize,
+ breaks,
+ onFence (type, mode) {
+ if (type === 'code') {
+ addCodeMirror()
+
+ if (mode && modes[mode] !== true) {
+ const file = unprefix(`${appPath}/node_modules/codemirror/mode/${mode}/${mode}.js`)
+
+ if (fs.existsSync(file)) {
+ exportTasks.push({
+ src: file,
+ dst: `js/codemirror/mode/${mode}`
+ })
+
+ modes[mode] = true
+ }
+ }
+ }
+ }
+ })
+
let body = markdown.render(note.content)
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(note.content, props.storagePath)
files.forEach(file => {
- if (global.process.platform === 'win32') {
- file = file.replace('file:///', '')
- } else {
- file = file.replace('file://', '')
- }
exportTasks.push({
- src: file,
+ src: unprefix(file),
dst: 'css'
})
})
@@ -114,20 +244,21 @@ export default function formatHTML (props) {
body = attachmentManagement.replaceStorageReferences(body, note.key, destinationFolder)
- let styles = ''
- files.forEach(file => {
- styles += ``
- })
-
- return `
-
-
-
-
- ${styles}
-
- ${body}
- `
+ return `
+
+
+
+
+
+ ${styles}
+ ${scripts}
+
+
+
+${body}
+
+
+`
}
}