mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
export styles of code blocks
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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 `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="chart" data-height="${token.parameters.height}" data-format="${token.parameters.format || 'json'}">${token.content}</div>
|
||||
</pre>`
|
||||
},
|
||||
flowchart: token => {
|
||||
updatedOptions.onFence('flowchart')
|
||||
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
|
||||
</pre>`
|
||||
},
|
||||
mermaid: token => {
|
||||
updatedOptions.onFence('mermaid')
|
||||
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="mermaid" data-height="${token.parameters.height}">${token.content}</div>
|
||||
</pre>`
|
||||
},
|
||||
sequence: token => {
|
||||
updatedOptions.onFence('sequence')
|
||||
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="sequence" data-height="${token.parameters.height}">${token.content}</div>
|
||||
</pre>`
|
||||
}
|
||||
}, token => {
|
||||
updatedOptions.onFence('code', token.langType)
|
||||
|
||||
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
${createGutter(token.content, token.firstLineNumber)}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 => `<link rel="stylesheet" href="css/${path.basename(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 += `<script src="js/lodash.min.js"></script>`
|
||||
}
|
||||
|
||||
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 += `
|
||||
<script src="js/codemirror/codemirror.js"></script>
|
||||
<script src="js/codemirror/mode/meta.js"></script>
|
||||
<script src="js/codemirror/addon/mode/loadmode.js"></script>
|
||||
<script src="js/codemirror/addon/runmode/runmode.js"></script>
|
||||
`
|
||||
|
||||
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 += `<link rel="stylesheet" href="css/${path.basename(file)}">`
|
||||
})
|
||||
|
||||
return `<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name = "viewport" content = "width = device-width, initial-scale = 1, maximum-scale = 1">
|
||||
<style id="style">${inlineStyles}</style>
|
||||
${styles}
|
||||
</head>
|
||||
<body>${body}</body>
|
||||
</html>`
|
||||
return `
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name = "viewport" content = "width = device-width, initial-scale = 1, maximum-scale = 1">
|
||||
<style id="style">${inlineStyles}</style>
|
||||
${styles}
|
||||
${scripts}
|
||||
<script>${inlineScripts}</script>
|
||||
</head>
|
||||
<body>
|
||||
${body}
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user