From a537f9762b6a9a96bc94c999e2ebdd0101a76e04 Mon Sep 17 00:00:00 2001 From: Roman Klopsch Date: Wed, 28 Feb 2018 16:00:42 +0100 Subject: [PATCH 01/19] Fix missing progress bar in note list --- browser/components/NoteItem.styl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/NoteItem.styl b/browser/components/NoteItem.styl index f213348a..5203ccea 100644 --- a/browser/components/NoteItem.styl +++ b/browser/components/NoteItem.styl @@ -117,7 +117,7 @@ $control-height = 30px font-size 12px line-height 20px overflow ellipsis - display flex + display block .item-bottom-tagList flex 1 From 88ac6c6fc62e19581108cfcdb91d3834ca441a89 Mon Sep 17 00:00:00 2001 From: pfftdammitchris Date: Wed, 28 Feb 2018 16:34:42 -0800 Subject: [PATCH 02/19] Sort tags alphabetically --- browser/main/SideNav/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 6d05e37b..a08d2b39 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -117,9 +117,9 @@ class SideNav extends React.Component { tagListComponent () { const { data, location } = this.props - const tagList = data.tagNoteMap.map((tag, name) => { + const tagList = _.sortBy(data.tagNoteMap.map((tag, name) => { return { name, size: tag.size } - }) + }), ['name']) return ( tagList.map(tag => { return ( From 5aa7ef573837c76925d10e01fa3820d5037d321f Mon Sep 17 00:00:00 2001 From: pfftdammitchris Date: Wed, 28 Feb 2018 18:33:14 -0800 Subject: [PATCH 03/19] Folders in the side nav are now draggable --- browser/components/StorageItem.js | 48 ++++++++++++++++------------- browser/main/SideNav/StorageItem.js | 15 +++++---- browser/main/SideNav/index.js | 17 +++++++++- 3 files changed, 51 insertions(+), 29 deletions(-) diff --git a/browser/components/StorageItem.js b/browser/components/StorageItem.js index 25be3c57..c92579da 100644 --- a/browser/components/StorageItem.js +++ b/browser/components/StorageItem.js @@ -6,6 +6,7 @@ import React from 'react' import styles from './StorageItem.styl' import CSSModules from 'browser/lib/CSSModules' import _ from 'lodash' +import { SortableHandle } from 'react-sortable-hoc' /** * @param {boolean} isActive @@ -23,32 +24,35 @@ import _ from 'lodash' const StorageItem = ({ isActive, handleButtonClick, handleContextMenu, folderName, folderColor, isFolded, noteCount, handleDrop, handleDragEnter, handleDragLeave -}) => ( - -) + {(!isFolded && _.isNumber(noteCount)) && + {noteCount} + } + {isFolded && + + {folderName} + + } + + ) +} StorageItem.propTypes = { isActive: PropTypes.bool.isRequired, diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index 5d7e6005..89b654c0 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -9,6 +9,7 @@ import RenameFolderModal from 'browser/main/modals/RenameFolderModal' import dataApi from 'browser/main/lib/dataApi' import StorageItemChild from 'browser/components/StorageItem' import _ from 'lodash' +import { SortableElement } from 'react-sortable-hoc' const { remote } = require('electron') const { Menu, dialog } = remote @@ -236,7 +237,8 @@ class StorageItem extends React.Component { render () { const { storage, location, isFolded, data, dispatch } = this.props const { folderNoteMap, trashedSet } = data - const folderList = storage.folders.map((folder) => { + const SortableStorageItemChild = SortableElement(StorageItemChild) + const folderList = storage.folders.map((folder, index) => { const isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key))) const noteSet = folderNoteMap.get(storage.key + '-' + folder.key) @@ -250,8 +252,9 @@ class StorageItem extends React.Component { noteCount = noteSet.size - trashedNoteCount } return ( - this.handleFolderButtonClick(folder.key)(e)} handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)} @@ -273,9 +276,9 @@ class StorageItem extends React.Component { key={storage.key} >
this.handleHeaderContextMenu(e)} > diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 6d05e37b..19433477 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -17,6 +17,7 @@ import EventEmitter from 'browser/main/lib/eventEmitter' import PreferenceButton from './PreferenceButton' import ListButton from './ListButton' import TagButton from './TagButton' +import {SortableContainer} from 'react-sortable-hoc' class SideNav extends React.Component { // TODO: should not use electron stuff v0.7 @@ -68,6 +69,17 @@ class SideNav extends React.Component { router.push('/alltags') } + onSortEnd (storage) { + return ({oldIndex, newIndex}) => { + const { dispatch } = this.props + dataApi + .reorderFolder(storage.key, oldIndex, newIndex) + .then((data) => { + dispatch({ type: 'REORDER_FOLDER', storage: data.storage }) + }) + } + } + SideNavComponent (isFolded, storageList) { const { location, data } = this.props @@ -180,13 +192,16 @@ class SideNav extends React.Component { const isFolded = config.isSideNavFolded const storageList = data.storageMap.map((storage, key) => { - return }) const style = {} From e89c0a3e61e1eb5ac3761a1ede5fd6c6bd5d389e Mon Sep 17 00:00:00 2001 From: Yu-Hung Ou Date: Fri, 2 Mar 2018 00:06:58 +1100 Subject: [PATCH 04/19] added support for toggling smart quotes in preview --- browser/components/MarkdownEditor.js | 1 + browser/components/MarkdownPreview.js | 24 +- browser/components/MarkdownSplitEditor.js | 1 + browser/lib/markdown.js | 322 +++++++++--------- browser/main/lib/ConfigManager.js | 3 +- browser/main/modals/PreferencesModal/UiTab.js | 13 +- 6 files changed, 197 insertions(+), 167 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index f02a146a..d0e2f505 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -279,6 +279,7 @@ class MarkdownEditor extends React.Component { lineNumber={config.preview.lineNumber} indentSize={editorIndentSize} scrollPastEnd={config.preview.scrollPastEnd} + smartQuotes={config.preview.smartQuotes} ref='preview' onContextMenu={(e) => this.handleContextMenu(e)} onDoubleClick={(e) => this.handleDoubleClick(e)} diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index c5b0355d..ddda74bb 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types' import React from 'react' -import markdown from 'browser/lib/markdown' +import Markdown from 'browser/lib/markdown' import _ from 'lodash' import CodeMirror from 'codemirror' import 'codemirror-mode-elixir' @@ -130,6 +130,13 @@ export default class MarkdownPreview extends React.Component { this.printHandler = () => this.handlePrint() this.linkClickHandler = this.handlelinkClick.bind(this) + this.initMarkdown = this.initMarkdown.bind(this) + this.initMarkdown() + } + + initMarkdown () { + const { smartQuotes } = this.props + this.markdown = new Markdown({ typographer: smartQuotes }) } handlePreviewAnchorClick (e) { @@ -198,7 +205,7 @@ export default class MarkdownPreview extends React.Component { const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams() const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber) - const body = markdown.render(noteContent) + const body = this.markdown.render(noteContent) const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] files.forEach((file) => { @@ -309,6 +316,10 @@ export default class MarkdownPreview extends React.Component { componentDidUpdate (prevProps) { if (prevProps.value !== this.props.value) this.rewriteIframe() + if (prevProps.smartQuotes !== this.props.smartQuotes) { + this.initMarkdown() + this.rewriteIframe() + } if (prevProps.fontFamily !== this.props.fontFamily || prevProps.fontSize !== this.props.fontSize || prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily || @@ -374,7 +385,7 @@ export default class MarkdownPreview extends React.Component { value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock)) }) } - this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) + this.refs.root.contentWindow.document.body.innerHTML = this.markdown.render(value) _.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => { this.fixDecodedURI(el) @@ -390,9 +401,9 @@ export default class MarkdownPreview extends React.Component { }) _.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => { - el.src = markdown.normalizeLinkText(el.src) + el.src = this.markdown.normalizeLinkText(el.src) if (!/\/:storage/.test(el.src)) return - el.src = `file:///${markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}` + el.src = `file:///${this.markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}` }) codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme) @@ -533,5 +544,6 @@ MarkdownPreview.propTypes = { className: PropTypes.string, value: PropTypes.string, showCopyNotification: PropTypes.bool, - storagePath: PropTypes.string + storagePath: PropTypes.string, + smartQuotes: PropTypes.bool } diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 505fbaf4..0aa2d16c 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -127,6 +127,7 @@ class MarkdownSplitEditor extends React.Component { codeBlockFontFamily={config.editor.fontFamily} lineNumber={config.preview.lineNumber} scrollPastEnd={config.preview.scrollPastEnd} + smartQuotes={config.preview.smartQuotes} ref='preview' tabInde='0' value={value} diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index d0801a1b..e75e13ee 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -19,171 +19,175 @@ function createGutter (str, firstLineNumber) { return '' + lines.join('') + '' } -var md = markdownit({ - typographer: true, - linkify: true, - html: true, - xhtmlOut: true, - breaks: true, - highlight: function (str, lang) { - const delimiter = ':' - const langInfo = lang.split(delimiter) - const langType = langInfo[0] - const fileName = langInfo[1] || '' - const firstLineNumber = parseInt(langInfo[2], 10) +class Markdown { + constructor (options = {}) { + const defaultOptions = { + typographer: true, + linkify: true, + html: true, + xhtmlOut: true, + breaks: true, + highlight: function (str, lang) { + const delimiter = ':' + const langInfo = lang.split(delimiter) + const langType = langInfo[0] + const fileName = langInfo[1] || '' + const firstLineNumber = parseInt(langInfo[2], 10) - if (langType === 'flowchart') { - return `
${str}
` - } - if (langType === 'sequence') { - return `
${str}
` - } - return '
' +
-      '' + fileName + '' +
-      createGutter(str, firstLineNumber) +
-      '' +
-      str +
-      '
' - } -}) -md.use(emoji, { - shortcuts: {} -}) -md.use(math, { - inlineOpen: config.preview.latexInlineOpen, - inlineClose: config.preview.latexInlineClose, - blockOpen: config.preview.latexBlockOpen, - blockClose: config.preview.latexBlockClose, - inlineRenderer: function (str) { - let output = '' - try { - output = katex.renderToString(str.trim()) - } catch (err) { - output = `${err.message}` - } - return output - }, - blockRenderer: function (str) { - let output = '' - try { - output = katex.renderToString(str.trim(), { displayMode: true }) - } catch (err) { - output = `
${err.message}
` - } - return output - } -}) -md.use(require('markdown-it-imsize')) -md.use(require('markdown-it-footnote')) -md.use(require('markdown-it-multimd-table')) -md.use(require('markdown-it-named-headers'), { - slugify: (header) => { - return encodeURI(header.trim() - .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') - .replace(/\s+/g, '-')) - .replace(/\-+$/, '') - } -}) -md.use(require('markdown-it-kbd')) - -const deflate = require('markdown-it-plantuml/lib/deflate') -md.use(require('markdown-it-plantuml'), '', { - generateSource: function (umlCode) { - const s = unescape(encodeURIComponent(umlCode)) - const zippedCode = deflate.encode64( - deflate.zip_deflate(`@startuml\n${s}\n@enduml`, 9) - ) - return `http://www.plantuml.com/plantuml/svg/${zippedCode}` - } -}) - -// Override task item -md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) { - let content, terminate, i, l, token - let nextLine = startLine + 1 - const terminatorRules = state.md.block.ruler.getRules('paragraph') - const endLine = state.lineMax - - // jump line-by-line until empty one or EOF - for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { - // this would be a code block normally, but after paragraph - // it's considered a lazy continuation regardless of what's there - if (state.sCount[nextLine] - state.blkIndent > 3) { continue } - - // quirk for blockquotes, this line should already be checked by that rule - if (state.sCount[nextLine] < 0) { continue } - - // Some tags can terminate paragraph without empty line. - terminate = false - for (i = 0, l = terminatorRules.length; i < l; i++) { - if (terminatorRules[i](state, nextLine, endLine, true)) { - terminate = true - break - } - } - if (terminate) { break } - } - - content = state.getLines(startLine, nextLine, state.blkIndent, false).trim() - - state.line = nextLine - - token = state.push('paragraph_open', 'p', 1) - token.map = [startLine, state.line] - - if (state.parentType === 'list') { - const match = content.match(/^\[( |x)\] ?(.+)/i) - if (match) { - const liToken = lastFindInArray(state.tokens, token => token.type === 'list_item_open') - if (liToken) { - if (!liToken.attrs) { - liToken.attrs = [] + if (langType === 'flowchart') { + return `
${str}
` } - liToken.attrs.push(['class', 'taskListItem']) + if (langType === 'sequence') { + return `
${str}
` + } + return '
' +
+          '' + fileName + '' +
+          createGutter(str, firstLineNumber) +
+          '' +
+          str +
+          '
' } - content = `` } + + const updatedOptions = Object.assign(defaultOptions, options) + this.md = markdownit(updatedOptions) + this.md.use(emoji, { + shortcuts: {} + }) + this.md.use(math, { + inlineOpen: config.preview.latexInlineOpen, + inlineClose: config.preview.latexInlineClose, + blockOpen: config.preview.latexBlockOpen, + blockClose: config.preview.latexBlockClose, + inlineRenderer: function (str) { + let output = '' + try { + output = katex.renderToString(str.trim()) + } catch (err) { + output = `${err.message}` + } + return output + }, + blockRenderer: function (str) { + let output = '' + try { + output = katex.renderToString(str.trim(), { displayMode: true }) + } catch (err) { + output = `
${err.message}
` + } + return output + } + }) + this.md.use(require('markdown-it-imsize')) + this.md.use(require('markdown-it-footnote')) + this.md.use(require('markdown-it-multimd-table')) + this.md.use(require('markdown-it-named-headers'), { + slugify: (header) => { + return encodeURI(header.trim() + .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') + .replace(/\s+/g, '-')) + .replace(/\-+$/, '') + } + }) + this.md.use(require('markdown-it-kbd')) + + const deflate = require('markdown-it-plantuml/lib/deflate') + this.md.use(require('markdown-it-plantuml'), '', { + generateSource: function (umlCode) { + const s = unescape(encodeURIComponent(umlCode)) + const zippedCode = deflate.encode64( + deflate.zip_deflate(`@startuml\n${s}\n@enduml`, 9) + ) + return `http://www.plantuml.com/plantuml/svg/${zippedCode}` + } + }) + + // Override task item + this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) { + let content, terminate, i, l, token + let nextLine = startLine + 1 + const terminatorRules = state.md.block.ruler.getRules('paragraph') + const endLine = state.lineMax + + // jump line-by-line until empty one or EOF + for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) { + // this would be a code block normally, but after paragraph + // it's considered a lazy continuation regardless of what's there + if (state.sCount[nextLine] - state.blkIndent > 3) { continue } + + // quirk for blockquotes, this line should already be checked by that rule + if (state.sCount[nextLine] < 0) { continue } + + // Some tags can terminate paragraph without empty line. + terminate = false + for (i = 0, l = terminatorRules.length; i < l; i++) { + if (terminatorRules[i](state, nextLine, endLine, true)) { + terminate = true + break + } + } + if (terminate) { break } + } + + content = state.getLines(startLine, nextLine, state.blkIndent, false).trim() + + state.line = nextLine + + token = state.push('paragraph_open', 'p', 1) + token.map = [startLine, state.line] + + if (state.parentType === 'list') { + const match = content.match(/^\[( |x)\] ?(.+)/i) + if (match) { + const liToken = lastFindInArray(state.tokens, token => token.type === 'list_item_open') + if (liToken) { + if (!liToken.attrs) { + liToken.attrs = [] + } + liToken.attrs.push(['class', 'taskListItem']) + } + content = `` + } + } + + token = state.push('inline', '', 0) + token.content = content + token.map = [startLine, state.line] + token.children = [] + + token = state.push('paragraph_close', 'p', -1) + + return true + }) + + // Add line number attribute for scrolling + const originalRender = this.md.renderer.render + this.md.renderer.render = (tokens, options, env) => { + tokens.forEach((token) => { + switch (token.type) { + case 'heading_open': + case 'paragraph_open': + case 'blockquote_open': + case 'table_open': + token.attrPush(['data-line', token.map[0]]) + } + }) + const result = originalRender.call(this.md.renderer, tokens, options, env) + return result + } + // FIXME We should not depend on global variable. + window.md = this.md } - token = state.push('inline', '', 0) - token.content = content - token.map = [startLine, state.line] - token.children = [] - - token = state.push('paragraph_close', 'p', -1) - - return true -}) - -// Add line number attribute for scrolling -const originalRender = md.renderer.render -md.renderer.render = function render (tokens, options, env) { - tokens.forEach((token) => { - switch (token.type) { - case 'heading_open': - case 'paragraph_open': - case 'blockquote_open': - case 'table_open': - token.attrPush(['data-line', token.map[0]]) - } - }) - const result = originalRender.call(md.renderer, tokens, options, env) - return result -} -// FIXME We should not depend on global variable. -window.md = md - -function normalizeLinkText (linkText) { - return md.normalizeLinkText(linkText) -} - -const markdown = { - render: function markdown (content) { + render (content) { if (!_.isString(content)) content = '' - const renderedContent = md.render(content) - return renderedContent - }, - normalizeLinkText + return this.md.render(content) + } + + normalizeLinkText (linkText) { + return this.md.normalizeLinkText(linkText) + } } -export default markdown +export default Markdown + diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 0c8d6ee9..7080105c 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -48,7 +48,8 @@ export const DEFAULT_CONFIG = { latexInlineClose: '$', latexBlockOpen: '$$', latexBlockClose: '$$', - scrollPastEnd: false + scrollPastEnd: false, + smartQuotes: true } } diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 50e13f6c..ddffe6d9 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -88,7 +88,8 @@ class UiTab extends React.Component { latexInlineClose: this.refs.previewLatexInlineClose.value, latexBlockOpen: this.refs.previewLatexBlockOpen.value, latexBlockClose: this.refs.previewLatexBlockClose.value, - scrollPastEnd: this.refs.previewScrollPastEnd.checked + scrollPastEnd: this.refs.previewScrollPastEnd.checked, + smartQuotes: this.refs.previewSmartQuotes.checked } } @@ -402,6 +403,16 @@ class UiTab extends React.Component { Show line numbers for preview code blocks
+
+ +
LaTeX Inline Open Delimiter From 0280a5f09e47b8f32bc3f8bac6a0bb847deca310 Mon Sep 17 00:00:00 2001 From: mirsch Date: Sun, 4 Mar 2018 22:21:14 +0100 Subject: [PATCH 05/19] use uuid in keygen, remove storage.key from note hash --- browser/components/MarkdownPreview.js | 15 +++++++++++++-- browser/components/NoteItem.js | 6 +++--- browser/components/NoteItemSimple.js | 6 +++--- browser/lib/keygen.js | 8 +++----- browser/main/Detail/MarkdownNoteDetail.js | 4 ++-- browser/main/Detail/SnippetNoteDetail.js | 4 ++-- browser/main/Detail/index.js | 7 ++----- browser/main/NoteList/index.js | 13 ++++++------- browser/main/modals/NewNoteModal.js | 4 ++-- browser/main/store.js | 12 ++++++------ package.json | 3 ++- tests/fixtures/TestDummy.js | 4 ++-- yarn.lock | 4 ++++ 13 files changed, 50 insertions(+), 40 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index c5b0355d..470da1df 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -504,9 +504,20 @@ export default class MarkdownPreview extends React.Component { handlelinkClick (e) { const noteHash = e.target.href.split('/').pop() - const regexIsNoteLink = /^(.{20})-(.{20})$/ + // this will match the new uuid v4 hash and the old hash + // e.g. + // :note:1c211eb7dcb463de6490 and + // :note:7dd23275-f2b4-49cb-9e93-3454daf1af9c + const regexIsNoteLink = /^:note:([a-zA-Z0-9-]{20,36})$/ if (regexIsNoteLink.test(noteHash)) { - eventEmitter.emit('list:jump', noteHash) + eventEmitter.emit('list:jump', noteHash.replace(':note:', '')) + } + // this will match the old link format storage.key-note.key + // e.g. + // 877f99c3268608328037-1c211eb7dcb463de6490 + const regexIsLegacyNoteLink = /^(.{20})-(.{20})$/ + if (regexIsLegacyNoteLink.test(noteHash)) { + eventEmitter.emit('list:jump', noteHash.split('-')[1]) } } diff --git a/browser/components/NoteItem.js b/browser/components/NoteItem.js index 2c93dc18..72d1263a 100644 --- a/browser/components/NoteItem.js +++ b/browser/components/NoteItem.js @@ -62,9 +62,9 @@ const NoteItem = ({ ? 'item--active' : 'item' } - key={`${note.storage}-${note.key}`} - onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)} - onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)} + key={note.key} + onClick={e => handleNoteClick(e, note.key)} + onContextMenu={e => handleNoteContextMenu(e, note.key)} onDragStart={e => handleDragStart(e, note)} draggable='true' > diff --git a/browser/components/NoteItemSimple.js b/browser/components/NoteItemSimple.js index 0d2465e9..8262ea1d 100644 --- a/browser/components/NoteItemSimple.js +++ b/browser/components/NoteItemSimple.js @@ -28,9 +28,9 @@ const NoteItemSimple = ({ ? 'item-simple--active' : 'item-simple' } - key={`${note.storage}-${note.key}`} - onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)} - onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)} + key={note.key} + onClick={e => handleNoteClick(e, note.key)} + onContextMenu={e => handleNoteContextMenu(e, note.key)} onDragStart={e => handleDragStart(e, note)} draggable='true' > diff --git a/browser/lib/keygen.js b/browser/lib/keygen.js index f4937a83..8bf58a19 100644 --- a/browser/lib/keygen.js +++ b/browser/lib/keygen.js @@ -1,7 +1,5 @@ -const crypto = require('crypto') -const _ = require('lodash') +const uuidv4 = require('uuid/v4') -module.exports = function (length) { - if (!_.isFinite(length)) length = 10 - return crypto.randomBytes(length).toString('hex') +module.exports = function () { + return uuidv4() } diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index a543a5aa..6821bf2f 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -139,7 +139,7 @@ class MarkdownNoteDetail extends React.Component { hashHistory.replace({ pathname: location.pathname, query: { - key: newNote.storage + '-' + newNote.key + key: newNote.key } }) this.setState({ @@ -393,7 +393,7 @@ class MarkdownNoteDetail extends React.Component { `${note.storage}-${note.key}` === noteKey) + return notes.find((note) => note.key === noteKey) } function findNotesByKeys (notes, noteKeys) { @@ -39,7 +39,7 @@ function findNotesByKeys (notes, noteKeys) { } function getNoteKey (note) { - return `${note.storage}-${note.key}` + return note.key } class NoteList extends React.Component { @@ -114,10 +114,10 @@ class NoteList extends React.Component { componentDidUpdate (prevProps) { const { location } = this.props const { selectedNoteKeys } = this.state - const visibleNoteKeys = this.notes.map(note => `${note.storage}-${note.key}`) + const visibleNoteKeys = this.notes.map(note => note.key) const note = this.notes[0] const prevKey = prevProps.location.query.key - const noteKey = visibleNoteKeys.includes(prevKey) ? prevKey : note && `${note.storage}-${note.key}` + const noteKey = visibleNoteKeys.includes(prevKey) ? prevKey : note && note.key if (note && location.query.key == null) { const { router } = this.context @@ -613,19 +613,18 @@ class NoteList extends React.Component { content: firstNote.content }) .then((note) => { - const uniqueKey = note.storage + '-' + note.key dispatch({ type: 'UPDATE_NOTE', note: note }) this.setState({ - selectedNoteKeys: [uniqueKey] + selectedNoteKeys: [note.key] }) hashHistory.push({ pathname: location.pathname, - query: {key: uniqueKey} + query: {key: note.key} }) }) } diff --git a/browser/main/modals/NewNoteModal.js b/browser/main/modals/NewNoteModal.js index 8322d99f..84f510c4 100644 --- a/browser/main/modals/NewNoteModal.js +++ b/browser/main/modals/NewNoteModal.js @@ -35,7 +35,7 @@ class NewNoteModal extends React.Component { content: '' }) .then((note) => { - const noteHash = `${note.storage}-${note.key}` + const noteHash = note.key dispatch({ type: 'UPDATE_NOTE', note: note @@ -75,7 +75,7 @@ class NewNoteModal extends React.Component { }] }) .then((note) => { - const noteHash = `${note.storage}-${note.key}` + const noteHash = note.key dispatch({ type: 'UPDATE_NOTE', note: note diff --git a/browser/main/store.js b/browser/main/store.js index abd34889..1ee9fc88 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -27,7 +27,7 @@ function data (state = defaultDataMap(), action) { action.notes.some((note) => { if (note === undefined) return true - const uniqueKey = note.storage + '-' + note.key + const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder state.noteMap.set(uniqueKey, note) @@ -66,7 +66,7 @@ function data (state = defaultDataMap(), action) { case 'UPDATE_NOTE': { const note = action.note - const uniqueKey = note.storage + '-' + note.key + const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder const oldNote = state.noteMap.get(uniqueKey) @@ -162,9 +162,9 @@ function data (state = defaultDataMap(), action) { case 'MOVE_NOTE': { const originNote = action.originNote - const originKey = originNote.storage + '-' + originNote.key + const originKey = originNote.key const note = action.note - const uniqueKey = note.storage + '-' + note.key + const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder const oldNote = state.noteMap.get(uniqueKey) @@ -423,7 +423,7 @@ function data (state = defaultDataMap(), action) { state.folderNoteMap = new Map(state.folderNoteMap) state.tagNoteMap = new Map(state.tagNoteMap) action.notes.forEach((note) => { - const uniqueKey = note.storage + '-' + note.key + const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder state.noteMap.set(uniqueKey, note) @@ -483,7 +483,7 @@ function data (state = defaultDataMap(), action) { state.tagNoteMap = new Map(state.tagNoteMap) state.starredSet = new Set(state.starredSet) notes.forEach((note) => { - const noteKey = storage.key + '-' + note.key + const noteKey = note.key state.noteMap.delete(noteKey) state.starredSet.delete(noteKey) note.tags.forEach((tag) => { diff --git a/package.json b/package.json index db2a9733..4c258224 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,8 @@ "sander": "^0.5.1", "striptags": "^2.2.1", "superagent": "^1.2.0", - "superagent-promise": "^1.0.3" + "superagent-promise": "^1.0.3", + "uuid": "^3.2.1" }, "devDependencies": { "ava": "^0.16.0", diff --git a/tests/fixtures/TestDummy.js b/tests/fixtures/TestDummy.js index 4edcc9cf..1ded274b 100644 --- a/tests/fixtures/TestDummy.js +++ b/tests/fixtures/TestDummy.js @@ -149,9 +149,9 @@ function dummyLegacyStorage (storagePath, override = {}) { var folderNotes = [] var noteCount = Math.floor((Math.random() * 5)) + 1 for (var i = 0; i < noteCount; i++) { - var key = keygen(6) + var key = keygen() while (folderNotes.some((note) => note.key === key)) { - key = keygen(6) + key = keygen() } var noteData = dummyNote({ diff --git a/yarn.lock b/yarn.lock index c1a949a4..ffd1da24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6672,6 +6672,10 @@ uuid@^2.0.1, uuid@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" +uuid@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" From fa2d34dcfc7c88c2472a3902ec14896f4b8c25ad Mon Sep 17 00:00:00 2001 From: mirsch Date: Sun, 4 Mar 2018 23:28:18 +0100 Subject: [PATCH 06/19] fix delete and empty trash --- browser/main/NoteList/index.js | 6 ++---- browser/main/SideNav/index.js | 10 ++++------ browser/main/store.js | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 4a67abeb..76d089cc 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -548,11 +548,9 @@ class NoteList extends React.Component { }) if (dialogueButtonIndex === 1) return Promise.all( - selectedNoteKeys.map((uniqueKey) => { - const storageKey = uniqueKey.split('-')[0] - const noteKey = uniqueKey.split('-')[1] + selectedNotes.map((note) => { return dataApi - .deleteNote(storageKey, noteKey) + .deleteNote(note.storage, note.key) }) ) .then((data) => { diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 6d05e37b..6205300e 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -148,10 +148,8 @@ class SideNav extends React.Component { emptyTrash (entries) { const { dispatch } = this.props - const deletionPromises = entries.map((storageAndNoteKey) => { - const storageKey = storageAndNoteKey.split('-')[0] - const noteKey = storageAndNoteKey.split('-')[1] - return dataApi.deleteNote(storageKey, noteKey) + const deletionPromises = entries.map((note) => { + return dataApi.deleteNote(note.storage, note.key) }) Promise.all(deletionPromises) .then((arrayOfStorageAndNoteKeys) => { @@ -167,9 +165,9 @@ class SideNav extends React.Component { handleFilterButtonContextMenu (event) { const { data } = this.props - const entries = data.trashedSet.toJS() + const trashedNotes = data.trashedSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey)) const menu = Menu.buildFromTemplate([ - { label: 'Empty Trash', click: () => this.emptyTrash(entries) } + { label: 'Empty Trash', click: () => this.emptyTrash(trashedNotes) } ]) menu.popup() } diff --git a/browser/main/store.js b/browser/main/store.js index 1ee9fc88..f078ad20 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -297,7 +297,7 @@ function data (state = defaultDataMap(), action) { } case 'DELETE_NOTE': { - const uniqueKey = action.storageKey + '-' + action.noteKey + const uniqueKey = action.noteKey const targetNote = state.noteMap.get(uniqueKey) state = Object.assign({}, state) From b74ba22c4460c46aba6c4e9633d5de0754b063fa Mon Sep 17 00:00:00 2001 From: mirsch Date: Mon, 5 Mar 2018 00:02:30 +0100 Subject: [PATCH 07/19] adjust keygen to use uuid only for notes (uuid on storage/folders woud need more refactoring) --- browser/lib/keygen.js | 10 ++++++++-- browser/main/lib/dataApi/createNote.js | 4 ++-- browser/main/lib/dataApi/moveNote.js | 4 ++-- tests/fixtures/TestDummy.js | 8 ++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/browser/lib/keygen.js b/browser/lib/keygen.js index 8bf58a19..814efedd 100644 --- a/browser/lib/keygen.js +++ b/browser/lib/keygen.js @@ -1,5 +1,11 @@ +const crypto = require('crypto') +const _ = require('lodash') const uuidv4 = require('uuid/v4') -module.exports = function () { - return uuidv4() +module.exports = function (uuid) { + if (typeof uuid === typeof true && uuid) { + return uuidv4() + } + const length = 10 + return crypto.randomBytes(length).toString('hex') } diff --git a/browser/main/lib/dataApi/createNote.js b/browser/main/lib/dataApi/createNote.js index 4b667385..e5d44489 100644 --- a/browser/main/lib/dataApi/createNote.js +++ b/browser/main/lib/dataApi/createNote.js @@ -52,12 +52,12 @@ function createNote (storageKey, input) { return storage }) .then(function saveNote (storage) { - let key = keygen() + let key = keygen(true) let isUnique = false while (!isUnique) { try { sander.statSync(path.join(storage.path, 'notes', key + '.cson')) - key = keygen() + key = keygen(true) } catch (err) { if (err.code === 'ENOENT') { isUnique = true diff --git a/browser/main/lib/dataApi/moveNote.js b/browser/main/lib/dataApi/moveNote.js index 4580062e..ef6760d7 100644 --- a/browser/main/lib/dataApi/moveNote.js +++ b/browser/main/lib/dataApi/moveNote.js @@ -37,12 +37,12 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) { return resolveStorageData(newStorage) .then(function findNewNoteKey (_newStorage) { newStorage = _newStorage - newNoteKey = keygen() + newNoteKey = keygen(true) let isUnique = false while (!isUnique) { try { sander.statSync(path.join(newStorage.path, 'notes', newNoteKey + '.cson')) - newNoteKey = keygen() + newNoteKey = keygen(true) } catch (err) { if (err.code === 'ENOENT') { isUnique = true diff --git a/tests/fixtures/TestDummy.js b/tests/fixtures/TestDummy.js index 1ded274b..b1b176ea 100644 --- a/tests/fixtures/TestDummy.js +++ b/tests/fixtures/TestDummy.js @@ -107,9 +107,9 @@ function dummyStorage (storagePath, override = {}) { var notesData = [] var noteCount = Math.floor((Math.random() * 15)) + 1 for (var i = 0; i < noteCount; i++) { - var key = keygen() + var key = keygen(true) while (notesData.some((note) => note.key === key)) { - key = keygen() + key = keygen(true) } var noteData = dummyNote({ @@ -149,9 +149,9 @@ function dummyLegacyStorage (storagePath, override = {}) { var folderNotes = [] var noteCount = Math.floor((Math.random() * 5)) + 1 for (var i = 0; i < noteCount; i++) { - var key = keygen() + var key = keygen(true) while (folderNotes.some((note) => note.key === key)) { - key = keygen() + key = keygen(true) } var noteData = dummyNote({ From f370508c936e034ab9d8475fcc2c7d2e73892740 Mon Sep 17 00:00:00 2001 From: mirsch Date: Wed, 7 Mar 2018 01:08:10 +0100 Subject: [PATCH 08/19] fixes #1442, Export responsive html --- browser/components/MarkdownPreview.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 2bb42291..ae2bdea2 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -217,6 +217,7 @@ export default class MarkdownPreview extends React.Component { return ` + ${styles} From 74c32bed296600b1de4dd709a6e9f59aa05875b4 Mon Sep 17 00:00:00 2001 From: Kazz Yokomizo Date: Thu, 8 Mar 2018 11:35:41 +0900 Subject: [PATCH 09/19] Update-readme --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 67a5689d..b5d02729 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -:mega: Open sourcing our [Android and iOS apps](https://github.com/BoostIO/Boostnote-mobile)! +:mega: We've launched a blogging platform with markdown called **[Boostlog](https://boostlog.io/)**. ![Boostnote app screenshot](./resources/repository/top.png) @@ -25,8 +25,8 @@ Boostnote is an open source project. It's an independent project with its ongoin ## Community - [Facebook Group](https://www.facebook.com/groups/boostnote/) - [Twitter](https://twitter.com/boostnoteapp) -- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzAzMjI1MTIyNTQ3LTc2MjNiYWU3NTc1YjZlMTk3NzFmOWE1ZWU1MGRhMzBkMGIwMWFjOWMxMDRiM2I2NzkzYzc4OGZhNmVhZjYzZTM) -- [Blog](https://boostlog.io/@junp1234) +- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzI3NTIxMTQzMTQzLTUyYWZmZWM1YzcwYzQ5OWQ5YzA3Y2M2NzUzNmIwNzYzMjg5NmQyOGJlNzcyZDJhMGY0ZDc0ZjdlZDFhMDdiMWE) +- [Blog](https://boostlog.io/tags/boostnote) - [Reddit](https://www.reddit.com/r/Boostnote/) From d01a7b16a111290a976678afecee50aad81d41cd Mon Sep 17 00:00:00 2001 From: Yu-Hung Ou Date: Thu, 8 Mar 2018 23:40:13 +1100 Subject: [PATCH 10/19] fixed codeblock style in exported HTML file --- browser/components/MarkdownPreview.js | 4 ++-- browser/lib/markdown.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index ae2bdea2..537ebe38 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -421,9 +421,9 @@ export default class MarkdownPreview extends React.Component { el.innerHTML = '' if (codeBlockTheme.indexOf('solarized') === 0) { const [refThema, color] = codeBlockTheme.split(' ') - el.parentNode.className += ` cm-s-${refThema} cm-s-${color} CodeMirror` + el.parentNode.className += ` cm-s-${refThema} cm-s-${color}` } else { - el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror` + el.parentNode.className += ` cm-s-${codeBlockTheme}` } CodeMirror.runMode(content, syntax.mime, el, { tabSize: indentSize diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index b97f9d56..bfe621f7 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -39,7 +39,7 @@ var md = markdownit({ if (langType === 'sequence') { return `
${str}
` } - return '
' +
+    return '
' +
       '' + fileName + '' +
       createGutter(str, firstLineNumber) +
       '' +

From bd385ec062c4692bd1d3e58e68771cfaac346c0f Mon Sep 17 00:00:00 2001
From: Yu-Hung Ou 
Date: Fri, 9 Mar 2018 23:27:49 +1100
Subject: [PATCH 11/19] increased the minimum count of dummy folders to 2

---
 tests/fixtures/TestDummy.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/fixtures/TestDummy.js b/tests/fixtures/TestDummy.js
index 4edcc9cf..c62d3849 100644
--- a/tests/fixtures/TestDummy.js
+++ b/tests/fixtures/TestDummy.js
@@ -22,7 +22,7 @@ function dummyBoostnoteJSONData (override = {}, isLegacy = false) {
   if (override.folders == null) {
     data.folders = []
 
-    var folderCount = Math.floor((Math.random() * 5)) + 1
+    var folderCount = Math.floor((Math.random() * 5)) + 2
     for (var i = 0; i < folderCount; i++) {
       var key = keygen()
       while (data.folders.some((folder) => folder.key === key)) {

From 410e82e3754f3640cad514925716129a30ad433b Mon Sep 17 00:00:00 2001
From: Yu-Hung Ou 
Date: Fri, 9 Mar 2018 23:28:01 +1100
Subject: [PATCH 12/19] increased the minimum count of dummy notes to 2

---
 tests/fixtures/TestDummy.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/fixtures/TestDummy.js b/tests/fixtures/TestDummy.js
index c62d3849..a1f5d16e 100644
--- a/tests/fixtures/TestDummy.js
+++ b/tests/fixtures/TestDummy.js
@@ -105,7 +105,7 @@ function dummyStorage (storagePath, override = {}) {
 
   sander.writeFileSync(path.join(storagePath, 'boostnote.json'), JSON.stringify(jsonData))
   var notesData = []
-  var noteCount = Math.floor((Math.random() * 15)) + 1
+  var noteCount = Math.floor((Math.random() * 15)) + 2
   for (var i = 0; i < noteCount; i++) {
     var key = keygen()
     while (notesData.some((note) => note.key === key)) {

From 8fb0b5b57231a079b90828ba16d8ca841d7fb406 Mon Sep 17 00:00:00 2001
From: paalon 
Date: Sat, 10 Mar 2018 00:36:00 +0900
Subject: [PATCH 13/19] Update KaTeX Library from 0.8.3 to 0.9.0.

---
 package.json |   2 +-
 yarn.lock    | 135 ++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 128 insertions(+), 9 deletions(-)

diff --git a/package.json b/package.json
index 6b90ade9..04ca2baa 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
     "iconv-lite": "^0.4.19",
     "immutable": "^3.8.1",
     "js-sequence-diagrams": "^1000000.0.6",
-    "katex": "^0.8.3",
+    "katex": "^0.9.0",
     "lodash": "^4.11.1",
     "lodash-move": "^1.1.1",
     "markdown-it": "^6.0.1",
diff --git a/yarn.lock b/yarn.lock
index c5da5dcd..f0c81311 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -114,6 +114,12 @@ ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
 
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  dependencies:
+    color-convert "^1.9.0"
+
 ansi-styles@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
@@ -1381,6 +1387,14 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
+chalk@^2.3.0, chalk@^2.3.1:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65"
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
 charenc@~0.0.1:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -1523,6 +1537,12 @@ color-convert@^1.3.0:
   dependencies:
     color-name "^1.1.1"
 
+color-convert@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
+  dependencies:
+    color-name "^1.1.1"
+
 color-name@^1.0.0, color-name@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
@@ -2057,6 +2077,13 @@ doctrine@^2.0.0:
     esutils "^2.0.2"
     isarray "^1.0.0"
 
+dom-serializer@0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+  dependencies:
+    domelementtype "~1.1.1"
+    entities "~1.1.1"
+
 dom-storage@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/dom-storage/-/dom-storage-2.0.2.tgz#ed17cbf68abd10e0aef8182713e297c5e4b500b0"
@@ -2069,6 +2096,27 @@ domain-browser@^1.1.1:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
 
+domelementtype@1, domelementtype@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@^2.3.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
+  dependencies:
+    domelementtype "1"
+
+domutils@^1.5.1:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+  dependencies:
+    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"
@@ -2249,7 +2297,7 @@ enhanced-resolve@~0.9.0:
     memory-fs "^0.2.0"
     tapable "^0.1.8"
 
-entities@~1.1.1:
+entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
@@ -3274,6 +3322,10 @@ has-flag@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
 
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+
 has-unicode@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -3361,6 +3413,17 @@ html-encoding-sniffer@^1.0.1:
   dependencies:
     whatwg-encoding "^1.0.1"
 
+htmlparser2@^3.9.0:
+  version "3.9.2"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+  dependencies:
+    domelementtype "^1.3.0"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^2.0.2"
+
 http-errors@~1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257"
@@ -3910,11 +3973,11 @@ jsx-ast-utils@^2.0.0:
   dependencies:
     array-includes "^3.0.3"
 
-katex@^0.8.3:
-  version "0.8.3"
-  resolved "https://registry.yarnpkg.com/katex/-/katex-0.8.3.tgz#909d99864baf964c3ccae39c4a99a8e0fb9a1bd0"
+katex@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/katex/-/katex-0.9.0.tgz#26a7d082c21d53725422d2d71da9b2d8455fbd4a"
   dependencies:
-    match-at "^0.1.0"
+    match-at "^0.1.1"
 
 kind-of@^3.0.2:
   version "3.2.2"
@@ -4019,14 +4082,30 @@ lodash.difference@^4.3.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
 
+lodash.escaperegexp@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
+
 lodash.get@^4.0.0:
   version "4.4.2"
   resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
 
+lodash.isplainobject@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+
+lodash.isstring@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+
 lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
+lodash.mergewith@^4.6.0:
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
+
 lodash.some@^4.5.1:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
@@ -4162,9 +4241,9 @@ markdown-it@^6.0.1:
     mdurl "~1.0.1"
     uc.micro "^1.0.1"
 
-match-at@^0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.0.tgz#f561e7709ff9a105b85cc62c6b8ee7c15bf24f31"
+match-at@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
 
 matcher@^0.1.1:
   version "0.1.2"
@@ -5142,6 +5221,14 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0
     source-map "^0.5.6"
     supports-color "^3.2.3"
 
+postcss@^6.0.14:
+  version "6.0.19"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555"
+  dependencies:
+    chalk "^2.3.1"
+    source-map "^0.6.1"
+    supports-color "^5.2.0"
+
 power-assert-context-formatter@^1.0.4:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/power-assert-context-formatter/-/power-assert-context-formatter-1.1.1.tgz#edba352d3ed8a603114d667265acce60d689ccdf"
@@ -5847,6 +5934,21 @@ sander@^0.5.1:
     mkdirp "^0.5.1"
     rimraf "^2.5.2"
 
+sanitize-html@^1.18.2:
+  version "1.18.2"
+  resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.18.2.tgz#61877ba5a910327e42880a28803c2fbafa8e4642"
+  dependencies:
+    chalk "^2.3.0"
+    htmlparser2 "^3.9.0"
+    lodash.clonedeep "^4.5.0"
+    lodash.escaperegexp "^4.1.2"
+    lodash.isplainobject "^4.0.6"
+    lodash.isstring "^4.0.1"
+    lodash.mergewith "^4.6.0"
+    postcss "^6.0.14"
+    srcset "^1.0.0"
+    xtend "^4.0.0"
+
 sax@0.5.x:
   version "0.5.8"
   resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
@@ -6012,6 +6114,10 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
   version "0.5.6"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
 
+source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+
 source-map@~0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
@@ -6050,6 +6156,13 @@ sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
 
+srcset@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef"
+  dependencies:
+    array-uniq "^1.0.2"
+    number-is-nan "^1.0.0"
+
 sshpk@^1.7.0:
   version "1.13.0"
   resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c"
@@ -6287,6 +6400,12 @@ supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.2.3:
   dependencies:
     has-flag "^1.0.0"
 
+supports-color@^5.2.0, supports-color@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
+  dependencies:
+    has-flag "^3.0.0"
+
 svgo@^0.7.0:
   version "0.7.2"
   resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"

From cfffa1b10a8f97513ebf4cf838bb42b725aa8048 Mon Sep 17 00:00:00 2001
From: Yu-Hung Ou 
Date: Sun, 11 Mar 2018 16:22:04 +1100
Subject: [PATCH 14/19] fixed missing classname in lib/markdown highlight
 render function

---
 browser/lib/markdown.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index d1f32640..8b6b4d30 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -41,7 +41,7 @@ class Markdown {
         if (langType === 'sequence') {
           return `
${str}
` } - return '
' +
+        return '
' +
           '' + fileName + '' +
           createGutter(str, firstLineNumber) +
           '' +

From 46fc010cf9da5de3472d2f53be01341ddfaaf9d8 Mon Sep 17 00:00:00 2001
From: Junyoung Choi 
Date: Mon, 12 Mar 2018 11:34:17 +0900
Subject: [PATCH 15/19] v0.11.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 567159c5..72741792 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "boost",
   "productName": "Boostnote",
-  "version": "0.10.0",
+  "version": "0.11.0",
   "main": "index.js",
   "description": "Boostnote",
   "license": "GPL-3.0",

From 10b86aec495e1fe88b2c54aa3d84e256f506d64e Mon Sep 17 00:00:00 2001
From: Junyoung Choi 
Date: Mon, 12 Mar 2018 17:40:27 +0900
Subject: [PATCH 16/19] Allow checkbox

---
 browser/lib/markdown.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index 8b6b4d30..a2b9da51 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -55,11 +55,12 @@ class Markdown {
 
     // Sanitize use rinput before other plugins
     this.md.use(sanitize, {
-      allowedTags: ['img', 'iframe'],
+      allowedTags: ['img', 'iframe', 'input'],
       allowedAttributes: {
         '*': ['alt', 'style'],
         'img': ['src', 'width', 'height'],
-        'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen']
+        'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
+        'input': ['type', 'id', 'checked']
       },
       allowedIframeHostnames: ['www.youtube.com']
     })

From f435595ad8d81ea7d972b81f5727d2dcd02446c6 Mon Sep 17 00:00:00 2001
From: Jeroen Ketelaar 
Date: Mon, 12 Mar 2018 09:42:44 +0100
Subject: [PATCH 17/19] Fixed English typo & grammar

---
 browser/main/modals/PreferencesModal/InfoTab.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js
index 25c0b255..57385f28 100644
--- a/browser/main/modals/PreferencesModal/InfoTab.js
+++ b/browser/main/modals/PreferencesModal/InfoTab.js
@@ -42,7 +42,7 @@ class InfoTab extends React.Component {
       })
     } else {
       this.setState({
-        amaMessage: 'Thank\'s for trust us'
+        amaMessage: 'Thanks for trusting us'
       })
     }
 

From bb2978b3704d359b0d28004a4f4aeb558f156f66 Mon Sep 17 00:00:00 2001
From: Junyoung Choi 
Date: Mon, 12 Mar 2018 17:53:16 +0900
Subject: [PATCH 18/19] v0.11.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 72741792..3f1248e6 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "boost",
   "productName": "Boostnote",
-  "version": "0.11.0",
+  "version": "0.11.1 ",
   "main": "index.js",
   "description": "Boostnote",
   "license": "GPL-3.0",

From f53c182cfb9bf40df4a89f2c189f0cffc3ce01f0 Mon Sep 17 00:00:00 2001
From: Junyoung Choi 
Date: Mon, 12 Mar 2018 18:50:22 +0900
Subject: [PATCH 19/19] Fix version

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 3f1248e6..bf277851 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "boost",
   "productName": "Boostnote",
-  "version": "0.11.1 ",
+  "version": "0.11.1",
   "main": "index.js",
   "description": "Boostnote",
   "license": "GPL-3.0",