1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 10:16:26 +00:00

Compare commits

...

59 Commits

Author SHA1 Message Date
Junyoung Choi
d015b18c66 Revert flickering fix 2018-09-01 16:50:52 +09:00
Junyoung Choi
039f73711a Update yarn.lock 2018-08-20 15:22:28 +09:00
Junyoung Choi (Sai)
6e885acf8c Merge pull request #2311 from BoostIO/update-build-document
Update build documents
2018-08-20 15:20:31 +09:00
Junyoung Choi
9ef07cea7a Update build documents 2018-08-20 15:19:40 +09:00
Junyoung Choi (Sai)
9e3b321aaf Merge pull request #2216 from ZeroX-DG/improve-dev-script
Improved dev script
2018-08-20 15:05:21 +09:00
Junyoung Choi (Sai)
21560701ea Merge pull request #2303 from daiyam/update-electron
update electron version
2018-08-20 14:58:49 +09:00
Junyoung Choi (Sai)
4556375174 Merge pull request #2284 from mikaoelitiana/feat-933
Add per-folder sort
2018-08-20 13:46:03 +09:00
Junyoung Choi (Sai)
91b5398b5a Merge pull request #2307 from modmod24/fix-todo-percentage
update todo percentage correctly #2267
2018-08-20 12:00:42 +09:00
Junyoung Choi (Sai)
eeb8016992 Merge pull request #2302 from daiyam/window-position
restore window position
2018-08-20 11:57:47 +09:00
Junyoung Choi (Sai)
736106be3a Merge pull request #2283 from ZeroX-DG/fix-snippet-list-bug
Fixed snippet list bug
2018-08-20 10:35:06 +09:00
Junyoung Choi (Sai)
f400568dc0 Merge pull request #2272 from ZeroX-DG/improve-theme
Improve sideNav scroll bar color
2018-08-20 10:24:48 +09:00
Junyoung Choi (Sai)
0ca96cba6e Merge pull request #2074 from max-buranbaev/blinking-markdown-crunch-fix
Blinking markdown crunch fix
2018-08-20 10:05:06 +09:00
Unknown
df4d837026 add todo status test 2018-08-18 14:38:07 +01:00
Unknown
760f84d7fa fix for 2267
todo percentage not updated correctly
2018-08-18 14:30:23 +01:00
Baptiste Augrain
174a315e3f update electron version 2018-08-16 01:23:35 +02:00
Baptiste Augrain
0834313456 restore window position 2018-08-16 01:06:13 +02:00
Unknown
df931e10c0 Merge remote-tracking branch 'BoostIO/master' 2018-08-12 17:53:03 +01:00
Mika Andrianarijaona
9572cb2d33 Fix default value of config.sortBy 2018-08-12 09:21:46 +02:00
Max Buranbaev
51e836f32a code style 2018-08-11 16:17:56 +05:00
Max Buranbaev
7fefbd88d0 Resolving conflict 2018-08-11 11:13:04 +00:00
Mika Andrianarijaona
cb956c5508 Use default value config.sortBy
- for new folders
- for folders with no config set
2018-08-11 11:44:22 +02:00
Mika Andrianarijaona
47b0086bf8 Add per-folder sort
- save sort configuration in `config.[folderKey].sortBy`
- use lodash ` _.get(config, [folderKey, 'sortBy'])` to avoid error
2018-08-11 11:36:36 +02:00
Nguyễn Việt Hưng
b8d66e4a95 fixed snippet list bug 2018-08-11 09:20:04 +07:00
Max Buranbaev
bfc1c93153 Merge branch 'master' into blinking-markdown-crunch-fix 2018-08-10 19:02:43 +05:00
Nguyễn Việt Hưng
404faf8a0b limited style for just side nav 2018-08-09 22:59:40 +07:00
Nguyễn Việt Hưng
4a7b0f4711 improved scroll bar color for default theme 2018-08-09 22:57:16 +07:00
Nguyen Viet Hung
dd62fca45d change dev-start to dev 2018-08-09 16:21:27 +07:00
Junyoung Choi (Sai)
79fb04126c Merge pull request #2214 from cdayjr/master
Fix for BoostIO/Boostnote#2204
2018-08-09 18:15:20 +09:00
Junyoung Choi (Sai)
39c9574ae3 Merge pull request #2257 from ehhc/Attachments_in_markdown_export_#1786
might fixes #1786 --> export attachments for markdown notes
2018-08-09 18:10:48 +09:00
Junyoung Choi (Sai)
38af257adf Merge pull request #2253 from mbarczak/master
Fix for issue #2088 :  snippet notes are not displaying in markdown
2018-08-09 18:07:29 +09:00
Junyoung Choi (Sai)
5aae9a4722 Merge pull request #2231 from yougotwill/mermaid_dark_theme_fix
Mermaid dark theme rendering fix
2018-08-09 17:54:54 +09:00
Junyoung Choi (Sai)
cfe3cae88d Merge pull request #2226 from sklein12/addSnippetToSearchScope
Add code snippets to search scope
2018-08-09 17:54:28 +09:00
Junyoung Choi (Sai)
612de84ac6 Merge pull request #2208 from enyaxu/feature/2132
New Feature: Shortcuts for focusing tag editor(CmdOrControl+Shift+T)(#2132)
2018-08-09 17:53:54 +09:00
Nguyen Viet Hung
33be597ef0 Delete package-lock.json 2018-08-09 15:50:07 +07:00
Junyoung Choi (Sai)
cc26fd80d7 Merge pull request #2235 from amedora/feature/1454-ditaa
add Ditaa support
2018-08-09 17:47:37 +09:00
Junyoung Choi (Sai)
c227a1ffec Merge pull request #2171 from yamash723/fixbug-html-export
Fix search value for html export path
2018-08-09 17:43:56 +09:00
Nguyen Viet Hung
f0df787bbe Fix escape codeblock (#2230)
* updated package-lock

* added fix and test for escape html in code block

* fixed markdown preview render bug

* updated comment in escape function

* improved escape function

* Delete package-lock.json
2018-08-09 17:08:52 +09:00
ehhc
09188bed48 might fixes #1786 --> export attachments for markdown notes 2018-07-30 18:09:02 +02:00
Maciek
4a3bcaba06 BUG FIX: Change the way of checking for empty array
The original condition : attachments !== [] always returns true,
for empty array, as well as for array with elements.
2018-07-28 22:25:10 +02:00
Maciek
1d1ab65edd BUG FIX: snippet notes are not displaying in markdown #2088
Fix for issue https://github.com/BoostIo/Boostnote/issues/2088.
In specific situation, when all below conditions are met :
- one of the snippets notes tabs is a Markdown tab,
- the migrateAttachments code is called on a snippet note,
- historical attachments location '/images' exists in snippet storage
  folder

Following exception is being thrown :
path.js:28 Uncaught TypeError: Path must be a string. Received undefined
The exception is a result of an undefined noteKey variable
(which is defined only for Markdown notes).
The solution is to skip migration of attachments for notes without
noteKey (which wouldn't be possible anyway, since noteKey is a
necessary for creating folder for attachments).
2018-07-28 22:23:14 +02:00
Junyoung Choi (Sai)
7330cdaf1c Merge pull request #2209 from yougotwill/info_box_fix
Trash infoPanel Fix
2018-07-28 23:34:46 +09:00
Junyoung Choi (Sai)
050a1fb6cf Merge pull request #2239 from narukami894/improve_jp_build_md
add and improve translation, fix typo
2018-07-28 23:33:56 +09:00
narukami894
1e8397cf17 add and improve translation, fix typo 2018-07-24 16:01:41 +09:00
amedora
59b53ece2b add Ditaa support (resolve #1454) 2018-07-23 11:16:20 +09:00
William Grant
16c62cd46f changed double quotes to single quotes 2018-07-21 19:31:40 +10:00
William Grant
eff56c2514 mermaid diagram rendering for dark themes is now fixed 2018-07-21 19:20:20 +10:00
yamash723
ee6b9a223f Change output path format of html file by platform 2018-07-21 15:25:09 +09:00
Chad Wade Day, Jr
acc6ea434a Merge remote-tracking branch 'upstream/master' 2018-07-20 11:45:36 -07:00
Steve Klein
1e5a7356f4 Add code snippets to search scope 2018-07-20 02:00:11 -07:00
Nguyễn Việt Hưng
4c8342c19d updated dev script 2018-07-19 17:04:55 +07:00
Nguyễn Việt Hưng
dad5232ecb updated package-lock 2018-07-19 16:55:19 +07:00
Chad Wade Day, Jr
be972781ee Fix for BoostIO/Boostnote#2204 2018-07-18 11:58:54 -07:00
William Grant
58fbc298b1 Merge branch 'master' into info_box_fix 2018-07-17 14:41:45 +02:00
William Grant
7de7772339 Fixed infoButton panel in trash positioning 2018-07-17 14:41:22 +02:00
JianXu
ad847a2f5d New Feature: Shortcuts for focusing tag editor(CmdOrControl+T) 2018-07-17 17:19:03 +08:00
yamash723
4f79f52524 Fix search value for html export path 2018-07-03 10:03:31 +09:00
Max Buranbaev
2c7f24cb8c Debouncing of rendering due to flickering fix 2018-06-28 13:03:12 +05:00
Max Buranbaev
398ebae2ba Merge branch 'master' into blinking-markdown-crunch-fix 2018-06-27 14:21:34 +00:00
Max Buranbaev
5de176757d Fixing flickering in both cases 2018-06-27 13:07:38 +05:00
40 changed files with 1240 additions and 746 deletions

View File

@@ -22,7 +22,9 @@
"fontSize": "14",
"lineNumber": true
},
"sortBy": "UPDATED_AT",
"sortBy": {
"default": "UPDATED_AT"
},
"sortTagsBy": "ALPHABETICAL",
"ui": {
"defaultNote": "ALWAYS_ASK",

View File

@@ -18,14 +18,17 @@ import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
const buildCMRulers = (rulers, enableRulers) =>
enableRulers ? rulers.map(ruler => ({column: ruler})) : []
(enableRulers ? rulers.map(ruler => ({ column: ruler })) : [])
export default class CodeEditor extends React.Component {
constructor (props) {
super(props)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
this.changeHandler = (e) => this.handleChange(e)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {
leading: false,
trailing: true
})
this.changeHandler = e => this.handleChange(e)
this.focusHandler = () => {
ipcRenderer.send('editor:focused', true)
}
@@ -41,11 +44,15 @@ export default class CodeEditor extends React.Component {
}
this.props.onBlur != null && this.props.onBlur(e)
const {storageKey, noteKey} = this.props
attachmentManagement.deleteAttachmentsNotPresentInNote(this.editor.getValue(), storageKey, noteKey)
const { storageKey, noteKey } = this.props
attachmentManagement.deleteAttachmentsNotPresentInNote(
this.editor.getValue(),
storageKey,
noteKey
)
}
this.pasteHandler = (editor, e) => this.handlePaste(editor, e)
this.loadStyleHandler = (e) => {
this.loadStyleHandler = e => {
this.editor.refresh()
}
this.searchHandler = (e, msg) => this.handleSearch(msg)
@@ -66,7 +73,10 @@ export default class CodeEditor extends React.Component {
cm.addOverlay(component.searchState)
function makeOverlay (query, style) {
query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi')
query = new RegExp(
query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'),
'gi'
)
return {
token: function (stream) {
query.lastIndex = stream.pos
@@ -102,7 +112,11 @@ export default class CodeEditor extends React.Component {
}
]
if (!fs.existsSync(consts.SNIPPET_FILE)) {
fs.writeFileSync(consts.SNIPPET_FILE, JSON.stringify(defaultSnippet, null, 4), 'utf8')
fs.writeFileSync(
consts.SNIPPET_FILE,
JSON.stringify(defaultSnippet, null, 4),
'utf8'
)
}
this.value = this.props.value
@@ -144,9 +158,14 @@ export default class CodeEditor extends React.Component {
cm.execCommand('insertSoftTab')
}
cm.execCommand('goLineEnd')
} else if (!charBeforeCursor.match(/\t|\s|\r|\n/) && cursor.ch > 1) {
} else if (
!charBeforeCursor.match(/\t|\s|\r|\n/) &&
cursor.ch > 1
) {
// text expansion on tab key if the char before is alphabet
const snippets = JSON.parse(fs.readFileSync(consts.SNIPPET_FILE, 'utf8'))
const snippets = JSON.parse(
fs.readFileSync(consts.SNIPPET_FILE, 'utf8')
)
if (expandSnippet(line, cursor, cm, snippets) === false) {
if (tabs) {
cm.execCommand('insertTab')
@@ -167,7 +186,7 @@ export default class CodeEditor extends React.Component {
// Do nothing
},
Enter: 'boostNewLineAndIndentContinueMarkdownList',
'Ctrl-C': (cm) => {
'Ctrl-C': cm => {
if (cm.getOption('keyMap').substr(0, 3) === 'vim') {
document.execCommand('copy')
}
@@ -201,7 +220,11 @@ export default class CodeEditor extends React.Component {
}
expandSnippet (line, cursor, cm, snippets) {
const wordBeforeCursor = this.getWordBeforeCursor(line, cursor.line, cursor.ch)
const wordBeforeCursor = this.getWordBeforeCursor(
line,
cursor.line,
cursor.ch
)
const templateCursorString = ':{}'
for (let i = 0; i < snippets.length; i++) {
if (snippets[i].prefix.indexOf(wordBeforeCursor.text) !== -1) {
@@ -219,7 +242,10 @@ export default class CodeEditor extends React.Component {
wordBeforeCursor.range.from,
wordBeforeCursor.range.to
)
cm.setCursor({ line: cursor.line + cursorLineNumber, ch: cursorLinePosition })
cm.setCursor({
line: cursor.line + cursorLineNumber,
ch: cursorLinePosition
})
}
}
} else {
@@ -261,8 +287,8 @@ export default class CodeEditor extends React.Component {
return {
text: wordBeforeCursor,
range: {
from: {line: lineNumber, ch: originCursorPosition},
to: {line: lineNumber, ch: cursorPosition}
from: { line: lineNumber, ch: originCursorPosition },
to: { line: lineNumber, ch: cursorPosition }
}
}
}
@@ -286,7 +312,7 @@ export default class CodeEditor extends React.Component {
componentDidUpdate (prevProps, prevState) {
let needRefresh = false
const {rulers, enableRulers} = this.props
const { rulers, enableRulers } = this.props
if (prevProps.mode !== this.props.mode) {
this.setMode(this.props.mode)
}
@@ -304,7 +330,10 @@ export default class CodeEditor extends React.Component {
needRefresh = true
}
if (prevProps.enableRulers !== enableRulers || prevProps.rulers !== rulers) {
if (
prevProps.enableRulers !== enableRulers ||
prevProps.rulers !== rulers
) {
this.editor.setOption('rulers', buildCMRulers(rulers, enableRulers))
}
@@ -344,11 +373,9 @@ export default class CodeEditor extends React.Component {
}
}
moveCursorTo (row, col) {
}
moveCursorTo (row, col) {}
scrollToLine (num) {
}
scrollToLine (num) {}
focus () {
this.editor.focus()
@@ -376,8 +403,13 @@ export default class CodeEditor extends React.Component {
handleDropImage (dropEvent) {
dropEvent.preventDefault()
const {storageKey, noteKey} = this.props
attachmentManagement.handleAttachmentDrop(this, storageKey, noteKey, dropEvent)
const { storageKey, noteKey } = this.props
attachmentManagement.handleAttachmentDrop(
this,
storageKey,
noteKey,
dropEvent
)
}
insertAttachmentMd (imageMd) {
@@ -386,34 +418,44 @@ export default class CodeEditor extends React.Component {
handlePaste (editor, e) {
const clipboardData = e.clipboardData
const {storageKey, noteKey} = this.props
const { storageKey, noteKey } = this.props
const dataTransferItem = clipboardData.items[0]
const pastedTxt = clipboardData.getData('text')
const isURL = (str) => {
const isURL = str => {
const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/
return matcher.test(str)
}
const isInLinkTag = (editor) => {
const isInLinkTag = editor => {
const startCursor = editor.getCursor('start')
const prevChar = editor.getRange(
{line: startCursor.line, ch: startCursor.ch - 2},
{line: startCursor.line, ch: startCursor.ch}
{ line: startCursor.line, ch: startCursor.ch - 2 },
{ line: startCursor.line, ch: startCursor.ch }
)
const endCursor = editor.getCursor('end')
const nextChar = editor.getRange(
{line: endCursor.line, ch: endCursor.ch},
{line: endCursor.line, ch: endCursor.ch + 1}
{ line: endCursor.line, ch: endCursor.ch },
{ line: endCursor.line, ch: endCursor.ch + 1 }
)
return prevChar === '](' && nextChar === ')'
}
if (dataTransferItem.type.match('image')) {
attachmentManagement.handlePastImageEvent(this, storageKey, noteKey, dataTransferItem)
} else if (this.props.fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) {
attachmentManagement.handlePastImageEvent(
this,
storageKey,
noteKey,
dataTransferItem
)
} else if (
this.props.fetchUrlTitle &&
isURL(pastedTxt) &&
!isInLinkTag(editor)
) {
this.handlePasteUrl(e, editor, pastedTxt)
}
if (attachmentManagement.isAttachmentLink(pastedTxt)) {
attachmentManagement.handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt)
.then((modifiedText) => {
attachmentManagement
.handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt)
.then(modifiedText => {
this.editor.replaceSelection(modifiedText)
})
e.preventDefault()
@@ -431,39 +473,49 @@ export default class CodeEditor extends React.Component {
const taggedUrl = `<${pastedTxt}>`
editor.replaceSelection(taggedUrl)
const isImageReponse = (response) => {
return response.headers.has('content-type') &&
const isImageReponse = response => {
return (
response.headers.has('content-type') &&
response.headers.get('content-type').match(/^image\/.+$/)
)
}
const replaceTaggedUrl = (replacement) => {
const replaceTaggedUrl = replacement => {
const value = editor.getValue()
const cursor = editor.getCursor()
const newValue = value.replace(taggedUrl, replacement)
const newCursor = Object.assign({}, cursor, { ch: cursor.ch + newValue.length - value.length })
const newCursor = Object.assign({}, cursor, {
ch: cursor.ch + newValue.length - value.length
})
editor.setValue(newValue)
editor.setCursor(newCursor)
}
fetch(pastedTxt, {
method: 'get'
}).then((response) => {
if (isImageReponse(response)) {
return this.mapImageResponse(response, pastedTxt)
} else {
return this.mapNormalResponse(response, pastedTxt)
}
}).then((replacement) => {
replaceTaggedUrl(replacement)
}).catch((e) => {
replaceTaggedUrl(pastedTxt)
})
.then(response => {
if (isImageReponse(response)) {
return this.mapImageResponse(response, pastedTxt)
} else {
return this.mapNormalResponse(response, pastedTxt)
}
})
.then(replacement => {
replaceTaggedUrl(replacement)
})
.catch(e => {
replaceTaggedUrl(pastedTxt)
})
}
mapNormalResponse (response, pastedTxt) {
return this.decodeResponse(response).then((body) => {
return this.decodeResponse(response).then(body => {
return new Promise((resolve, reject) => {
try {
const parsedBody = (new window.DOMParser()).parseFromString(body, 'text/html')
const parsedBody = new window.DOMParser().parseFromString(
body,
'text/html'
)
const linkWithTitle = `[${parsedBody.title}](${pastedTxt})`
resolve(linkWithTitle)
} catch (e) {
@@ -491,10 +543,13 @@ export default class CodeEditor extends React.Component {
const _charset = headers.has('content-type')
? this.extractContentTypeCharset(headers.get('content-type'))
: undefined
return response.arrayBuffer().then((buff) => {
return response.arrayBuffer().then(buff => {
return new Promise((resolve, reject) => {
try {
const charset = _charset !== undefined && iconv.encodingExists(_charset) ? _charset : 'utf-8'
const charset = _charset !== undefined &&
iconv.encodingExists(_charset)
? _charset
: 'utf-8'
resolve(iconv.decode(new Buffer(buff), charset).toString())
} catch (e) {
reject(e)
@@ -504,11 +559,14 @@ export default class CodeEditor extends React.Component {
}
extractContentTypeCharset (contentType) {
return contentType.split(';').filter((str) => {
return str.trim().toLowerCase().startsWith('charset')
}).map((str) => {
return str.replace(/['"]/g, '').split('=')[1]
})[0]
return contentType
.split(';')
.filter(str => {
return str.trim().toLowerCase().startsWith('charset')
})
.map(str => {
return str.replace(/['"]/g, '').split('=')[1]
})[0]
}
render () {
@@ -517,10 +575,7 @@ export default class CodeEditor extends React.Component {
const width = this.props.width
return (
<div
className={className == null
? 'CodeEditor'
: `CodeEditor ${className}`
}
className={className == null ? 'CodeEditor' : `CodeEditor ${className}`}
ref='root'
tabIndex='-1'
style={{
@@ -528,7 +583,7 @@ export default class CodeEditor extends React.Component {
fontSize: fontSize,
width: width
}}
onDrop={(e) => this.handleDropImage(e)}
onDrop={e => this.handleDropImage(e)}
/>
)
}

View File

@@ -28,15 +28,24 @@ const fileUrl = require('file-url')
const dialog = remote.dialog
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
const appPath = fileUrl(process.env.NODE_ENV === 'production'
? app.getAppPath()
: path.resolve())
const appPath = fileUrl(
process.env.NODE_ENV === 'production' ? app.getAppPath() : path.resolve()
)
const CSS_FILES = [
`${appPath}/node_modules/katex/dist/katex.min.css`,
`${appPath}/node_modules/codemirror/lib/codemirror.css`
]
function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS) {
function buildStyle (
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
) {
return `
@font-face {
font-family: 'Lato';
@@ -160,17 +169,27 @@ if (!OSX) {
defaultFontFamily.unshift('Microsoft YaHei')
defaultFontFamily.unshift('meiryo')
}
const defaultCodeBlockFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace']
const defaultCodeBlockFontFamily = [
'Monaco',
'Menlo',
'Ubuntu Mono',
'Consolas',
'source-code-pro',
'monospace'
]
export default class MarkdownPreview extends React.Component {
constructor (props) {
super(props)
this.contextMenuHandler = (e) => this.handleContextMenu(e)
this.mouseDownHandler = (e) => this.handleMouseDown(e)
this.mouseUpHandler = (e) => this.handleMouseUp(e)
this.DoubleClickHandler = (e) => this.handleDoubleClick(e)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
this.contextMenuHandler = e => this.handleContextMenu(e)
this.mouseDownHandler = e => this.handleMouseDown(e)
this.mouseUpHandler = e => this.handleMouseUp(e)
this.DoubleClickHandler = e => this.handleDoubleClick(e)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {
leading: false,
trailing: true
})
this.checkboxClickHandler = e => this.handleCheckboxClick(e)
this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd()
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
@@ -232,36 +251,85 @@ export default class MarkdownPreview extends React.Component {
}
handleSaveAsMd () {
this.exportAsDocument('md')
this.exportAsDocument('md', (noteContent, exportTasks) => {
let result = noteContent
if (this.props && this.props.storagePath && this.props.noteKey) {
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
noteContent,
this.props.storagePath
)
attachmentsAbsolutePaths.forEach(attachment => {
exportTasks.push({
src: attachment,
dst: attachmentManagement.DESTINATION_FOLDER
})
})
result = attachmentManagement.removeStorageAndNoteReferences(
noteContent,
this.props.noteKey
)
}
return result
})
}
handleSaveAsHtml () {
this.exportAsDocument('html', (noteContent, exportTasks) => {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS)
let body = this.markdown.render(escapeHtmlCharacters(noteContent, { detectCodeBlock: true }))
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.getStyleParams()
const inlineStyles = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
)
let body = this.markdown.render(
escapeHtmlCharacters(noteContent, { detectCodeBlock: true })
)
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(noteContent, this.props.storagePath)
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
noteContent,
this.props.storagePath
)
files.forEach((file) => {
file = file.replace('file://', '')
files.forEach(file => {
if (global.process.platform === 'win32') {
file = file.replace('file:///', '')
} else {
file = file.replace('file://', '')
}
exportTasks.push({
src: file,
dst: 'css'
})
})
attachmentsAbsolutePaths.forEach((attachment) => {
attachmentsAbsolutePaths.forEach(attachment => {
exportTasks.push({
src: attachment,
dst: attachmentManagement.DESTINATION_FOLDER
})
})
body = attachmentManagement.removeStorageAndNoteReferences(body, this.props.noteKey)
body = attachmentManagement.removeStorageAndNoteReferences(
body,
this.props.noteKey
)
let styles = ''
files.forEach((file) => {
files.forEach(file => {
styles += `<link rel="stylesheet" href="css/${path.basename(file)}">`
})
@@ -283,43 +351,47 @@ export default class MarkdownPreview extends React.Component {
exportAsDocument (fileType, contentFormatter) {
const options = {
filters: [
{name: 'Documents', extensions: [fileType]}
],
filters: [{ name: 'Documents', extensions: [fileType] }],
properties: ['openFile', 'createDirectory']
}
dialog.showSaveDialog(remote.getCurrentWindow(), options,
(filename) => {
if (filename) {
const content = this.props.value
const storage = this.props.storagePath
dialog.showSaveDialog(remote.getCurrentWindow(), options, filename => {
if (filename) {
const content = this.props.value
const storage = this.props.storagePath
exportNote(storage, content, filename, contentFormatter)
.then((res) => {
dialog.showMessageBox(remote.getCurrentWindow(), {type: 'info', message: `Exported to ${filename}`})
}).catch((err) => {
dialog.showErrorBox('Export error', err ? err.message || err : 'Unexpected error during export')
throw err
})
}
})
exportNote(storage, content, filename, contentFormatter)
.then(res => {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info',
message: `Exported to ${filename}`
})
})
.catch(err => {
dialog.showErrorBox(
'Export error',
err ? err.message || err : 'Unexpected error during export'
)
throw err
})
}
})
}
fixDecodedURI (node) {
if (node && node.children.length === 1 && typeof node.children[0] === 'string') {
if (
node &&
node.children.length === 1 &&
typeof node.children[0] === 'string'
) {
const { innerText, href } = node
node.innerText = mdurl.decode(href) === innerText
? href
: innerText
node.innerText = mdurl.decode(href) === innerText ? href : innerText
}
}
getScrollBarStyle () {
const {
theme
} = this.props
const { theme } = this.props
switch (theme) {
case 'dark':
@@ -333,7 +405,10 @@ export default class MarkdownPreview extends React.Component {
componentDidMount () {
this.refs.root.setAttribute('sandbox', 'allow-scripts')
this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler)
this.refs.root.contentWindow.document.body.addEventListener(
'contextmenu',
this.contextMenuHandler
)
let styles = `
<style id='style'></style>
@@ -344,7 +419,7 @@ export default class MarkdownPreview extends React.Component {
</style>
`
CSS_FILES.forEach((file) => {
CSS_FILES.forEach(file => {
styles += `<link rel="stylesheet" href="${file}">`
})
@@ -352,12 +427,30 @@ export default class MarkdownPreview extends React.Component {
this.rewriteIframe()
this.applyStyle()
this.refs.root.contentWindow.document.addEventListener('mousedown', this.mouseDownHandler)
this.refs.root.contentWindow.document.addEventListener('mouseup', this.mouseUpHandler)
this.refs.root.contentWindow.document.addEventListener('dblclick', this.DoubleClickHandler)
this.refs.root.contentWindow.document.addEventListener('drop', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.addEventListener('scroll', this.scrollHandler)
this.refs.root.contentWindow.document.addEventListener(
'mousedown',
this.mouseDownHandler
)
this.refs.root.contentWindow.document.addEventListener(
'mouseup',
this.mouseUpHandler
)
this.refs.root.contentWindow.document.addEventListener(
'dblclick',
this.DoubleClickHandler
)
this.refs.root.contentWindow.document.addEventListener(
'drop',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.addEventListener(
'dragover',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.addEventListener(
'scroll',
this.scrollHandler
)
eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
@@ -365,13 +458,34 @@ export default class MarkdownPreview extends React.Component {
}
componentWillUnmount () {
this.refs.root.contentWindow.document.body.removeEventListener('contextmenu', this.contextMenuHandler)
this.refs.root.contentWindow.document.removeEventListener('mousedown', this.mouseDownHandler)
this.refs.root.contentWindow.document.removeEventListener('mouseup', this.mouseUpHandler)
this.refs.root.contentWindow.document.removeEventListener('dblclick', this.DoubleClickHandler)
this.refs.root.contentWindow.document.removeEventListener('drop', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.removeEventListener('scroll', this.scrollHandler)
this.refs.root.contentWindow.document.body.removeEventListener(
'contextmenu',
this.contextMenuHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'mousedown',
this.mouseDownHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'mouseup',
this.mouseUpHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'dblclick',
this.DoubleClickHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'drop',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'dragover',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'scroll',
this.scrollHandler
)
eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('export:save-html', this.saveAsHtmlHandler)
@@ -380,14 +494,17 @@ export default class MarkdownPreview extends React.Component {
componentDidUpdate (prevProps) {
if (prevProps.value !== this.props.value) this.rewriteIframe()
if (prevProps.smartQuotes !== this.props.smartQuotes ||
prevProps.sanitize !== this.props.sanitize ||
prevProps.smartArrows !== this.props.smartArrows ||
prevProps.breaks !== this.props.breaks) {
if (
prevProps.smartQuotes !== this.props.smartQuotes ||
prevProps.sanitize !== this.props.sanitize ||
prevProps.smartArrows !== this.props.smartArrows ||
prevProps.breaks !== this.props.breaks
) {
this.initMarkdown()
this.rewriteIframe()
}
if (prevProps.fontFamily !== this.props.fontFamily ||
if (
prevProps.fontFamily !== this.props.fontFamily ||
prevProps.fontSize !== this.props.fontSize ||
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
prevProps.codeBlockTheme !== this.props.codeBlockTheme ||
@@ -396,34 +513,82 @@ export default class MarkdownPreview extends React.Component {
prevProps.theme !== this.props.theme ||
prevProps.scrollPastEnd !== this.props.scrollPastEnd ||
prevProps.allowCustomCSS !== this.props.allowCustomCSS ||
prevProps.customCSS !== this.props.customCSS) {
prevProps.customCSS !== this.props.customCSS
) {
this.applyStyle()
this.rewriteIframe()
}
}
getStyleParams () {
const { fontSize, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS } = this.props
const {
fontSize,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.props
let { fontFamily, codeBlockFontFamily } = this.props
fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0
? fontFamily.split(',').map(fontName => fontName.trim()).concat(defaultFontFamily)
: defaultFontFamily
codeBlockFontFamily = _.isString(codeBlockFontFamily) && codeBlockFontFamily.trim().length > 0
? codeBlockFontFamily.split(',').map(fontName => fontName.trim()).concat(defaultCodeBlockFontFamily)
: defaultCodeBlockFontFamily
? fontFamily
.split(',')
.map(fontName => fontName.trim())
.concat(defaultFontFamily)
: defaultFontFamily
codeBlockFontFamily = _.isString(codeBlockFontFamily) &&
codeBlockFontFamily.trim().length > 0
? codeBlockFontFamily
.split(',')
.map(fontName => fontName.trim())
.concat(defaultCodeBlockFontFamily)
: defaultCodeBlockFontFamily
return {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS}
return {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
}
}
applyStyle () {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.getStyleParams()
this.getWindow().document.getElementById('codeTheme').href = this.GetCodeThemeLink(codeBlockTheme)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS)
this.getWindow().document.getElementById(
'codeTheme'
).href = this.GetCodeThemeLink(codeBlockTheme)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
)
}
GetCodeThemeLink (theme) {
theme = consts.THEMES.some((_theme) => _theme === theme) && theme !== 'default'
theme = consts.THEMES.some(_theme => _theme === theme) &&
theme !== 'default'
? theme
: 'elegant'
return theme.startsWith('solarized')
@@ -432,71 +597,92 @@ export default class MarkdownPreview extends React.Component {
}
rewriteIframe () {
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
el.removeEventListener('click', this.checkboxClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll(
'input[type="checkbox"]'
),
el => {
el.removeEventListener('click', this.checkboxClickHandler)
}
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
el.removeEventListener('click', this.linkClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('a'),
el => {
el.removeEventListener('click', this.linkClickHandler)
}
)
const { theme, indentSize, showCopyNotification, storagePath, noteKey } = this.props
const {
theme,
indentSize,
showCopyNotification,
storagePath,
noteKey
} = this.props
let { value, codeBlockTheme } = this.props
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g)
if (codeBlocks !== null) {
codeBlocks.forEach((codeBlock) => {
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
})
}
const renderedHTML = this.markdown.render(value)
attachmentManagement.migrateAttachments(value, storagePath, noteKey)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(
renderedHTML,
storagePath
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll(
'input[type="checkbox"]'
),
el => {
el.addEventListener('click', this.checkboxClickHandler)
}
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
el.addEventListener('click', this.checkboxClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('a'),
el => {
this.fixDecodedURI(el)
el.addEventListener('click', this.linkClickHandler)
}
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
el.addEventListener('click', this.linkClickHandler)
})
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
codeBlockTheme = consts.THEMES.some(_theme => _theme === codeBlockTheme)
? codeBlockTheme
: 'default'
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.code code'), (el) => {
let syntax = CodeMirror.findModeByName(convertModeName(el.className))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
const content = htmlTextHelper.decodeEntities(el.innerHTML)
const copyIcon = document.createElement('i')
copyIcon.innerHTML = '<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
copyIcon.onclick = (e) => {
copy(content)
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.code code'),
el => {
let syntax = CodeMirror.findModeByName(convertModeName(el.className))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
const content = htmlTextHelper.decodeEntities(el.innerHTML)
const copyIcon = document.createElement('i')
copyIcon.innerHTML =
'<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
copyIcon.onclick = e => {
copy(content)
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
}
}
}
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
if (codeBlockTheme.indexOf('solarized') === 0) {
const [refThema, color] = codeBlockTheme.split(' ')
el.parentNode.className += ` cm-s-${refThema} cm-s-${color}`
} else {
el.parentNode.className += ` cm-s-${codeBlockTheme}`
}
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
if (codeBlockTheme.indexOf('solarized') === 0) {
const [refThema, color] = codeBlockTheme.split(' ')
el.parentNode.className += ` cm-s-${refThema} cm-s-${color}`
} else {
el.parentNode.className += ` cm-s-${codeBlockTheme}`
}
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
})
})
})
})
}
)
const opts = {}
// if (this.props.theme === 'dark') {
// opts['font-color'] = '#DDD'
@@ -504,41 +690,51 @@ export default class MarkdownPreview extends React.Component {
// opts['element-color'] = '#DDD'
// opts['fill'] = '#3A404C'
// }
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => {
Raphael.setWindow(this.getWindow())
try {
const diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'flowchart-error'
el.innerHTML = 'Flowchart parse error: ' + e.message
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.flowchart'),
el => {
Raphael.setWindow(this.getWindow())
try {
const diagram = flowchart.parse(
htmlTextHelper.decodeEntities(el.innerHTML)
)
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), el => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'flowchart-error'
el.innerHTML = 'Flowchart parse error: ' + e.message
}
}
})
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => {
Raphael.setWindow(this.getWindow())
try {
const diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
el.innerHTML = ''
diagram.drawSVG(el, {theme: 'simple'})
_.forEach(el.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'sequence-error'
el.innerHTML = 'Sequence diagram parse error: ' + e.message
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.sequence'),
el => {
Raphael.setWindow(this.getWindow())
try {
const diagram = SequenceDiagram.parse(
htmlTextHelper.decodeEntities(el.innerHTML)
)
el.innerHTML = ''
diagram.drawSVG(el, { theme: 'simple' })
_.forEach(el.querySelectorAll('a'), el => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'sequence-error'
el.innerHTML = 'Sequence diagram parse error: ' + e.message
}
}
})
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.chart'),
(el) => {
el => {
try {
const chartConfig = JSON.parse(el.innerHTML)
el.innerHTML = ''
@@ -555,8 +751,8 @@ export default class MarkdownPreview extends React.Component {
)
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.mermaid'),
(el) => {
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML))
el => {
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme)
}
)
}
@@ -570,7 +766,9 @@ export default class MarkdownPreview extends React.Component {
}
scrollTo (targetRow) {
const blocks = this.getWindow().document.querySelectorAll('body>[data-line]')
const blocks = this.getWindow().document.querySelectorAll(
'body>[data-line]'
)
for (let index = 0; index < blocks.length; index++) {
let block = blocks[index]
@@ -590,7 +788,11 @@ export default class MarkdownPreview extends React.Component {
notify (title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
options.icon = path.join(
'file://',
global.__dirname,
'../../resources/app.png'
)
}
return new window.Notification(title, options)
}
@@ -605,7 +807,9 @@ export default class MarkdownPreview extends React.Component {
const regexNoteInternalLink = /main.html#(.+)/
if (regexNoteInternalLink.test(linkHash)) {
const targetId = mdurl.encode(linkHash.match(regexNoteInternalLink)[1])
const targetElement = this.refs.root.contentWindow.document.getElementById(targetId)
const targetElement = this.refs.root.contentWindow.document.getElementById(
targetId
)
if (targetElement != null) {
this.getWindow().scrollTo(0, targetElement.offsetTop)
@@ -639,9 +843,9 @@ export default class MarkdownPreview extends React.Component {
render () {
const { className, style, tabIndex } = this.props
return (
<iframe className={className != null
? 'MarkdownPreview ' + className
: 'MarkdownPreview'
<iframe
className={
className != null ? 'MarkdownPreview ' + className : 'MarkdownPreview'
}
style={style}
tabIndex={tabIndex}

View File

@@ -26,14 +26,12 @@ const TagElement = ({ tagName }) => (
* @param {Array|null} tags
* @return {React.Component}
*/
const TagElementList = (tags) => {
const TagElementList = tags => {
if (!isArray(tags)) {
return []
}
const tagElements = tags.map(tag => (
TagElement({tagName: tag})
))
const tagElements = tags.map(tag => TagElement({ tagName: tag }))
return tagElements
}
@@ -59,10 +57,8 @@ const NoteItem = ({
folderName,
viewType
}) => (
<div styleName={isActive
? 'item--active'
: 'item'
}
<div
styleName={isActive ? 'item--active' : 'item'}
key={note.key}
onClick={e => handleNoteClick(e, note.key)}
onContextMenu={e => handleNoteContextMenu(e, note.key)}
@@ -72,42 +68,54 @@ const NoteItem = ({
<div styleName='item-wrapper'>
{note.type === 'SNIPPET_NOTE'
? <i styleName='item-title-icon' className='fa fa-fw fa-code' />
: <i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />
}
: <i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />}
<div styleName='item-title'>
{note.title.trim().length > 0
? note.title
: <span styleName='item-title-empty'>{i18n.__('Empty note')}</span>
}
: <span styleName='item-title-empty'>{i18n.__('Empty note')}</span>}
</div>
{['ALL', 'STORAGE'].includes(viewType) && <div styleName='item-middle'>
<div styleName='item-middle-time'>{dateDisplay}</div>
<div styleName='item-middle-app-meta'>
<div title={viewType === 'ALL' ? storageName : viewType === 'STORAGE' ? folderName : null} styleName='item-middle-app-meta-label'>
{viewType === 'ALL' && storageName}
{viewType === 'STORAGE' && folderName}
{['ALL', 'STORAGE'].includes(viewType) &&
<div styleName='item-middle'>
<div styleName='item-middle-time'>{dateDisplay}</div>
<div styleName='item-middle-app-meta'>
<div
title={
viewType === 'ALL'
? storageName
: viewType === 'STORAGE' ? folderName : null
}
styleName='item-middle-app-meta-label'
>
{viewType === 'ALL' && storageName}
{viewType === 'STORAGE' && folderName}
</div>
</div>
</div>
</div>}
</div>}
<div styleName='item-bottom'>
<div styleName='item-bottom-tagList'>
{note.tags.length > 0
? TagElementList(note.tags)
: <span style={{ fontStyle: 'italic', opacity: 0.5 }} styleName='item-bottom-tagList-empty'>{i18n.__('No tags')}</span>
}
: <span
style={{ fontStyle: 'italic', opacity: 0.5 }}
styleName='item-bottom-tagList-empty'
>
{i18n.__('No tags')}
</span>}
</div>
<div>
{note.isStarred
? <img styleName='item-star' src='../resources/icon/icon-starred.svg' /> : ''
}
? <img
styleName='item-star'
src='../resources/icon/icon-starred.svg'
/>
: ''}
{note.isPinned && !pathname.match(/\/starred|\/trash/)
? <i styleName='item-pin' className='fa fa-thumb-tack' /> : ''
}
? <i styleName='item-pin' className='fa fa-thumb-tack' />
: ''}
{note.type === 'MARKDOWN_NOTE'
? <TodoProcess todoStatus={getTodoStatus(note.content)} />
: ''
}
: ''}
</div>
</div>
</div>

View File

@@ -54,9 +54,8 @@ const StorageItem = ({
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
{!isFolded && (
<DraggableIcon className={styles['folderList-item-reorder']} />
)}
{!isFolded &&
<DraggableIcon className={styles['folderList-item-reorder']} />}
<span
styleName={
isFolded ? 'folderList-item-name--folded' : 'folderList-item-name'
@@ -72,12 +71,10 @@ const StorageItem = ({
: folderName}
</span>
{!isFolded &&
_.isNumber(noteCount) && (
<span styleName='folderList-item-noteCount'>{noteCount}</span>
)}
{isFolded && (
<span styleName='folderList-item-tooltip'>{folderName}</span>
)}
_.isNumber(noteCount) &&
<span styleName='folderList-item-noteCount'>{noteCount}</span>}
{isFolded &&
<span styleName='folderList-item-tooltip'>{folderName}</span>}
</button>
)
}

View File

@@ -1,5 +1,11 @@
import mermaidAPI from 'mermaid'
// fixes bad styling in the mermaid dark theme
const darkThemeStyling = `
.loopText tspan {
fill: white;
}`
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
@@ -13,8 +19,13 @@ function getId () {
return id
}
function render (element, content) {
function render (element, content, theme) {
try {
let isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai'
mermaidAPI.initialize({
theme: isDarkTheme ? 'dark' : 'default',
themeCSS: isDarkTheme ? darkThemeStyling : ''
})
mermaidAPI.render(getId(), content, (svgGraph) => {
element.innerHTML = svgGraph
})

View File

@@ -5,10 +5,10 @@ export function getTodoStatus (content) {
splitted.forEach((line) => {
const trimmedLine = line.trim()
if (trimmedLine.match(/^[\+\-\*] \[(\s|x)\] ./)) {
if (trimmedLine.match(/^[\+\-\*] \[(\s|x)\] ./i)) {
numberOfTodo++
}
if (trimmedLine.match(/^[\+\-\*] \[x\] ./)) {
if (trimmedLine.match(/^[\+\-\*] \[x\] ./i)) {
numberOfCompletedTodo++
}
})

View File

@@ -1,6 +1,7 @@
'use strict'
import sanitizeHtml from 'sanitize-html'
import { escapeHtmlCharacters } from './utils'
module.exports = function sanitizePlugin (md, options) {
options = options || {}
@@ -8,16 +9,26 @@ module.exports = function sanitizePlugin (md, options) {
md.core.ruler.after('linkify', 'sanitize_inline', state => {
for (let tokenIdx = 0; tokenIdx < state.tokens.length; tokenIdx++) {
if (state.tokens[tokenIdx].type === 'html_block') {
state.tokens[tokenIdx].content = sanitizeHtml(state.tokens[tokenIdx].content, options)
state.tokens[tokenIdx].content = sanitizeHtml(
state.tokens[tokenIdx].content,
options
)
}
if (state.tokens[tokenIdx].type === 'fence') {
state.tokens[tokenIdx].content = state.tokens[tokenIdx].content.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
// escapeHtmlCharacters has better performance
state.tokens[tokenIdx].content = escapeHtmlCharacters(
state.tokens[tokenIdx].content,
{ skipSingleQuote: true }
)
}
if (state.tokens[tokenIdx].type === 'inline') {
const inlineTokens = state.tokens[tokenIdx].children
for (let childIdx = 0; childIdx < inlineTokens.length; childIdx++) {
if (inlineTokens[childIdx].type === 'html_inline') {
inlineTokens[childIdx].content = sanitizeHtml(inlineTokens[childIdx].content, options)
inlineTokens[childIdx].content = sanitizeHtml(
inlineTokens[childIdx].content,
options
)
}
}
}

View File

@@ -163,6 +163,22 @@ class Markdown {
}
})
// Ditaa support
this.md.use(require('markdown-it-plantuml'), {
openMarker: '@startditaa',
closeMarker: '@endditaa',
generateSource: function (umlCode) {
const stripTrailingSlash = (url) => url.endsWith('/') ? url.slice(0, -1) : url
// Currently PlantUML server doesn't support Ditaa in SVG, so we set the format as PNG at the moment.
const serverAddress = stripTrailingSlash(config.preview.plantUMLServerAddress) + '/png'
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`@startditaa\n${s}\n@endditaa`, 9)
)
return `${serverAddress}/${zippedCode}`
}
})
// Override task item
this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
let content, terminate, i, l, token

View File

@@ -23,7 +23,9 @@ function findByWordOrTag (notes, block) {
return true
}
if (note.type === 'SNIPPET_NOTE') {
return note.description.match(wordRegExp)
return note.description.match(wordRegExp) || note.snippets.some((snippet) => {
return snippet.name.match(wordRegExp) || snippet.content.match(wordRegExp)
})
} else if (note.type === 'MARKDOWN_NOTE') {
return note.content.match(wordRegExp)
}

View File

@@ -6,8 +6,12 @@ export function lastFindInArray (array, callback) {
}
}
export function escapeHtmlCharacters (html, opt = { detectCodeBlock: false }) {
export function escapeHtmlCharacters (
html,
opt = { detectCodeBlock: false, skipSingleQuote: false }
) {
const matchHtmlRegExp = /["'&<>]/g
const matchCodeBlockRegExp = /```/g
const escapes = ['&quot;', '&amp;', '&#39;', '&lt;', '&gt;']
let match = null
const replaceAt = (str, index, replace) =>
@@ -15,11 +19,18 @@ export function escapeHtmlCharacters (html, opt = { detectCodeBlock: false }) {
replace +
str.substr(index + replace.length - (replace.length - 1))
// detecting code block
while ((match = matchHtmlRegExp.exec(html)) != null) {
while ((match = matchHtmlRegExp.exec(html)) !== null) {
const current = { char: match[0], index: match.index }
const codeBlockIndexs = []
let openCodeBlock = null
// if the detectCodeBlock option is activated then this function should skip
// characters that needed to be escape but located in code block
if (opt.detectCodeBlock) {
// position of the nearest line start
// The first type of code block is lines that start with 4 spaces
// Here we check for the \n character located before the character that
// needed to be escape. It means we check for the begining of the line that
// contain that character, then we check if there are 4 spaces next to the
// \n character (the line start with 4 spaces)
let previousLineEnd = current.index - 1
while (html[previousLineEnd] !== '\n' && previousLineEnd !== -1) {
previousLineEnd--
@@ -31,16 +42,54 @@ export function escapeHtmlCharacters (html, opt = { detectCodeBlock: false }) {
html[previousLineEnd + 3] === ' ' &&
html[previousLineEnd + 4] === ' '
) {
// so skip it
// skip the current character
continue
}
// The second type of code block is lines that wrapped in ```
// We will get the position of each ```
// then push it into an array
// then the array returned will be like this:
// [startCodeblock, endCodeBlock, startCodeBlock, endCodeBlock]
while ((openCodeBlock = matchCodeBlockRegExp.exec(html)) !== null) {
codeBlockIndexs.push(openCodeBlock.index)
}
let shouldSkipChar = false
// we loop through the array of positions
// we skip 2 element as the i index position is the position of ``` that
// open the codeblock and the i + 1 is the position of the ``` that close
// the code block
for (let i = 0; i < codeBlockIndexs.length; i += 2) {
// the i index position is the position of the ``` that open code block
// so we have to + 2 as that position is the position of the first ` in the ````
// but we need to make sure that the position current character is larger
// that the last ` in the ``` that open the code block so we have to take
// the position of the first ` and + 2
// the i + 1 index position is the closing ``` so the char must less than it
if (
current.index > codeBlockIndexs[i] + 2 &&
current.index < codeBlockIndexs[i + 1]
) {
// skip it
shouldSkipChar = true
break
}
}
if (shouldSkipChar) {
// skip the current character
continue
}
}
// otherwise, escape it !!!
if (current.char === '&') {
// when escaping character & we have to be becareful as the & could be a part
// of an escaped character like &quot; will be came &amp;quot;
let nextStr = ''
let nextIndex = current.index
let escapedStr = false
// maximum length of an escape string is 5. For example ('&quot;')
// maximum length of an escaped string is 5. For example ('&quot;')
// we take the next 5 character of the next string if it is one of the string:
// ['&quot;', '&amp;', '&#39;', '&lt;', '&gt;'] then we will not escape the & character
// as it is a part of the escaped string and should not be escaped
while (nextStr.length <= 5) {
nextStr += html[nextIndex]
nextIndex++
@@ -55,7 +104,7 @@ export function escapeHtmlCharacters (html, opt = { detectCodeBlock: false }) {
}
} else if (current.char === '"') {
html = replaceAt(html, current.index, '&quot;')
} else if (current.char === "'") {
} else if (current.char === "'" && !opt.skipSingleQuote) {
html = replaceAt(html, current.index, '&#39;')
} else if (current.char === '<') {
html = replaceAt(html, current.index, '&lt;')

View File

@@ -33,6 +33,7 @@
.control-infoButton-panel-trash
z-index 200
margin-top 0px
top 50px
right 0px
position absolute
padding 20px 25px 0 25px

View File

@@ -441,7 +441,7 @@ class SnippetNoteDetail extends React.Component {
const isSuper = global.process.platform === 'darwin'
? e.metaKey
: e.ctrlKey
if (isSuper) {
if (isSuper && !e.shiftKey) {
e.preventDefault()
this.addSnippet()
}

View File

@@ -5,6 +5,7 @@ import styles from './TagSelect.styl'
import _ from 'lodash'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import i18n from 'browser/lib/i18n'
import ee from 'browser/main/lib/eventEmitter'
class TagSelect extends React.Component {
constructor (props) {
@@ -13,16 +14,26 @@ class TagSelect extends React.Component {
this.state = {
newTag: ''
}
this.addtagHandler = this.handleAddTag.bind(this)
}
componentDidMount () {
this.value = this.props.value
ee.on('editor:add-tag', this.addtagHandler)
}
componentDidUpdate () {
this.value = this.props.value
}
componentWillUnmount () {
ee.off('editor:add-tag', this.addtagHandler)
}
handleAddTag () {
this.refs.newTag.focus()
}
handleNewTagInputKeyDown (e) {
switch (e.keyCode) {
case 9:

View File

@@ -22,7 +22,6 @@ const electron = require('electron')
const { remote } = electron
class Main extends React.Component {
constructor (props) {
super(props)
@@ -60,10 +59,10 @@ class Main extends React.Component {
name: 'My Storage',
path: path.join(remote.app.getPath('home'), 'Boostnote')
})
.then((data) => {
.then(data => {
return data
})
.then((data) => {
.then(data => {
if (data.storage.folders[0] != null) {
return data
} else {
@@ -72,7 +71,7 @@ class Main extends React.Component {
color: '#1278BD',
name: 'Default'
})
.then((_data) => {
.then(_data => {
return {
storage: _data.storage,
notes: data.notes
@@ -80,7 +79,7 @@ class Main extends React.Component {
})
}
})
.then((data) => {
.then(data => {
console.log(data)
store.dispatch({
type: 'ADD_STORAGE',
@@ -98,16 +97,16 @@ class Main extends React.Component {
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Enjoy Boostnote!</h1>\n</body>\n</html>'
content: "<html>\n<body>\n<h1 id='hello'>Enjoy Boostnote!</h1>\n</body>\n</html>"
},
{
name: 'example.js',
mode: 'javascript',
content: 'var boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)'
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)"
}
]
})
.then((note) => {
.then(note => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
@@ -120,7 +119,7 @@ class Main extends React.Component {
title: 'Welcome to Boostnote!',
content: '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)'
})
.then((note) => {
.then(note => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
@@ -131,10 +130,10 @@ class Main extends React.Component {
.then(defaultMarkdownNote)
.then(() => data.storage)
})
.then((storage) => {
.then(storage => {
hashHistory.push('/storages/' + storage.key)
})
.catch((err) => {
.catch(err => {
throw err
})
}
@@ -142,12 +141,7 @@ class Main extends React.Component {
componentDidMount () {
const { dispatch, config } = this.props
const supportedThemes = [
'dark',
'white',
'solarized-dark',
'monokai'
]
const supportedThemes = ['dark', 'white', 'solarized-dark', 'monokai']
if (supportedThemes.indexOf(config.ui.theme) !== -1) {
document.body.setAttribute('data-theme', config.ui.theme)
@@ -162,19 +156,18 @@ class Main extends React.Component {
}
applyShortcuts()
// Reload all data
dataApi.init()
.then((data) => {
dispatch({
type: 'INIT_ALL',
storages: data.storages,
notes: data.notes
})
if (data.storages.length < 1) {
this.init()
}
dataApi.init().then(data => {
dispatch({
type: 'INIT_ALL',
storages: data.storages,
notes: data.notes
})
if (data.storages.length < 1) {
this.init()
}
})
eventEmitter.on('editor:fullscreen', this.toggleFullScreen)
}
@@ -199,34 +192,40 @@ class Main extends React.Component {
handleMouseUp (e) {
// Change width of NoteList component.
if (this.state.isRightSliderFocused) {
this.setState({
isRightSliderFocused: false
}, () => {
const { dispatch } = this.props
const newListWidth = this.state.listWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({listWidth: newListWidth})
dispatch({
type: 'SET_LIST_WIDTH',
listWidth: newListWidth
})
})
this.setState(
{
isRightSliderFocused: false
},
() => {
const { dispatch } = this.props
const newListWidth = this.state.listWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({ listWidth: newListWidth })
dispatch({
type: 'SET_LIST_WIDTH',
listWidth: newListWidth
})
}
)
}
// Change width of SideNav component.
if (this.state.isLeftSliderFocused) {
this.setState({
isLeftSliderFocused: false
}, () => {
const { dispatch } = this.props
const navWidth = this.state.navWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({ navWidth })
dispatch({
type: 'SET_NAV_WIDTH',
navWidth
})
})
this.setState(
{
isLeftSliderFocused: false
},
() => {
const { dispatch } = this.props
const navWidth = this.state.navWidth
// TODO: ConfigManager should dispatch itself.
ConfigManager.set({ navWidth })
dispatch({
type: 'SET_NAV_WIDTH',
navWidth
})
}
)
}
}
@@ -271,8 +270,8 @@ class Main extends React.Component {
}
hideLeftLists (noteDetail, noteList, mainBody) {
this.setState({noteDetailWidth: noteDetail.style.left})
this.setState({mainBodyWidth: mainBody.style.left})
this.setState({ noteDetailWidth: noteDetail.style.left })
this.setState({ mainBodyWidth: mainBody.style.left })
noteDetail.style.left = '0px'
mainBody.style.left = '0px'
noteList.style.display = 'none'
@@ -294,33 +293,36 @@ class Main extends React.Component {
<div
className='Main'
styleName='root'
onMouseMove={(e) => this.handleMouseMove(e)}
onMouseUp={(e) => this.handleMouseUp(e)}
onMouseMove={e => this.handleMouseMove(e)}
onMouseUp={e => this.handleMouseUp(e)}
>
<SideNav
{..._.pick(this.props, [
'dispatch',
'data',
'config',
'location'
])}
{..._.pick(this.props, ['dispatch', 'data', 'config', 'location'])}
width={this.state.navWidth}
/>
{!config.isSideNavFolded &&
<div styleName={this.state.isLeftSliderFocused ? 'slider--active' : 'slider'}
style={{left: this.state.navWidth}}
onMouseDown={(e) => this.handleLeftSlideMouseDown(e)}
<div
styleName={
this.state.isLeftSliderFocused ? 'slider--active' : 'slider'
}
style={{ left: this.state.navWidth }}
onMouseDown={e => this.handleLeftSlideMouseDown(e)}
draggable='false'
>
<div styleName='slider-hitbox' />
</div>
}
<div styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
</div>}
<div
styleName={config.isSideNavFolded ? 'body--expanded' : 'body'}
id='main-body'
ref='body'
style={{left: config.isSideNavFolded ? foldedNavigationWidth : this.state.navWidth}}
style={{
left: config.isSideNavFolded
? foldedNavigationWidth
: this.state.navWidth
}}
>
<TopBar style={{width: this.state.listWidth}}
<TopBar
style={{ width: this.state.listWidth }}
{..._.pick(this.props, [
'dispatch',
'config',
@@ -329,7 +331,8 @@ class Main extends React.Component {
'location'
])}
/>
<NoteList style={{width: this.state.listWidth}}
<NoteList
style={{ width: this.state.listWidth }}
{..._.pick(this.props, [
'dispatch',
'data',
@@ -338,15 +341,20 @@ class Main extends React.Component {
'location'
])}
/>
<div styleName={this.state.isRightSliderFocused ? 'slider-right--active' : 'slider-right'}
style={{left: this.state.listWidth - 1}}
onMouseDown={(e) => this.handleRightSlideMouseDown(e)}
<div
styleName={
this.state.isRightSliderFocused
? 'slider-right--active'
: 'slider-right'
}
style={{ left: this.state.listWidth - 1 }}
onMouseDown={e => this.handleRightSlideMouseDown(e)}
draggable='false'
>
<div styleName='slider-hitbox' />
</div>
<Detail
style={{left: this.state.listWidth}}
style={{ left: this.state.listWidth }}
{..._.pick(this.props, [
'dispatch',
'data',
@@ -374,4 +382,4 @@ Main.propTypes = {
data: PropTypes.shape({}).isRequired
}
export default connect((x) => x)(CSSModules(Main, styles))
export default connect(x => x)(CSSModules(Main, styles))

View File

@@ -418,10 +418,10 @@ class NoteList extends React.Component {
}
handleSortByChange (e) {
const { dispatch } = this.props
const { dispatch, params: { folderKey } } = this.props
const config = {
sortBy: e.target.value
[folderKey]: { sortBy: e.target.value }
}
ConfigManager.set(config)
@@ -909,12 +909,13 @@ class NoteList extends React.Component {
}
render () {
const { location, config } = this.props
const { location, config, params: { folderKey } } = this.props
let { notes } = this.props
const { selectedNoteKeys } = this.state
const sortFunc = config.sortBy === 'CREATED_AT'
const sortBy = _.get(config, [folderKey, 'sortBy'], config.sortBy.default)
const sortFunc = sortBy === 'CREATED_AT'
? sortByCreatedAt
: config.sortBy === 'ALPHABETICAL'
: sortBy === 'ALPHABETICAL'
? sortByAlphabetical
: sortByUpdatedAt
const sortedNotes = location.pathname.match(/\/starred|\/trash/)
@@ -965,7 +966,7 @@ class NoteList extends React.Component {
notes.length === 1 ||
(autoSelectFirst && index === 0)
const dateDisplay = moment(
config.sortBy === 'CREATED_AT'
sortBy === 'CREATED_AT'
? note.createdAt : note.updatedAt
).fromNow('D')
@@ -1014,7 +1015,7 @@ class NoteList extends React.Component {
<i className='fa fa-angle-down' />
<select styleName='control-sortBy-select'
title={i18n.__('Select filter mode')}
value={config.sortBy}
value={sortBy}
onChange={(e) => this.handleSortByChange(e)}
>
<option title='Sort by update time' value='UPDATED_AT'>{i18n.__('Updated')}</option>

View File

@@ -152,3 +152,7 @@ body[data-theme="monokai"]
background-color $ui-monokai-backgroundColor
.sortableItemHelper
color: $ui-monokai-text-color
body[data-theme="default"]
.SideNav ::-webkit-scrollbar-thumb
background-color rgba(255, 255, 255, 0.3)

View File

@@ -16,7 +16,9 @@ export const DEFAULT_CONFIG = {
isSideNavFolded: false,
listWidth: 280,
navWidth: 200,
sortBy: 'UPDATED_AT', // 'CREATED_AT', 'UPDATED_AT', 'APLHABETICAL'
sortBy: {
default: 'UPDATED_AT' // 'CREATED_AT', 'UPDATED_AT', 'APLHABETICAL'
},
sortTagsBy: 'ALPHABETICAL', // 'ALPHABETICAL', 'COUNTER'
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
amaEnabled: true,

View File

@@ -82,9 +82,9 @@ function createAttachmentDestinationFolder (destinationStoragePath, noteKey) {
* @param noteKey Key of the current note
*/
function migrateAttachments (markdownContent, storagePath, noteKey) {
if (sander.existsSync(path.join(storagePath, 'images'))) {
if (noteKey !== undefined && sander.existsSync(path.join(storagePath, 'images'))) {
const attachments = getAttachmentsInMarkdownContent(markdownContent) || []
if (attachments !== []) {
if (attachments.length) {
createAttachmentDestinationFolder(storagePath, noteKey)
}
for (const attachment of attachments) {

View File

@@ -22,7 +22,7 @@ class SnippetList extends React.Component {
reloadSnippetList () {
dataApi.fetchSnippet().then(snippets => {
this.setState({snippets})
this.props.onSnippetSelect(snippets[0])
this.props.onSnippetSelect(this.props.currentSnippet)
})
}

76
dev-scripts/dev.js Normal file
View File

@@ -0,0 +1,76 @@
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const config = require('../webpack.config')
const signale = require('signale')
const { spawn } = require('child_process')
const electron = require('electron')
const port = 8080
let server = null
let firstRun = true
const options = {
publicPath: config.output.publicPath,
hot: true,
inline: true,
quiet: true
}
function startServer () {
config.plugins.push(new webpack.HotModuleReplacementPlugin())
config.entry.main.unshift(
`webpack-dev-server/client?http://localhost:${port}/`,
'webpack/hot/dev-server'
)
const compiler = webpack(config)
server = new WebpackDevServer(compiler, options)
return new Promise((resolve, reject) => {
server.listen(port, 'localhost', function (err) {
if (err) {
reject(err)
}
signale.success(`Webpack Dev Server listening at localhost:${port}`)
signale.watch(`Waiting for webpack to bundle...`)
compiler.plugin('done', stats => {
if (!stats.hasErrors()) {
signale.success(`Bundle success !`)
resolve()
} else {
if (!firstRun) {
console.log(stats.compilation.errors[0])
} else {
firstRun = false
reject(stats.compilation.errors[0])
}
}
})
})
})
}
function startElectron () {
spawn(electron, ['--hot', './index.js'])
.on('close', () => {
server.close()
})
.on('error', err => {
signale.error(err)
server.close()
})
.on('disconnect', () => {
server.close()
})
.on('exit', () => {
server.close()
})
}
startServer()
.then(() => {
startElectron()
signale.success('Electron started')
})
.catch(err => {
signale.error(err)
process.exit(1)
})

View File

@@ -2,10 +2,9 @@
This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
## Environments
* npm: 4.x
* node: 7.x
You should use `npm v4.x` because `$ grunt pre-build` fails on `v5.x`.
* npm: 6.x
* node: 8.x
## Development
@@ -21,17 +20,9 @@ $ yarn
Build and run.
```
$ yarn run dev-start
$ yarn run dev
```
This command runs `yarn run webpack` and `yarn run hot` in parallel. It is the same as running these commands in two terminals.
The `webpack` will watch for code changes and then apply them automatically.
If the following error occurs: `Failed to load resource: net::ERR_CONNECTION_REFUSED`, please reload Boostnote.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### Notice
> There are some cases where you have to refresh the app manually.
> 1. When editing a constructor method of a component
@@ -44,8 +35,6 @@ You can build the program by using `grunt`. However, we don't recommend this bec
So, we've prepared a separate script which just makes an executable file.
This build doesn't work on npm v5.3.0. So you need to use v5.2.0 when you build it.
```
grunt pre-build
```

View File

@@ -2,10 +2,9 @@
Diese Seite ist auch verfügbar in [Japanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Koreanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Vereinfachtem Chinesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [Französisch](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) und [Deutsch](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
## Umgebungen
* npm: 4.x
* node: 7.x
Du solltest `npm v4.x` benutzen weil `$ grunt pre-build` scheitert mit Version `v5.x`.
* npm: 6.x
* node: 8.x
## Entwicklung
@@ -21,17 +20,9 @@ $ yarn
Bauen und Ausführen.
```
$ yarn run dev-start
$ yarn run dev
```
Dieser Befehl startet `yarn run webpack` und `yarn run hot` parallel. Es hat den selben Effekt wie beide Befehle separat in zwei Terminals zu starten.
Das `webpack` überprüft den Code auf Änderungen und wendet diese dann automatisch an.
Wenn folgender Fehler passiert: `Failed to load resource: net::ERR_CONNECTION_REFUSED`, bitte Boostnote neu starten.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### Notiz
> Es gibt einige Fälle bei denen die App manuell zu refreshen ist.
> 1. Wenn eine "constructor method" einer Komponente manuell editiert wird.
@@ -44,11 +35,10 @@ Du kannst das Programm unter Verwendung von `grunt` bauen. Jedoch empfehlen wir
Deshalb haben wir ein separates Script vorbereitet welches eine ausführbare Datei erstellt.
Dieser build funktioniert nicht mit npm v5.3.0. Deshalb musst du für den Build die Version v5.2.0 verwenden.
```
grunt pre-build
```
Du findest die ausführbare Datein in dem Verzeichnis `dist`. Beachte, der auto updater funktioniert nicht da die app nicht signiert ist.
Wenn du es für notwendig erachtest, kannst du codesign or authenticode mit dieser ausführbaren Datei verwenden.

View File

@@ -2,10 +2,9 @@
Cette page est également disponible en [Anglais](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md), [Japonais](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Coréen](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russe](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Chinois Simplifié](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md) et en [Allemand](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md)
## Environnements
* npm: 4.x
* node: 7.x
Il est conseillé d'utiliser `npm v4.x` car `$ grunt pre-build` ne marche pas sur la `v5.x`.
* npm: 6.x
* node: 8.x
## Développement
@@ -20,17 +19,9 @@ $ yarn
Build et start
```
$ yarn run dev-start
$ yarn run dev
```
Cette commande lance `yarn run webpack` et `yarn run hot` en parallèle. Cela revient au même que si on utilisait ces deux commandes dans 2 terminaux.
La commande `webpack` va surveiller les changements de code et les appliquer automatiquement.
Si l'erreur suivante apparait : `Failed to load resource: net::ERR_CONNECTION_REFUSED`, relancez Boostnote.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### Notice
> Il y a certains cas où vous voudrez relancer l'application manuellement.
> 1. Quand vous éditez la méthode constructeur dans un composant
@@ -43,8 +34,6 @@ Vous pouvez build le programme en utilisant `grunt`. Cependant, nous ne recomman
Nous avons donc préparé un script séparé qui va rendre un fichier exécutable.
Le build ne fonctionne pas sur `npm v5.3.0`. Il faut donc utiliser `npm v5.2.0` quand vous faites le build.
```
grunt pre-build
```

View File

@@ -1,9 +1,15 @@
# Build
## 環境
* npm: 6.x
* node: 8.x
## 開発
Webpack HRMを使います。
次の命令から私達がしておいた設定を使うことができます。
Boostnoteの最上位ディレクトリにて以下のコマンドを実行して、
デフォルトの設定の開発環境を起動させます。
依存するパッケージをインストールします。
@@ -14,30 +20,20 @@ $ yarn
ビルドして実行します。
```
$ yarn run dev-start
$ yarn run dev
```
このコマンドは `yarn run webpack``yarn run hot`を並列に実行します。つまりこのコマンドは2つのターミナルで同時にこれらのコマンドを実行するのと同じことです。
そして、Webpackが自動的にコードの変更を確認し、それを適用してくれるようになります。
もし、 `Failed to load resource: net::ERR_CONNECTION_REFUSED`というエラーが起きた場合、Boostnoteをリロードしてください。
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### 注意
> 時々、直接リフレッシュをする必要があります。
> 1. コンポネントのコンストラクタ関数を編集する場合
> 2. 新しいCSSクラスを追加する場合(1.の理由と同じ: CSSクラス名はコンポネントごとに書きなおされまが、この作業はコンストラクタで行われます。)
> 1. コンポネントのコンストラクタ関数を編集する場合
> 2. 新しいCSSクラスを追加する場合(1.の理由と同じ: CSSクラス名はコンポネントごとに書きなおされまが、この作業はコンストラクタで行われます。)
## 配布
Gruntを使います。
実際の配布は`grunt`で実行できます。しかし、これにはCodesignとAuthenticodeの仮定が含まれるので、使っては行けないです
実際の配布は`grunt`で実行できます。しかし、これにはCodesignとAuthenticodeを実行するタスクが含まれるので、使用しないでください
それで、実行ファイルを作るスクリプトを用意しておきました。
このビルドはnpm v5.3.0では動かないのでv5.2.0で動かす必要があります。
代わりに、実行ファイルを作るスクリプトを用意しておきました。
```
grunt pre-build

View File

@@ -1,8 +1,9 @@
# Build
## 환경
* npm: 4.x
* node: 7.x
* npm: 6.x
* node: 8.x
`$ grunt pre-build``npm v5.x`에서 실행할 수 없기 때문에, 반드시 `npm v4.x`를 사용하셔야 합니다.
@@ -20,17 +21,9 @@ $ yarn
그 다음, 아래의 명령으로 빌드를 끝내고 자동적으로 어플리케이션을 실행합니다.
```
$ yarn run dev-start
$ yarn run dev
```
이 명령은 `yarn run webpack``yarn run hot`을 동시에 실행합니다. 이는 두개의 터미널에서 각각의 명령을 동시에 실행하는 것과 같습니다.
`Webpack`은 코드의 변화를 자동으로 탐지하여 적용시키는 역할을 합니다.
만약, `Failed to load resource: net::ERR_CONNECTION_REFUSED`과 같은 에러가 나타난다면 Boostnote를 리로드해주세요.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### 주의
> 가끔 직접 리프레쉬를 해주어야 하는 경우가 있습니다.
> 1. 콤포넌트의 컨스트럭터 함수를 수정할 경우
@@ -43,8 +36,6 @@ Boostnote에서는 배포 자동화를 위하여 그런트를 사용합니다.
그래서, 실행파일만을 만드는 스크립트를 준비해 뒀습니다.
이 빌드는 npm v5.3.0에서는 작동하지 않습니다. 그러므로, 성공적으로 빌드하기 위해서는 v5.2.0을 사용해야 합니다.
```
grunt pre-build
```

View File

@@ -1,10 +1,9 @@
# Сборка
## Используемые инструменты
* npm: 4.x
* node: 7.x
Вы должны использовать `npm v4.x`, так как `$ grunt pre-build` не работает в `v5.x`.
* npm: 6.x
* node: 8.x
## Разработка
@@ -20,17 +19,9 @@ $ yarn
Соберите и запустите.
```
$ yarn run dev-start
$ yarn run dev
```
Эта команда выполняет `yarn run webpack` и `yarn run hot` параллельно. Результат будет такой же, если вы выполните эти две команды раздельно.
`Webpack` будет следить за изменениями в коде и будет применять их автоматически.
Если возникает следующая ошибка: `Failed to load resource: net::ERR_CONNECTION_REFUSED`, пожалуйста, перезапустите Boostnote.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### Примечание
> В некоторых случаях вам необходимо обновить приложение вручную.
> 1. При редактировании метода конструктора компонента
@@ -41,9 +32,7 @@ $ yarn run dev-start
Мы используем Grunt для автоматического деплоя.
Вы можете создать задачу, используя `grunt`. Однако мы не рекомендуем этого делать, так как задача по умолчанию включает в себя код и аутентификацию.
Мы подготовили отдельный скрипт, который просто создает исполняемый файл:
This build doesn't work on npm v5.3.0. So you need to use v5.2.0 when you build it.
Мы подготовили отдельный скрипт, который просто создает исполняемый файл.
```
grunt pre-build

View File

@@ -1,37 +1,27 @@
# 构建Boostnote
## 环境
* npm: 4.x
* node: 7.x
因为`$ grunt pre-build`的问题,您只能使用`npm v4.x`而不能使用`npm v5.x`
* npm: 6.x
* node: 8.x
## 开发
我们使用Webpack HMR来开发Boostnote。
在代码根目录下运行下列指令可以以默认配置运行Boostnote。
我们使用Webpack HMR来开发Boostnote。
在代码根目录下运行下列指令可以以默认配置运行Boostnote。
### 首先使用yarn安装所需的依赖包。
### 首先使用yarn安装所需的依赖包。
```
$ yarn
```
### 接着编译并且运行Boostnote。
### 接着编译并且运行Boostnote。
```
$ yarn run dev-start
$ yarn run dev
```
这个指令相当于在两个终端内同时运行`yarn run webpack``yarn run hot`
如果出现错误`Failed to load resource: net::ERR_CONNECTION_REFUSED`请尝试重新运行Boostnote。
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
### 然后您就可以进行开发了
当您对代码作出更改的时候,`webpack`会自动抓取并应用所有代码更改。
> ### 提示
> 在如下情况中您可能需要重新运行Boostnote才能应用代码更改
> 1. 当您在修改了一个组件的构造函数的时候When editing a constructor method of a component
@@ -39,18 +29,16 @@ $ yarn run dev-start
## 部署
我们使用Grunt来自动部署Boostnote。
因为部署需要协同设计(codesign)与验证码(authenticode),所以您可以但我们不建议通过`grunt`来部署。
所以我们准备了一个脚本文件来生成执行文件。
我们使用Grunt来自动部署Boostnote。
因为部署需要协同设计(codesign)与验证码(authenticode),所以您可以但我们不建议通过`grunt`来部署。
所以我们准备了一个脚本文件来生成执行文件。
```
grunt pre-build
```
您只能使用`npm v5.2.0`而不能使用`npm v5.3.0`
接下来您就可以在`dist`目录中找到可执行文件。
接下来您就可以在`dist`目录中找到可执行文件。
> ### 提示
> 因为此可执行文件并没有被注册,所以自动更新不可用。
> 如果需要,您也可将协同设计(codesign)与验证码(authenticode)使用于这个可执行文件中。
> 如果需要,您也可将协同设计(codesign)与验证码(authenticode)使用于这个可执行文件中。

View File

@@ -2,10 +2,9 @@
此文件還提供下列的語言 [日文](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [韓文](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [俄文](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [簡體中文](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [法文](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) and [德文](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
## 環境
* npm: 4.x
* node: 7.x
`$ grunt pre-build``npm v5.x` 有問題,所以只能用 `npm v4.x`
* npm: 6.x
* node: 8.x
## 開發
@@ -22,18 +21,9 @@ $ yarn
**開始開發**
```
$ yarn run dev-start
$ yarn run dev
```
上述指令同時運行了 `yarn run webpack``yarn run hot`,相當於將這兩個指令在不同的 terminal 中運行。
`webpack` 會同時監控修改過的程式碼,並
The `webpack` will watch for code changes and then apply them automatically.
If the following error occurs: `Failed to load resource: net::ERR_CONNECTION_REFUSED`, please reload Boostnote.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### Notice
> There are some cases where you have to refresh the app manually.
> 1. When editing a constructor method of a component
@@ -46,8 +36,6 @@ You can build the program by using `grunt`. However, we don't recommend this bec
So, we've prepared a separate script which just makes an executable file.
This build doesn't work on npm v5.3.0. So you need to use v5.2.0 when you build it.
```
grunt pre-build
```

View File

@@ -218,6 +218,16 @@ const edit = {
label: 'Select All',
accelerator: 'Command+A',
selector: 'selectAll:'
},
{
type: 'separator'
},
{
label: 'Add Tag',
accelerator: 'CommandOrControl+Shift+T',
click () {
mainWindow.webContents.send('editor:add-tag')
}
}
]
}

View File

@@ -7,9 +7,11 @@ const config = new Config()
const _ = require('lodash')
var showMenu = process.platform !== 'win32'
const windowSize = config.get('windowsize') || { width: 1080, height: 720 }
const windowSize = config.get('windowsize') || { x: null, y: null, width: 1080, height: 720 }
const mainWindow = new BrowserWindow({
x: windowSize.x,
y: windowSize.y,
width: windowSize.width,
height: windowSize.height,
minWidth: 500,
@@ -59,6 +61,7 @@ if (process.platform === 'darwin') {
}
mainWindow.on('resize', _.throttle(storeWindowSize, 500))
mainWindow.on('move', _.throttle(storeWindowSize, 500))
function storeWindowSize () {
try {

View File

@@ -1,72 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css" media="screen" charset="utf-8">
<link rel="shortcut icon" href="../resources/favicon.ico">
<link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="../node_modules/katex/dist/katex.min.css">
<link rel="stylesheet" href="../node_modules/codemirror/addon/dialog/dialog.css">
<title>Boostnote</title>
<style>
@font-face {
font-family: 'OpenSans';
src: url('../resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
@font-face {
font-family: 'Lato';
src: url('../resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
#loadingCover{
background-color: #f4f4f4;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
padding: 65px 0;
font-family: sans-serif;
}
#loadingCover img{
display: block;
margin: 75px auto 5px;
width: 160px;
height: 160px;
}
#loadingCover .message{
font-size: 30px;
text-align: center;
line-height: 1.6;
font-weight: 100;
color: #888;
}
.CodeEditor {
opacity: 1 !important;
pointer-events: auto !important;
}
.CodeMirror-ruler {
border-left-color: rgba(142, 142, 142, 0.5);
mix-blend-mode: difference;
}
@font-face {
font-family: 'OpenSans';
src: url('../resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
@font-face {
font-family: 'Lato';
src: url('../resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
#loadingCover {
background-color: #f4f4f4;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
box-sizing: border-box;
padding: 65px 0;
font-family: sans-serif;
}
#loadingCover img {
display: block;
margin: 75px auto 5px;
width: 160px;
height: 160px;
}
#loadingCover .message {
font-size: 30px;
text-align: center;
line-height: 1.6;
font-weight: 100;
color: #888;
}
.CodeEditor {
opacity: 1 !important;
pointer-events: auto !important;
}
.CodeMirror-ruler {
border-left-color: rgba(142, 142, 142, 0.5);
mix-blend-mode: difference;
}
</style>
</head>
<body>
<div id="loadingCover">
<img src="../resources/app.png">
<div class='message'><i class="fa fa-spinner fa-spin" spin></i></div>
<div class='message'>
<i class="fa fa-spinner fa-spin" spin></i>
</div>
</div>
<div id="content"></div>
@@ -130,4 +141,5 @@
}
</style>
</body>
</html>
</html>

35
package-lock.json generated
View File

@@ -1,35 +0,0 @@
{
"name": "boost",
"version": "0.10.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"i18n-2": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.2.tgz",
"integrity": "sha512-Rdh6vfpNhL7q61cNf27x7QGULTi1TcGLVdFb5OJ6dOiJo+EkOTqEg0+3xgyeEMgYhopUBsh2IiSkFkjM+EhEmA==",
"requires": {
"debug": "3.1.0",
"sprintf": "0.1.5"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"sprintf": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz",
"integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8="
}
}
}

View File

@@ -7,17 +7,15 @@
"license": "GPL-3.0",
"scripts": {
"start": "electron ./index.js",
"hot": "electron ./index.js --hot",
"webpack": "webpack-dev-server --hot --inline --config webpack.config.js",
"compile": "grunt compile",
"test": "PWD=$(pwd) NODE_ENV=test ava --serial",
"jest": "jest",
"fix": "eslint . --fix",
"lint": "eslint .",
"dev-start": "concurrently --kill-others \"npm run webpack\" \"npm run hot\""
"dev": "node dev-scripts/dev.js"
},
"config": {
"electron-version": "2.0.3"
"electron-version": "2.0.7"
},
"repository": {
"type": "git",
@@ -57,7 +55,7 @@
"chart.js": "^2.7.2",
"codemirror": "^5.39.0",
"codemirror-mode-elixir": "^1.1.1",
"electron-config": "^0.2.1",
"electron-config": "^1.0.0",
"electron-gh-releases": "^2.0.2",
"escape-string-regexp": "^1.0.5",
"file-url": "^2.0.2",
@@ -73,7 +71,7 @@
"lodash": "^4.11.1",
"lodash-move": "^1.1.1",
"markdown-it": "^6.0.1",
"markdown-it-admonition": "https://github.com/johannbre/markdown-it-admonition.git",
"markdown-it-admonition": "^1.0.4",
"markdown-it-emoji": "^1.1.1",
"markdown-it-footnote": "^3.0.0",
"markdown-it-imsize": "^2.0.1",
@@ -120,8 +118,8 @@
"css-loader": "^0.19.0",
"devtron": "^1.1.0",
"dom-storage": "^2.0.2",
"electron": "2.0.3",
"electron-packager": "^6.0.0",
"electron": "2.0.7",
"electron-packager": "^12.0.0",
"eslint": "^3.13.1",
"eslint-config-standard": "^6.2.1",
"eslint-config-standard-jsx": "^3.2.0",
@@ -145,6 +143,7 @@
"react-router": "^2.4.0",
"react-router-redux": "^4.0.4",
"react-test-renderer": "^15.6.2",
"signale": "^1.2.1",
"standard": "^8.4.0",
"style-loader": "^0.12.4",
"stylus": "^0.52.4",

View File

@@ -319,6 +319,21 @@ it('should remove the all ":storage" and noteKey references', function () {
expect(actual).toEqual(expectedOutput)
})
it('should make sure that "removeStorageAndNoteReferences" works with markdown content as well', function () {
const noteKey = 'noteKey'
const testInput =
'Test input' +
'![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + noteKey + path.sep + 'image.jpg](imageName}) \n' +
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + noteKey + path.sep + 'pdf.pdf](pdf})'
const expectedOutput =
'Test input' +
'![' + systemUnderTest.DESTINATION_FOLDER + path.sep + 'image.jpg](imageName}) \n' +
'[' + systemUnderTest.DESTINATION_FOLDER + path.sep + 'pdf.pdf](pdf})'
const actual = systemUnderTest.removeStorageAndNoteReferences(testInput, noteKey)
expect(actual).toEqual(expectedOutput)
})
it('should delete the correct attachment folder if a note is deleted', function () {
const dummyStorage = {path: 'dummyStoragePath'}
const storageKey = 'storageKey'

View File

@@ -33,13 +33,38 @@ test('escapeHtmlCharacters should NOT skip code block if that option is NOT enab
t.is(actual, expected)
})
test('escapeHtmlCharacters should NOT escape & character if it\'s a part of an escaped character', t => {
test("escapeHtmlCharacters should NOT escape & character if it's a part of an escaped character", t => {
const input = 'Do not escape &amp; or &quot; but do escape &'
const expected = 'Do not escape &amp; or &quot; but do escape &amp;'
const actual = escapeHtmlCharacters(input)
t.is(actual, expected)
})
test('escapeHtmlCharacters should skip char if in code block', t => {
const input = `
\`\`\`
<dontescapeme>
\`\`\`
das<das>dasd
dasdasdasd
\`\`\`
<dontescapeme>
\`\`\`
`
const expected = `
\`\`\`
<dontescapeme>
\`\`\`
das&lt;das&gt;dasd
dasdasdasd
\`\`\`
<dontescapeme>
\`\`\`
`
const actual = escapeHtmlCharacters(input, { detectCodeBlock: true })
t.is(actual, expected)
})
test('escapeHtmlCharacters should return the correct result', t => {
const input = '& < > " \''
const expected = '&amp; &lt; &gt; &quot; &#39;'

View File

@@ -12,6 +12,7 @@ test('getTodoStatus should return a correct hash object', t => {
['- [ ] a\n- [x] a\n', { total: 2, completed: 1 }],
['+ [ ] a\n', { total: 1, completed: 0 }],
['+ [ ] a\n+ [x] a\n', { total: 2, completed: 1 }],
['+ [ ] a\n+ [X] a\n', { total: 2, completed: 1 }],
['+ [ ] a\n+ [testx] a\n', { total: 1, completed: 0 }],
['+ [ ] a\n+ [xtest] a\n', { total: 1, completed: 0 }],
['+ [ ] a\n+ foo[x]bar a\n', { total: 1, completed: 0 }],

View File

@@ -5,7 +5,7 @@ const { parse } = require('browser/lib/RcParser')
// Unit test
test('RcParser should return a json object', t => {
const validJson = { 'editor': { 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Control + L' }, 'listWidth': 135, 'navWidth': 135 }
const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': 'UPDATED_AT', 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 }
const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': { 'default': 'UPDATED_AT' }, 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 }
// [input, expected]
const validTestCases = [

View File

@@ -4,20 +4,24 @@ const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin')
var config = {
entry: {
main: './browser/main/index.js'
main: ['./browser/main/index.js']
},
resolve: {
extensions: ['', '.js', '.jsx', '.styl'],
packageMains: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main'],
packageMains: [
'webpack',
'browser',
'web',
'browserify',
['jam', 'main'],
'main'
],
alias: {
'lib': path.join(__dirname, 'lib'),
'browser': path.join(__dirname, 'browser')
lib: path.join(__dirname, 'lib'),
browser: path.join(__dirname, 'browser')
}
},
plugins: [
new webpack.NoErrorsPlugin(),
new NodeTargetPlugin()
],
plugins: [new webpack.NoErrorsPlugin(), new NodeTargetPlugin()],
stylus: {
use: [require('nib')()],
import: [
@@ -43,14 +47,13 @@ var config = {
react: 'var React',
'react-dom': 'var ReactDOM',
'react-redux': 'var ReactRedux',
'codemirror': 'var CodeMirror',
'redux': 'var Redux',
'raphael': 'var Raphael',
'flowchart': 'var flowchart',
codemirror: 'var CodeMirror',
redux: 'var Redux',
raphael: 'var Raphael',
flowchart: 'var flowchart',
'sequence-diagram': 'var Diagram'
}
]
}
module.exports = config

365
yarn.lock
View File

@@ -358,6 +358,19 @@ asar@^0.12.0:
mksnapshot "^0.3.0"
tmp "0.0.28"
asar@^0.14.0:
version "0.14.3"
resolved "https://registry.yarnpkg.com/asar/-/asar-0.14.3.tgz#c72a81542a48e3bca459fb1b07ee2b6adfae265d"
dependencies:
chromium-pickle-js "^0.2.0"
commander "^2.9.0"
cuint "^0.2.1"
glob "^6.0.4"
minimatch "^3.0.3"
mkdirp "^0.5.0"
mksnapshot "^0.3.0"
tmp "0.0.28"
asar@^0.9.0:
version "0.9.1"
resolved "https://registry.yarnpkg.com/asar/-/asar-0.9.1.tgz#b2f2fe1b49c163013bdb229d6eba2d2078ff5cc4"
@@ -438,6 +451,10 @@ atob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a"
author-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450"
auto-bind@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-1.2.0.tgz#8b7e318aad53d43ba8a8ecaf0066d85d5f798cd6"
@@ -1222,9 +1239,9 @@ balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
base64-js@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978"
base64-js@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
base64-js@^1.0.2:
version "1.3.0"
@@ -1277,7 +1294,7 @@ bluebird@^2.9.30:
version "2.11.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
bluebird@^3.0.0, bluebird@^3.1.1, bluebird@^3.3.4:
bluebird@^3.0.0, bluebird@^3.1.1, bluebird@^3.3.4, bluebird@^3.5.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@@ -1396,6 +1413,21 @@ buf-compare@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
buffer-alloc@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
buffer-from@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
@@ -1572,7 +1604,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1:
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
dependencies:
@@ -1860,6 +1892,10 @@ commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
compare-version@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
compare-versions@^3.1.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.2.1.tgz#a49eb7689d4caaf0b6db5220173fd279614000f7"
@@ -1928,14 +1964,15 @@ concurrently@^3.4.0:
supports-color "^3.2.3"
tree-kill "^1.1.0"
conf@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/conf/-/conf-0.11.2.tgz#879f479267600483e502583462ca4063fc9779b2"
conf@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/conf/-/conf-1.4.0.tgz#1ea66c9d7a9b601674a5bb9d2b8dc3c726625e67"
dependencies:
dot-prop "^3.0.0"
env-paths "^0.3.0"
mkdirp "^0.5.1"
pkg-up "^1.0.0"
dot-prop "^4.1.0"
env-paths "^1.0.0"
make-dir "^1.0.0"
pkg-up "^2.0.0"
write-file-atomic "^2.3.0"
configstore@^3.0.0:
version "3.1.2"
@@ -2464,7 +2501,7 @@ debug-log@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f"
debug@*, debug@^3.0.1, debug@^3.1.0:
debug@*, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@@ -2692,12 +2729,6 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
dot-prop@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
dependencies:
is-obj "^1.0.0"
dot-prop@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
@@ -2728,24 +2759,11 @@ ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
electron-config@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/electron-config/-/electron-config-0.2.1.tgz#7e12c26412d06bf3ed3896d0479df162986b95ba"
electron-config@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/electron-config/-/electron-config-1.0.0.tgz#069d044cc794f04784ae72f12916725d3c8c39af"
dependencies:
conf "^0.11.1"
electron-download@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-2.2.1.tgz#3d78af3645c96435e3bf3df9b882a14cc2ca294c"
dependencies:
debug "^2.2.0"
home-path "^1.0.1"
minimist "^1.2.0"
mkdirp "^0.5.0"
mv "^2.0.3"
nugget "^1.5.1"
path-exists "^1.0.0"
rc "^1.1.2"
conf "^1.0.0"
electron-download@^3.0.1:
version "3.3.0"
@@ -2761,6 +2779,20 @@ electron-download@^3.0.1:
semver "^5.3.0"
sumchecker "^1.2.0"
electron-download@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.0.tgz#bf932c746f2f87ffcc09d1dd472f2ff6b9187845"
dependencies:
debug "^2.2.0"
env-paths "^1.0.0"
fs-extra "^2.0.0"
minimist "^1.2.0"
nugget "^2.0.0"
path-exists "^3.0.0"
rc "^1.1.2"
semver "^5.3.0"
sumchecker "^2.0.1"
electron-gh-releases@^2.0.2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/electron-gh-releases/-/electron-gh-releases-2.0.4.tgz#198c07a0970fb8e80fcc67bd0b4198a010923dc3"
@@ -2797,34 +2829,38 @@ electron-installer-redhat@^0.3.0:
word-wrap "^1.1.0"
yargs "^6.0.0"
electron-osx-sign@^0.3.0:
version "0.3.2"
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.3.2.tgz#88fa7d6ebadb5d9c79368b96491a0d8c4630146e"
electron-osx-sign@^0.4.1:
version "0.4.10"
resolved "https://registry.yarnpkg.com/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz#be4f3b89b2a75a1dc5f1e7249081ab2929ca3a26"
dependencies:
debug "^2.2.0"
minimist "^1.1.1"
run-series "^1.1.1"
bluebird "^3.5.0"
compare-version "^0.1.2"
debug "^2.6.8"
isbinaryfile "^3.0.2"
minimist "^1.2.0"
plist "^2.1.0"
electron-packager@^6.0.0:
version "6.0.2"
resolved "https://registry.yarnpkg.com/electron-packager/-/electron-packager-6.0.2.tgz#8ee00669fe8a36309502732fcfed117d7ee9ac29"
electron-packager@^12.0.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/electron-packager/-/electron-packager-12.1.0.tgz#048dd4ff3848be19c5873c315b5b312df6215328"
dependencies:
asar "^0.11.0"
electron-download "^2.0.0"
electron-osx-sign "^0.3.0"
asar "^0.14.0"
debug "^3.0.0"
electron-download "^4.0.0"
electron-osx-sign "^0.4.1"
extract-zip "^1.0.3"
fs-extra "^0.26.5"
get-package-info "0.0.2"
minimist "^1.1.1"
mkdirp "^0.5.0"
mv "^2.0.3"
ncp "^2.0.0"
object-assign "^4.0.1"
plist "^1.1.0"
rcedit "^0.5.0"
fs-extra "^5.0.0"
galactus "^0.2.1"
get-package-info "^1.0.0"
nodeify "^1.0.1"
parse-author "^2.0.0"
pify "^3.0.0"
plist "^2.0.0"
rcedit "^1.0.0"
resolve "^1.1.6"
rimraf "^2.3.2"
run-series "^1.1.1"
sanitize-filename "^1.6.0"
semver "^5.3.0"
yargs-parser "^10.0.0"
electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.47:
version "1.3.48"
@@ -2841,9 +2877,9 @@ electron-winstaller@^2.2.0:
lodash.template "^4.2.2"
temp "^0.8.3"
electron@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.3.tgz#8e591e820cae2ccdb0c3fd74c6d07834913fc133"
electron@2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.7.tgz#f7ce410433298e319032ce31f0e6ffd709ff052c"
dependencies:
"@types/node" "^8.0.24"
electron-download "^3.0.1"
@@ -2882,9 +2918,9 @@ entities@^1.1.1, entities@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
env-paths@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-0.3.1.tgz#c30ccfcbc30c890943dc08a85582517ef00da463"
env-paths@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
equal-length@^1.0.0:
version "1.0.1"
@@ -3577,6 +3613,13 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
flora-colossus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/flora-colossus/-/flora-colossus-1.0.0.tgz#54729c361edecee014dd441679e1a37c1d773a45"
dependencies:
debug "^3.1.0"
fs-extra "^4.0.0"
flowchart.js@^1.6.5:
version "1.11.0"
resolved "https://registry.yarnpkg.com/flowchart.js/-/flowchart.js-1.11.0.tgz#e8df60e69d08df90c07ffb09e857d555d8981fc7"
@@ -3653,7 +3696,7 @@ fs-extra@0.18.2:
jsonfile "^2.0.0"
rimraf "^2.2.8"
fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.5, fs-extra@^0.26.7:
fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.7:
version "0.26.7"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9"
dependencies:
@@ -3681,6 +3724,21 @@ fs-extra@^1.0.0:
jsonfile "^2.1.0"
klaw "^1.0.0"
fs-extra@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-2.1.2.tgz#046c70163cef9aad46b0e4a7fa467fb22d71de35"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^2.1.0"
fs-extra@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
dependencies:
graceful-fs "^4.1.2"
jsonfile "^4.0.0"
universalify "^0.1.0"
fs-extra@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd"
@@ -3723,6 +3781,14 @@ function-name-support@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/function-name-support/-/function-name-support-0.2.0.tgz#55d3bfaa6eafd505a50f9bc81fdf57564a0bb071"
galactus@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/galactus/-/galactus-0.2.1.tgz#cbed2d20a40c1f5679a35908e2b9415733e78db9"
dependencies:
debug "^3.1.0"
flora-colossus "^1.0.0"
fs-extra "^4.0.0"
gar@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/gar/-/gar-1.0.3.tgz#cd6e954dff11821697a9ed5852c7ac5f18df02ce"
@@ -3761,13 +3827,14 @@ get-folder-size@^1.0.0:
async "^1.4.2"
gar "^1.0.2"
get-package-info@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/get-package-info/-/get-package-info-0.0.2.tgz#72c38fbee2e76728424a00dc14e24dd1a28c2391"
get-package-info@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/get-package-info/-/get-package-info-1.0.0.tgz#6432796563e28113cd9474dbbd00052985a4999c"
dependencies:
bluebird "^3.1.1"
debug "^2.2.0"
lodash.get "^4.0.0"
resolve "^1.1.6"
read-pkg-up "^2.0.0"
get-port@^3.0.0:
version "3.2.0"
@@ -3840,7 +3907,7 @@ glob@^5.0.5:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^6.0.1, glob@^6.0.3, glob@^6.0.4:
glob@^6.0.3, glob@^6.0.4:
version "6.0.4"
resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
dependencies:
@@ -4715,6 +4782,10 @@ is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
is-promise@~1, is-promise@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-1.0.1.tgz#31573761c057e33c2e91aab9e96da08cefbe76e5"
is-property@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
@@ -4775,6 +4846,12 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isbinaryfile@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80"
dependencies:
buffer-alloc "^1.2.0"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -5576,10 +5653,6 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
lodash@^3.5.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.0.0, lodash@^4.0.1, lodash@^4.11.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.6.1:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@@ -5650,9 +5723,9 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
"markdown-it-admonition@https://github.com/johannbre/markdown-it-admonition.git":
version "1.0.2"
resolved "https://github.com/johannbre/markdown-it-admonition.git#e0c0fcd59e9119d6d60ed209aa3d0f1177ec0166"
markdown-it-admonition@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/markdown-it-admonition/-/markdown-it-admonition-1.0.4.tgz#d7bbc7eb1fe6168fc8cc304de7a9d8c993acb2f5"
markdown-it-emoji@^1.1.1:
version "1.4.0"
@@ -6032,14 +6105,6 @@ mute-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
mv@^2.0.3:
version "2.1.1"
resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2"
dependencies:
mkdirp "~0.5.1"
ncp "~2.0.0"
rimraf "~2.4.0"
nan@^2.9.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
@@ -6069,10 +6134,6 @@ natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
ncp@^2.0.0, ncp@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
needle@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d"
@@ -6175,6 +6236,13 @@ node-uuid@~1.4.0:
version "1.4.8"
resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
nodeify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/nodeify/-/nodeify-1.0.1.tgz#64ab69a7bdbaf03ce107b4f0335c87c0b9e91b1d"
dependencies:
is-promise "~1.0.0"
promise "~1.3.0"
nopt@^3.0.1:
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
@@ -6248,18 +6316,6 @@ npmlog@^4.0.2:
gauge "~2.7.3"
set-blocking "~2.0.0"
nugget@^1.5.1:
version "1.6.2"
resolved "https://registry.yarnpkg.com/nugget/-/nugget-1.6.2.tgz#88ca6e03ba5706a99173f5da0902593d6bcae107"
dependencies:
debug "^2.1.3"
minimist "^1.1.0"
pretty-bytes "^1.0.2"
progress-stream "^1.1.0"
request "^2.45.0"
single-line-log "^0.4.1"
throttleit "0.0.2"
nugget@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.1.tgz#201095a487e1ad36081b3432fa3cada4f8d071b0"
@@ -6498,6 +6554,12 @@ pako@~0.2.0:
version "0.2.9"
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
parse-author@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/parse-author/-/parse-author-2.0.0.tgz#d3460bf1ddd0dfaeed42da754242e65fb684a81f"
dependencies:
author-regex "^1.0.0"
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@@ -6548,10 +6610,6 @@ path-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
path-exists@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081"
path-exists@^2.0.0, path-exists@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
@@ -6636,7 +6694,7 @@ pinkie@^2.0.0:
version "2.0.4"
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
pkg-conf@^2.0.0:
pkg-conf@^2.0.0, pkg-conf@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/pkg-conf/-/pkg-conf-2.1.0.tgz#2126514ca6f2abfebd168596df18ba57867f0058"
dependencies:
@@ -6663,19 +6721,18 @@ pkg-dir@^2.0.0:
dependencies:
find-up "^2.1.0"
pkg-up@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26"
pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
dependencies:
find-up "^1.0.0"
find-up "^2.1.0"
plist@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/plist/-/plist-1.2.0.tgz#084b5093ddc92506e259f874b8d9b1afb8c79593"
plist@^2.0.0, plist@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025"
dependencies:
base64-js "0.0.8"
util-deprecate "1.0.2"
xmlbuilder "4.0.0"
base64-js "1.2.0"
xmlbuilder "8.2.2"
xmldom "0.1.x"
plur@^2.0.0:
@@ -7005,6 +7062,12 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
promise@~1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-1.3.0.tgz#e5cc9a4c8278e4664ffedc01c7da84842b040175"
dependencies:
is-promise "~1"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0:
version "15.6.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
@@ -7123,9 +7186,9 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
rcedit@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-0.5.1.tgz#d0bdcf5d280a9d1c29da6f118ccce2ce153cef1d"
rcedit@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/rcedit/-/rcedit-1.1.0.tgz#ae21c28d4efdd78e95fcab7309a5dd084920b16a"
react-codemirror@^0.3.0:
version "0.3.0"
@@ -7610,7 +7673,7 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
rimraf@^2.2.8, rimraf@^2.3.2, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1:
rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
dependencies:
@@ -7620,12 +7683,6 @@ rimraf@~2.2.6, rimraf@~2.2.8:
version "2.2.8"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
rimraf@~2.4.0:
version "2.4.5"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da"
dependencies:
glob "^6.0.1"
ripemd160@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce"
@@ -7644,10 +7701,6 @@ run-parallel@^1.1.2:
version "1.1.9"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
run-series@^1.1.1:
version "1.1.8"
resolved "https://registry.yarnpkg.com/run-series/-/run-series-1.1.8.tgz#2c4558f49221e01cd6371ff4e0a1e203e460fc36"
rw@1:
version "1.3.3"
resolved "http://registry.npm.taobao.org/rw/download/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
@@ -7702,6 +7755,12 @@ sane@^2.0.0:
optionalDependencies:
fsevents "^1.2.3"
sanitize-filename@^1.6.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.1.tgz#612da1c96473fa02dccda92dcd5b4ab164a6772a"
dependencies:
truncate-utf8-bytes "^1.0.0"
sanitize-html@^1.18.2:
version "1.18.2"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.18.2.tgz#61877ba5a910327e42880a28803c2fbafa8e4642"
@@ -7858,9 +7917,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
single-line-log@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-0.4.1.tgz#87a55649f749d783ec0dcd804e8140d9873c7cee"
signale@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/signale/-/signale-1.2.1.tgz#fbd4b952603ea3315dbe9e88f4f482f336cee828"
dependencies:
chalk "^2.3.2"
figures "^2.0.0"
pkg-conf "^2.1.0"
single-line-log@^1.1.2:
version "1.1.2"
@@ -8299,6 +8362,12 @@ sumchecker@^1.2.0:
debug "^2.2.0"
es6-promise "^4.0.5"
sumchecker@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e"
dependencies:
debug "^2.2.0"
supertap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supertap/-/supertap-1.0.0.tgz#bd9751c7fafd68c68cf8222a29892206a119fa9e"
@@ -8551,6 +8620,12 @@ trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
truncate-utf8-bytes@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
dependencies:
utf8-byte-length "^1.0.1"
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@@ -8768,7 +8843,11 @@ user-home@^2.0.0:
dependencies:
os-homedir "^1.0.0"
util-deprecate@1.0.2, util-deprecate@~1.0.1:
utf8-byte-length@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -9059,7 +9138,7 @@ write-file-atomic@^1.1.4:
imurmurhash "^0.1.4"
slide "^1.1.5"
write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
write-file-atomic@^2.0.0, write-file-atomic@^2.1.0, write-file-atomic@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
dependencies:
@@ -9117,11 +9196,9 @@ xml2js@0.4.17:
sax ">=0.6.0"
xmlbuilder "^4.1.0"
xmlbuilder@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.0.0.tgz#98b8f651ca30aa624036f127d11cc66dc7b907a3"
dependencies:
lodash "^3.5.0"
xmlbuilder@8.2.2:
version "8.2.2"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773"
xmlbuilder@^4.1.0:
version "4.2.1"
@@ -9159,6 +9236,12 @@ yallist@^3.0.0, yallist@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
yargs-parser@^10.0.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
dependencies:
camelcase "^4.1.0"
yargs-parser@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"