mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 02:06:29 +00:00
Merge branch 'master' into fix-scroll
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# EditorConfig is awesome: http://EditorConfig.org
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
# top-most EditorConfig file
|
# top-most EditorConfig file
|
||||||
root = true
|
root = true
|
||||||
|
|||||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -17,7 +17,7 @@
|
|||||||
"${workspaceFolder}/index.js"
|
"${workspaceFolder}/index.js"
|
||||||
],
|
],
|
||||||
"windows": {
|
"windows": {
|
||||||
"runtimeExecutable": "${workspaceFolder}/node_modeules/.bin/electron.cmd"
|
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
72
Backers.md
72
Backers.md
@@ -1,72 +0,0 @@
|
|||||||
<h1 align="center">Sponsors & Backers</h1>
|
|
||||||
|
|
||||||
Boostnote is an open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome backers. If you'd like to join them, please consider:
|
|
||||||
|
|
||||||
- [Become a backer or sponsor on Open Collective.](https://opencollective.com/boostnoteio)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Backers via OpenCollective
|
|
||||||
|
|
||||||
### [Gold Sponsors / $1,000 per month](https://opencollective.com/boostnoteio/order/2259)
|
|
||||||
- Get your logo on our Readme.md on GitHub and the frontpage of https://boostnote.io/.
|
|
||||||
|
|
||||||
### [Silver Sponsors / $250 per month](https://opencollective.com/boostnoteio/order/2257)
|
|
||||||
- Get your logo on our Readme.md on GitHub and the frontpage of https://boostnote.io/.
|
|
||||||
|
|
||||||
### [Bronze Sponsors / $50 per month](https://opencollective.com/boostnoteio/order/2258)
|
|
||||||
- Get your name and Url (or E-mail) on Readme.md on GitHub.
|
|
||||||
|
|
||||||
### [Backers3 / $10 per month](https://opencollective.com/boostnoteio/order/2176)
|
|
||||||
- [Ralph03](https://opencollective.com/ralph03)
|
|
||||||
|
|
||||||
- [Nikolas Dan](https://opencollective.com/nikolas-dan)
|
|
||||||
|
|
||||||
### [Backers2 / $5 per month](https://opencollective.com/boostnoteio/order/2175)
|
|
||||||
- [Yeojong Kim](https://twitter.com/yeojoy)
|
|
||||||
|
|
||||||
- [Scotia Draven](https://opencollective.com/scotia-draven)
|
|
||||||
|
|
||||||
- [A. J. Vargas](https://opencollective.com/aj-vargas)
|
|
||||||
|
|
||||||
### [Backers1](https://opencollective.com/boostnoteio/order/2563) and One-time sponsors
|
|
||||||
- Ryosuke Tamura - $30
|
|
||||||
|
|
||||||
- tatoosh11 - $10
|
|
||||||
|
|
||||||
- Alexander Borovkov - $10
|
|
||||||
|
|
||||||
- spoonhoop - $5
|
|
||||||
|
|
||||||
- Drew Williams - $2
|
|
||||||
|
|
||||||
- Andy Shaw - $2
|
|
||||||
|
|
||||||
- mysafesky -$2
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Backers via Bountysource
|
|
||||||
https://salt.bountysource.com/teams/boostnote
|
|
||||||
|
|
||||||
- Kuzz - $65
|
|
||||||
|
|
||||||
- Intense Raiden - $45
|
|
||||||
|
|
||||||
- ravy22 - $25
|
|
||||||
|
|
||||||
- trentpolack - $20
|
|
||||||
|
|
||||||
- hikariru - $10
|
|
||||||
|
|
||||||
- kolchan11 - $10
|
|
||||||
|
|
||||||
- RonWalker22 - $10
|
|
||||||
|
|
||||||
- hocchuc - $5
|
|
||||||
|
|
||||||
- Adam - $5
|
|
||||||
|
|
||||||
- Steve - $5
|
|
||||||
|
|
||||||
- evmin - $5
|
|
||||||
@@ -13,7 +13,7 @@ import crypto from 'crypto'
|
|||||||
import consts from 'browser/lib/consts'
|
import consts from 'browser/lib/consts'
|
||||||
import styles from '../components/CodeEditor.styl'
|
import styles from '../components/CodeEditor.styl'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
const { ipcRenderer, remote } = require('electron')
|
const { ipcRenderer, remote, clipboard } = require('electron')
|
||||||
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
|
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
|
||||||
const spellcheck = require('browser/lib/spellcheck')
|
const spellcheck = require('browser/lib/spellcheck')
|
||||||
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
|
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
|
||||||
@@ -25,6 +25,10 @@ CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
|||||||
const buildCMRulers = (rulers, enableRulers) =>
|
const buildCMRulers = (rulers, enableRulers) =>
|
||||||
(enableRulers ? rulers.map(ruler => ({ column: ruler })) : [])
|
(enableRulers ? rulers.map(ruler => ({ column: ruler })) : [])
|
||||||
|
|
||||||
|
function translateHotkey (hotkey) {
|
||||||
|
return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl')
|
||||||
|
}
|
||||||
|
|
||||||
export default class CodeEditor extends React.Component {
|
export default class CodeEditor extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props)
|
super(props)
|
||||||
@@ -34,6 +38,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
trailing: true
|
trailing: true
|
||||||
})
|
})
|
||||||
this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject)
|
this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject)
|
||||||
|
this.highlightHandler = (editor, changeObject) => this.handleHighlight(editor, changeObject)
|
||||||
this.focusHandler = () => {
|
this.focusHandler = () => {
|
||||||
ipcRenderer.send('editor:focused', true)
|
ipcRenderer.send('editor:focused', true)
|
||||||
}
|
}
|
||||||
@@ -56,7 +61,11 @@ export default class CodeEditor extends React.Component {
|
|||||||
noteKey
|
noteKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
this.pasteHandler = (editor, e) => this.handlePaste(editor, e)
|
this.pasteHandler = (editor, e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
this.handlePaste(editor, false)
|
||||||
|
}
|
||||||
this.loadStyleHandler = e => {
|
this.loadStyleHandler = e => {
|
||||||
this.editor.refresh()
|
this.editor.refresh()
|
||||||
}
|
}
|
||||||
@@ -65,12 +74,16 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.scrollToLineHandeler = this.scrollToLine.bind(this)
|
this.scrollToLineHandeler = this.scrollToLine.bind(this)
|
||||||
|
|
||||||
this.formatTable = () => this.handleFormatTable()
|
this.formatTable = () => this.handleFormatTable()
|
||||||
this.contextMenuHandler = function (editor, event) {
|
|
||||||
const menu = buildEditorContextMenu(editor, event)
|
if (props.switchPreview !== 'RIGHTCLICK') {
|
||||||
if (menu != null) {
|
this.contextMenuHandler = function (editor, event) {
|
||||||
setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
|
const menu = buildEditorContextMenu(editor, event)
|
||||||
|
if (menu != null) {
|
||||||
|
setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editorActivityHandler = () => this.handleEditorActivity()
|
this.editorActivityHandler = () => this.handleEditorActivity()
|
||||||
|
|
||||||
this.turndownService = new TurndownService()
|
this.turndownService = new TurndownService()
|
||||||
@@ -124,42 +137,9 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTableEditorState () {
|
updateDefaultKeyMap () {
|
||||||
const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions)
|
const { hotkey } = this.props
|
||||||
if (active) {
|
|
||||||
if (this.extraKeysMode !== 'editor') {
|
|
||||||
this.extraKeysMode = 'editor'
|
|
||||||
this.editor.setOption('extraKeys', this.editorKeyMap)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.extraKeysMode !== 'default') {
|
|
||||||
this.extraKeysMode = 'default'
|
|
||||||
this.editor.setOption('extraKeys', this.defaultKeyMap)
|
|
||||||
this.tableEditor.resetSmartCursor()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
const { rulers, enableRulers } = this.props
|
|
||||||
const expandSnippet = this.expandSnippet.bind(this)
|
const expandSnippet = this.expandSnippet.bind(this)
|
||||||
eventEmitter.on('line:jump', this.scrollToLineHandeler)
|
|
||||||
|
|
||||||
const defaultSnippet = [
|
|
||||||
{
|
|
||||||
id: crypto.randomBytes(16).toString('hex'),
|
|
||||||
name: 'Dummy text',
|
|
||||||
prefix: ['lorem', 'ipsum'],
|
|
||||||
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
if (!fs.existsSync(consts.SNIPPET_FILE)) {
|
|
||||||
fs.writeFileSync(
|
|
||||||
consts.SNIPPET_FILE,
|
|
||||||
JSON.stringify(defaultSnippet, null, 4),
|
|
||||||
'utf8'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.defaultKeyMap = CodeMirror.normalizeKeyMap({
|
this.defaultKeyMap = CodeMirror.normalizeKeyMap({
|
||||||
Tab: function (cm) {
|
Tab: function (cm) {
|
||||||
@@ -202,6 +182,9 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'Cmd-Left': function (cm) {
|
||||||
|
cm.execCommand('goLineLeft')
|
||||||
|
},
|
||||||
'Cmd-T': function (cm) {
|
'Cmd-T': function (cm) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
},
|
},
|
||||||
@@ -211,13 +194,56 @@ export default class CodeEditor extends React.Component {
|
|||||||
document.execCommand('copy')
|
document.execCommand('copy')
|
||||||
}
|
}
|
||||||
return CodeMirror.Pass
|
return CodeMirror.Pass
|
||||||
|
},
|
||||||
|
[translateHotkey(hotkey.pasteSmartly)]: cm => {
|
||||||
|
this.handlePaste(cm, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTableEditorState () {
|
||||||
|
const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions)
|
||||||
|
if (active) {
|
||||||
|
if (this.extraKeysMode !== 'editor') {
|
||||||
|
this.extraKeysMode = 'editor'
|
||||||
|
this.editor.setOption('extraKeys', this.editorKeyMap)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.extraKeysMode !== 'default') {
|
||||||
|
this.extraKeysMode = 'default'
|
||||||
|
this.editor.setOption('extraKeys', this.defaultKeyMap)
|
||||||
|
this.tableEditor.resetSmartCursor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
const { rulers, enableRulers } = this.props
|
||||||
|
eventEmitter.on('line:jump', this.scrollToLineHandeler)
|
||||||
|
|
||||||
|
const defaultSnippet = [
|
||||||
|
{
|
||||||
|
id: crypto.randomBytes(16).toString('hex'),
|
||||||
|
name: 'Dummy text',
|
||||||
|
prefix: ['lorem', 'ipsum'],
|
||||||
|
content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if (!fs.existsSync(consts.SNIPPET_FILE)) {
|
||||||
|
fs.writeFileSync(
|
||||||
|
consts.SNIPPET_FILE,
|
||||||
|
JSON.stringify(defaultSnippet, null, 4),
|
||||||
|
'utf8'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateDefaultKeyMap()
|
||||||
|
|
||||||
this.value = this.props.value
|
this.value = this.props.value
|
||||||
this.editor = CodeMirror(this.refs.root, {
|
this.editor = CodeMirror(this.refs.root, {
|
||||||
rulers: buildCMRulers(rulers, enableRulers),
|
rulers: buildCMRulers(rulers, enableRulers),
|
||||||
value: this.props.value,
|
value: this.props.value,
|
||||||
|
linesHighlighted: this.props.linesHighlighted,
|
||||||
lineNumbers: this.props.displayLineNumbers,
|
lineNumbers: this.props.displayLineNumbers,
|
||||||
lineWrapping: true,
|
lineWrapping: true,
|
||||||
theme: this.props.theme,
|
theme: this.props.theme,
|
||||||
@@ -244,8 +270,11 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.on('focus', this.focusHandler)
|
this.editor.on('focus', this.focusHandler)
|
||||||
this.editor.on('blur', this.blurHandler)
|
this.editor.on('blur', this.blurHandler)
|
||||||
this.editor.on('change', this.changeHandler)
|
this.editor.on('change', this.changeHandler)
|
||||||
|
this.editor.on('gutterClick', this.highlightHandler)
|
||||||
this.editor.on('paste', this.pasteHandler)
|
this.editor.on('paste', this.pasteHandler)
|
||||||
this.editor.on('contextmenu', this.contextMenuHandler)
|
if (this.props.switchPreview !== 'RIGHTCLICK') {
|
||||||
|
this.editor.on('contextmenu', this.contextMenuHandler)
|
||||||
|
}
|
||||||
eventEmitter.on('top:search', this.searchHandler)
|
eventEmitter.on('top:search', this.searchHandler)
|
||||||
|
|
||||||
eventEmitter.emit('code:init')
|
eventEmitter.emit('code:init')
|
||||||
@@ -320,6 +349,8 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
clientWidth: this.refs.root.clientWidth
|
clientWidth: this.refs.root.clientWidth
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.initialHighlighting()
|
||||||
}
|
}
|
||||||
|
|
||||||
expandSnippet (line, cursor, cm, snippets) {
|
expandSnippet (line, cursor, cm, snippets) {
|
||||||
@@ -483,6 +514,14 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.setOption('extraKeys', this.defaultKeyMap)
|
this.editor.setOption('extraKeys', this.defaultKeyMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevProps.hotkey !== this.props.hotkey) {
|
||||||
|
this.updateDefaultKeyMap()
|
||||||
|
|
||||||
|
if (this.extraKeysMode === 'default') {
|
||||||
|
this.editor.setOption('extraKeys', this.defaultKeyMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.clientWidth !== this.refs.root.clientWidth) {
|
if (this.state.clientWidth !== this.refs.root.clientWidth) {
|
||||||
this.setState({
|
this.setState({
|
||||||
clientWidth: this.refs.root.clientWidth
|
clientWidth: this.refs.root.clientWidth
|
||||||
@@ -516,12 +555,96 @@ export default class CodeEditor extends React.Component {
|
|||||||
|
|
||||||
handleChange (editor, changeObject) {
|
handleChange (editor, changeObject) {
|
||||||
spellcheck.handleChange(editor, changeObject)
|
spellcheck.handleChange(editor, changeObject)
|
||||||
|
|
||||||
|
this.updateHighlight(editor, changeObject)
|
||||||
|
|
||||||
this.value = editor.getValue()
|
this.value = editor.getValue()
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
this.props.onChange(editor)
|
this.props.onChange(editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
incrementLines (start, linesAdded, linesRemoved, editor) {
|
||||||
|
let highlightedLines = editor.options.linesHighlighted
|
||||||
|
|
||||||
|
const totalHighlightedLines = highlightedLines.length
|
||||||
|
|
||||||
|
let offset = linesAdded - linesRemoved
|
||||||
|
|
||||||
|
// Store new items to be added as we're changing the lines
|
||||||
|
let newLines = []
|
||||||
|
|
||||||
|
let i = totalHighlightedLines
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
const lineNumber = highlightedLines[i]
|
||||||
|
|
||||||
|
// Interval that will need to be updated
|
||||||
|
// Between start and (start + offset) remove highlight
|
||||||
|
if (lineNumber >= start) {
|
||||||
|
highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1)
|
||||||
|
|
||||||
|
// Lines that need to be relocated
|
||||||
|
if (lineNumber >= (start + linesRemoved)) {
|
||||||
|
newLines.push(lineNumber + offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding relocated lines
|
||||||
|
highlightedLines.push(...newLines)
|
||||||
|
|
||||||
|
if (this.props.onChange) {
|
||||||
|
this.props.onChange(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHighlight (editor, changeObject) {
|
||||||
|
const lines = editor.options.linesHighlighted
|
||||||
|
|
||||||
|
if (!lines.includes(changeObject)) {
|
||||||
|
lines.push(changeObject)
|
||||||
|
editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background')
|
||||||
|
} else {
|
||||||
|
lines.splice(lines.indexOf(changeObject), 1)
|
||||||
|
editor.removeLineClass(changeObject, 'text', 'CodeMirror-activeline-background')
|
||||||
|
}
|
||||||
|
if (this.props.onChange) {
|
||||||
|
this.props.onChange(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHighlight (editor, changeObject) {
|
||||||
|
const linesAdded = changeObject.text.length - 1
|
||||||
|
const linesRemoved = changeObject.removed.length - 1
|
||||||
|
|
||||||
|
// If no lines added or removed return
|
||||||
|
if (linesAdded === 0 && linesRemoved === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let start = changeObject.from.line
|
||||||
|
|
||||||
|
switch (changeObject.origin) {
|
||||||
|
case '+insert", "undo':
|
||||||
|
start += 1
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'paste':
|
||||||
|
case '+delete':
|
||||||
|
case '+input':
|
||||||
|
if (changeObject.to.ch !== 0 || changeObject.from.ch !== 0) {
|
||||||
|
start += 1
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.incrementLines(start, linesAdded, linesRemoved, editor)
|
||||||
|
}
|
||||||
|
|
||||||
moveCursorTo (row, col) {}
|
moveCursorTo (row, col) {}
|
||||||
|
|
||||||
scrollToLine (event, num) {
|
scrollToLine (event, num) {
|
||||||
@@ -546,6 +669,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.value = this.props.value
|
this.value = this.props.value
|
||||||
this.editor.setValue(this.props.value)
|
this.editor.setValue(this.props.value)
|
||||||
this.editor.clearHistory()
|
this.editor.clearHistory()
|
||||||
|
this.restartHighlighting()
|
||||||
this.editor.on('change', this.changeHandler)
|
this.editor.on('change', this.changeHandler)
|
||||||
this.editor.refresh()
|
this.editor.refresh()
|
||||||
}
|
}
|
||||||
@@ -571,15 +695,14 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.replaceSelection(imageMd)
|
this.editor.replaceSelection(imageMd)
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePaste (editor, e) {
|
handlePaste (editor, forceSmartPaste) {
|
||||||
const clipboardData = e.clipboardData
|
const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = 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*$/
|
const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/
|
||||||
return matcher.test(str)
|
return matcher.test(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInLinkTag = editor => {
|
const isInLinkTag = editor => {
|
||||||
const startCursor = editor.getCursor('start')
|
const startCursor = editor.getCursor('start')
|
||||||
const prevChar = editor.getRange(
|
const prevChar = editor.getRange(
|
||||||
@@ -594,30 +717,73 @@ export default class CodeEditor extends React.Component {
|
|||||||
return prevChar === '](' && nextChar === ')'
|
return prevChar === '](' && nextChar === ')'
|
||||||
}
|
}
|
||||||
|
|
||||||
const pastedHtml = clipboardData.getData('text/html')
|
const isInFencedCodeBlock = editor => {
|
||||||
if (pastedHtml !== '') {
|
const cursor = editor.getCursor()
|
||||||
this.handlePasteHtml(e, editor, pastedHtml)
|
|
||||||
} else if (dataTransferItem.type.match('image')) {
|
let token = editor.getTokenAt(cursor)
|
||||||
attachmentManagement.handlePastImageEvent(
|
if (token.state.fencedState) {
|
||||||
this,
|
return true
|
||||||
storageKey,
|
}
|
||||||
noteKey,
|
|
||||||
dataTransferItem
|
let line = line = cursor.line - 1
|
||||||
)
|
while (line >= 0) {
|
||||||
} else if (
|
token = editor.getTokenAt({
|
||||||
this.props.fetchUrlTitle &&
|
ch: 3,
|
||||||
isURL(pastedTxt) &&
|
line
|
||||||
!isInLinkTag(editor)
|
})
|
||||||
) {
|
|
||||||
this.handlePasteUrl(e, editor, pastedTxt)
|
if (token.start === token.end) {
|
||||||
|
--line
|
||||||
|
} else if (token.type === 'comment') {
|
||||||
|
if (line > 0) {
|
||||||
|
token = editor.getTokenAt({
|
||||||
|
ch: 3,
|
||||||
|
line: line - 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return token.type !== 'comment'
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if (attachmentManagement.isAttachmentLink(pastedTxt)) {
|
|
||||||
|
const pastedTxt = clipboard.readText()
|
||||||
|
|
||||||
|
if (isInFencedCodeBlock(editor)) {
|
||||||
|
this.handlePasteText(editor, pastedTxt)
|
||||||
|
} else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) {
|
||||||
|
this.handlePasteUrl(editor, pastedTxt)
|
||||||
|
} else if (enableSmartPaste || forceSmartPaste) {
|
||||||
|
const image = clipboard.readImage()
|
||||||
|
if (!image.isEmpty()) {
|
||||||
|
attachmentManagement.handlePastNativeImage(
|
||||||
|
this,
|
||||||
|
storageKey,
|
||||||
|
noteKey,
|
||||||
|
image
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const pastedHtml = clipboard.readHTML()
|
||||||
|
if (pastedHtml.length > 0) {
|
||||||
|
this.handlePasteHtml(editor, pastedHtml)
|
||||||
|
} else {
|
||||||
|
this.handlePasteText(editor, pastedTxt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (attachmentManagement.isAttachmentLink(pastedTxt)) {
|
||||||
attachmentManagement
|
attachmentManagement
|
||||||
.handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt)
|
.handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt)
|
||||||
.then(modifiedText => {
|
.then(modifiedText => {
|
||||||
this.editor.replaceSelection(modifiedText)
|
this.editor.replaceSelection(modifiedText)
|
||||||
})
|
})
|
||||||
e.preventDefault()
|
} else {
|
||||||
|
this.handlePasteText(editor, pastedTxt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,8 +793,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePasteUrl (e, editor, pastedTxt) {
|
handlePasteUrl (editor, pastedTxt) {
|
||||||
e.preventDefault()
|
|
||||||
const taggedUrl = `<${pastedTxt}>`
|
const taggedUrl = `<${pastedTxt}>`
|
||||||
editor.replaceSelection(taggedUrl)
|
editor.replaceSelection(taggedUrl)
|
||||||
|
|
||||||
@@ -667,12 +832,15 @@ export default class CodeEditor extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePasteHtml (e, editor, pastedHtml) {
|
handlePasteHtml (editor, pastedHtml) {
|
||||||
e.preventDefault()
|
|
||||||
const markdown = this.turndownService.turndown(pastedHtml)
|
const markdown = this.turndownService.turndown(pastedHtml)
|
||||||
editor.replaceSelection(markdown)
|
editor.replaceSelection(markdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePasteText (editor, pastedTxt) {
|
||||||
|
editor.replaceSelection(pastedTxt)
|
||||||
|
}
|
||||||
|
|
||||||
mapNormalResponse (response, pastedTxt) {
|
mapNormalResponse (response, pastedTxt) {
|
||||||
return this.decodeResponse(response).then(body => {
|
return this.decodeResponse(response).then(body => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -693,6 +861,29 @@ export default class CodeEditor extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialHighlighting () {
|
||||||
|
if (this.editor.options.linesHighlighted == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalHighlightedLines = this.editor.options.linesHighlighted.length
|
||||||
|
const totalAvailableLines = this.editor.lineCount()
|
||||||
|
|
||||||
|
for (let i = 0; i < totalHighlightedLines; i++) {
|
||||||
|
const lineNumber = this.editor.options.linesHighlighted[i]
|
||||||
|
if (lineNumber > totalAvailableLines) {
|
||||||
|
// make sure that we skip the invalid lines althrough this case should not be happened.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
this.editor.addLineClass(lineNumber, 'text', 'CodeMirror-activeline-background')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restartHighlighting () {
|
||||||
|
this.editor.options.linesHighlighted = this.props.linesHighlighted
|
||||||
|
this.initialHighlighting()
|
||||||
|
}
|
||||||
|
|
||||||
mapImageResponse (response, pastedTxt) {
|
mapImageResponse (response, pastedTxt) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview'
|
|||||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
import { findStorage } from 'browser/lib/findStorage'
|
import { findStorage } from 'browser/lib/findStorage'
|
||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
|
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
||||||
|
|
||||||
class MarkdownEditor extends React.Component {
|
class MarkdownEditor extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -221,6 +222,28 @@ class MarkdownEditor extends React.Component {
|
|||||||
this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`)
|
this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDropImage (dropEvent) {
|
||||||
|
dropEvent.preventDefault()
|
||||||
|
const { storageKey, noteKey } = this.props
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
status: 'CODE'
|
||||||
|
}, () => {
|
||||||
|
this.refs.code.focus()
|
||||||
|
|
||||||
|
this.refs.code.editor.execCommand('goDocEnd')
|
||||||
|
this.refs.code.editor.execCommand('goLineEnd')
|
||||||
|
this.refs.code.editor.execCommand('newlineAndIndent')
|
||||||
|
|
||||||
|
attachmentManagement.handleAttachmentDrop(
|
||||||
|
this.refs.code,
|
||||||
|
storageKey,
|
||||||
|
noteKey,
|
||||||
|
dropEvent
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
handleKeyUp (e) {
|
handleKeyUp (e) {
|
||||||
const keyPressed = this.state.keyPressed
|
const keyPressed = this.state.keyPressed
|
||||||
keyPressed.delete(e.keyCode)
|
keyPressed.delete(e.keyCode)
|
||||||
@@ -232,7 +255,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {className, value, config, storageKey, noteKey} = this.props
|
const {className, value, config, storageKey, noteKey, linesHighlighted} = this.props
|
||||||
|
|
||||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||||
@@ -275,9 +298,13 @@ class MarkdownEditor extends React.Component {
|
|||||||
noteKey={noteKey}
|
noteKey={noteKey}
|
||||||
fetchUrlTitle={config.editor.fetchUrlTitle}
|
fetchUrlTitle={config.editor.fetchUrlTitle}
|
||||||
enableTableEditor={config.editor.enableTableEditor}
|
enableTableEditor={config.editor.enableTableEditor}
|
||||||
|
linesHighlighted={linesHighlighted}
|
||||||
onChange={(e) => this.handleChange(e)}
|
onChange={(e) => this.handleChange(e)}
|
||||||
onBlur={(e) => this.handleBlur(e)}
|
onBlur={(e) => this.handleBlur(e)}
|
||||||
spellCheck={config.editor.spellcheck}
|
spellCheck={config.editor.spellcheck}
|
||||||
|
enableSmartPaste={config.editor.enableSmartPaste}
|
||||||
|
hotkey={config.hotkey}
|
||||||
|
switchPreview={config.editor.switchPreview}
|
||||||
/>
|
/>
|
||||||
<MarkdownPreview styleName={this.state.status === 'PREVIEW'
|
<MarkdownPreview styleName={this.state.status === 'PREVIEW'
|
||||||
? 'preview'
|
? 'preview'
|
||||||
@@ -311,6 +338,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
customCSS={config.preview.customCSS}
|
customCSS={config.preview.customCSS}
|
||||||
allowCustomCSS={config.preview.allowCustomCSS}
|
allowCustomCSS={config.preview.allowCustomCSS}
|
||||||
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
||||||
|
onDrop={(e) => this.handleDropImage(e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import yaml from 'js-yaml'
|
|||||||
import context from 'browser/lib/context'
|
import context from 'browser/lib/context'
|
||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import { render } from 'react-dom'
|
||||||
|
import Carousel from 'react-image-carousel'
|
||||||
import ConfigManager from '../main/lib/ConfigManager'
|
import ConfigManager from '../main/lib/ConfigManager'
|
||||||
|
|
||||||
const { remote, shell } = require('electron')
|
const { remote, shell } = require('electron')
|
||||||
@@ -40,7 +42,8 @@ const appPath = fileUrl(
|
|||||||
)
|
)
|
||||||
const CSS_FILES = [
|
const CSS_FILES = [
|
||||||
`${appPath}/node_modules/katex/dist/katex.min.css`,
|
`${appPath}/node_modules/katex/dist/katex.min.css`,
|
||||||
`${appPath}/node_modules/codemirror/lib/codemirror.css`
|
`${appPath}/node_modules/codemirror/lib/codemirror.css`,
|
||||||
|
`${appPath}/node_modules/react-image-carousel/lib/css/main.min.css`
|
||||||
]
|
]
|
||||||
|
|
||||||
function buildStyle (
|
function buildStyle (
|
||||||
@@ -207,7 +210,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
|
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
|
||||||
this.printHandler = () => this.handlePrint()
|
this.printHandler = () => this.handlePrint()
|
||||||
|
|
||||||
this.linkClickHandler = this.handlelinkClick.bind(this)
|
this.linkClickHandler = this.handleLinkClick.bind(this)
|
||||||
this.initMarkdown = this.initMarkdown.bind(this)
|
this.initMarkdown = this.initMarkdown.bind(this)
|
||||||
this.initMarkdown()
|
this.initMarkdown()
|
||||||
}
|
}
|
||||||
@@ -291,26 +294,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSaveAsMd () {
|
handleSaveAsMd () {
|
||||||
this.exportAsDocument('md', (noteContent, exportTasks) => {
|
this.exportAsDocument('md')
|
||||||
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 () {
|
handleSaveAsHtml () {
|
||||||
@@ -339,11 +323,6 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
)
|
)
|
||||||
let body = this.markdown.render(noteContent)
|
let body = this.markdown.render(noteContent)
|
||||||
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
||||||
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
|
|
||||||
noteContent,
|
|
||||||
this.props.storagePath
|
|
||||||
)
|
|
||||||
|
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
if (global.process.platform === 'win32') {
|
if (global.process.platform === 'win32') {
|
||||||
file = file.replace('file:///', '')
|
file = file.replace('file:///', '')
|
||||||
@@ -355,16 +334,6 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
dst: 'css'
|
dst: 'css'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
attachmentsAbsolutePaths.forEach(attachment => {
|
|
||||||
exportTasks.push({
|
|
||||||
src: attachment,
|
|
||||||
dst: attachmentManagement.DESTINATION_FOLDER
|
|
||||||
})
|
|
||||||
})
|
|
||||||
body = attachmentManagement.removeStorageAndNoteReferences(
|
|
||||||
body,
|
|
||||||
this.props.noteKey
|
|
||||||
)
|
|
||||||
|
|
||||||
let styles = ''
|
let styles = ''
|
||||||
files.forEach(file => {
|
files.forEach(file => {
|
||||||
@@ -397,8 +366,9 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
if (filename) {
|
if (filename) {
|
||||||
const content = this.props.value
|
const content = this.props.value
|
||||||
const storage = this.props.storagePath
|
const storage = this.props.storagePath
|
||||||
|
const nodeKey = this.props.noteKey
|
||||||
|
|
||||||
exportNote(storage, content, filename, contentFormatter)
|
exportNote(nodeKey, storage, content, filename, contentFormatter)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'info',
|
type: 'info',
|
||||||
@@ -443,6 +413,8 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
|
const { onDrop } = this.props
|
||||||
|
|
||||||
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
||||||
this.refs.root.contentWindow.document.body.addEventListener(
|
this.refs.root.contentWindow.document.body.addEventListener(
|
||||||
'contextmenu',
|
'contextmenu',
|
||||||
@@ -480,7 +452,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
)
|
)
|
||||||
this.refs.root.contentWindow.document.addEventListener(
|
this.refs.root.contentWindow.document.addEventListener(
|
||||||
'drop',
|
'drop',
|
||||||
this.preventImageDroppedHandler
|
onDrop || this.preventImageDroppedHandler
|
||||||
)
|
)
|
||||||
this.refs.root.contentWindow.document.addEventListener(
|
this.refs.root.contentWindow.document.addEventListener(
|
||||||
'dragover',
|
'dragover',
|
||||||
@@ -497,6 +469,8 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
|
const { onDrop } = this.props
|
||||||
|
|
||||||
this.refs.root.contentWindow.document.body.removeEventListener(
|
this.refs.root.contentWindow.document.body.removeEventListener(
|
||||||
'contextmenu',
|
'contextmenu',
|
||||||
this.contextMenuHandler
|
this.contextMenuHandler
|
||||||
@@ -515,7 +489,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
)
|
)
|
||||||
this.refs.root.contentWindow.document.removeEventListener(
|
this.refs.root.contentWindow.document.removeEventListener(
|
||||||
'drop',
|
'drop',
|
||||||
this.preventImageDroppedHandler
|
onDrop || this.preventImageDroppedHandler
|
||||||
)
|
)
|
||||||
this.refs.root.contentWindow.document.removeEventListener(
|
this.refs.root.contentWindow.document.removeEventListener(
|
||||||
'dragover',
|
'dragover',
|
||||||
@@ -800,6 +774,34 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme)
|
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_.forEach(
|
||||||
|
this.refs.root.contentWindow.document.querySelectorAll('.gallery'),
|
||||||
|
el => {
|
||||||
|
const images = el.innerHTML.split(/\n/g).filter(i => i.length > 0)
|
||||||
|
el.innerHTML = ''
|
||||||
|
|
||||||
|
const height = el.attributes.getNamedItem('data-height')
|
||||||
|
if (height && height.value !== 'undefined') {
|
||||||
|
el.style.height = height.value + 'vh'
|
||||||
|
}
|
||||||
|
|
||||||
|
let autoplay = el.attributes.getNamedItem('data-autoplay')
|
||||||
|
if (autoplay && autoplay.value !== 'undefined') {
|
||||||
|
autoplay = parseInt(autoplay.value, 10) || 0
|
||||||
|
} else {
|
||||||
|
autoplay = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Carousel
|
||||||
|
images={images}
|
||||||
|
autoplay={autoplay}
|
||||||
|
/>,
|
||||||
|
el
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus () {
|
||||||
@@ -842,7 +844,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
return new window.Notification(title, options)
|
return new window.Notification(title, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
handlelinkClick (e) {
|
handleLinkClick (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
|||||||
@@ -64,9 +64,9 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOnChange () {
|
handleOnChange (e) {
|
||||||
this.value = this.refs.code.value
|
this.value = this.refs.code.value
|
||||||
this.props.onChange()
|
this.props.onChange(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditorScroll (e) {
|
handleEditorScroll (e) {
|
||||||
@@ -249,7 +249,7 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {config, value, storageKey, noteKey} = this.props
|
const {config, value, storageKey, noteKey, linesHighlighted} = this.props
|
||||||
const storage = findStorage(storageKey)
|
const storage = findStorage(storageKey)
|
||||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||||
@@ -282,10 +282,14 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
enableTableEditor={config.editor.enableTableEditor}
|
enableTableEditor={config.editor.enableTableEditor}
|
||||||
storageKey={storageKey}
|
storageKey={storageKey}
|
||||||
noteKey={noteKey}
|
noteKey={noteKey}
|
||||||
onChange={this.handleOnChange.bind(this)}
|
linesHighlighted={linesHighlighted}
|
||||||
onScroll={this.handleEditorScroll.bind(this)}
|
onChange={(e) => this.handleOnChange(e)}
|
||||||
onCursorActivity={this.handleCursorActivity.bind(this)}
|
onScroll={(e) => this.handleEditorScroll(e)}
|
||||||
|
onCursorActivity={(e) => this.handleCursorActivity(e)}
|
||||||
spellCheck={config.editor.spellcheck}
|
spellCheck={config.editor.spellcheck}
|
||||||
|
enableSmartPaste={config.editor.enableSmartPaste}
|
||||||
|
hotkey={config.hotkey}
|
||||||
|
switchPreview={config.editor.switchPreview}
|
||||||
/>
|
/>
|
||||||
<div styleName='slider' style={{left: this.state.codeEditorWidthInPercent + '%'}} onMouseDown={e => this.handleMouseDown(e)} >
|
<div styleName='slider' style={{left: this.state.codeEditorWidthInPercent + '%'}} onMouseDown={e => this.handleMouseDown(e)} >
|
||||||
<div styleName='slider-hitbox' />
|
<div styleName='slider-hitbox' />
|
||||||
@@ -309,7 +313,7 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
tabInde='0'
|
tabInde='0'
|
||||||
value={value}
|
value={value}
|
||||||
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
|
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
|
||||||
onScroll={this.handlePreviewScroll.bind(this)}
|
onScroll={(e) => this.handlePreviewScroll(e)}
|
||||||
showCopyNotification={config.ui.showCopyNotification}
|
showCopyNotification={config.ui.showCopyNotification}
|
||||||
storagePath={storage.path}
|
storagePath={storage.path}
|
||||||
noteKey={noteKey}
|
noteKey={noteKey}
|
||||||
|
|||||||
@@ -3,19 +3,30 @@
|
|||||||
flex 1
|
flex 1
|
||||||
min-width 70px
|
min-width 70px
|
||||||
overflow hidden
|
overflow hidden
|
||||||
|
border-left 1px solid $ui-borderColor
|
||||||
|
border-top 1px solid $ui-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||||
.deleteButton
|
.deleteButton
|
||||||
color $ui-inactive-text-color
|
color: $ui-text-color
|
||||||
&:hover
|
visibility visible
|
||||||
background-color darken($ui-backgroundColor, 15%)
|
transition 0.15s
|
||||||
&:active
|
.button
|
||||||
color white
|
color: $ui-text-color
|
||||||
background-color $ui-active-color
|
transition 0.15s
|
||||||
|
|
||||||
.root--active
|
.root--active
|
||||||
@extend .root
|
@extend .root
|
||||||
min-width 100px
|
min-width 100px
|
||||||
border-bottom $ui-border
|
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||||
|
.deleteButton
|
||||||
|
visibility visible
|
||||||
|
color: $ui-text-color
|
||||||
|
transition 0.15s
|
||||||
|
.button
|
||||||
|
font-weight bold
|
||||||
|
color: $ui-text-color
|
||||||
|
transition 0.15s
|
||||||
|
|
||||||
.button
|
.button
|
||||||
width 100%
|
width 100%
|
||||||
@@ -27,8 +38,7 @@
|
|||||||
background-color transparent
|
background-color transparent
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
color $ui-inactive-text-color
|
||||||
background-color $ui-button--hover-backgroundColor
|
|
||||||
|
|
||||||
.deleteButton
|
.deleteButton
|
||||||
position absolute
|
position absolute
|
||||||
@@ -42,6 +52,7 @@
|
|||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
background-color transparent
|
background-color transparent
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
|
visibility hidden
|
||||||
|
|
||||||
.input
|
.input
|
||||||
height 29px
|
height 29px
|
||||||
@@ -50,76 +61,66 @@
|
|||||||
width 100%
|
width 100%
|
||||||
outline none
|
outline none
|
||||||
|
|
||||||
|
body[data-theme="default"], body[data-theme="white"]
|
||||||
|
.root--active
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
color $ui-dark-text-color
|
|
||||||
border-color $ui-dark-borderColor
|
border-color $ui-dark-borderColor
|
||||||
|
border-top 1px solid $ui-dark-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
transition 0.15s
|
||||||
|
.button
|
||||||
|
color $ui-dark-text-color
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
.deleteButton
|
||||||
color $ui-dark-inactive-text-color
|
color $ui-dark-text-color
|
||||||
&:hover
|
transition 0.15s
|
||||||
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
|
||||||
&:active
|
|
||||||
color $ui-dark-text-color
|
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
|
||||||
.root--active
|
.root--active
|
||||||
color $ui-dark-text-color
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
border-color $ui-dark-borderColor
|
border-left 1px solid $ui-dark-borderColor
|
||||||
&:hover
|
border-top 1px solid $ui-dark-borderColor
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
.button
|
||||||
.deleteButton
|
color $ui-dark-text-color
|
||||||
color $ui-dark-inactive-text-color
|
.deleteButton
|
||||||
&:hover
|
color $ui-dark-text-color
|
||||||
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
|
||||||
&:active
|
|
||||||
color $ui-dark-text-color
|
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
|
||||||
.button
|
.button
|
||||||
border none
|
border none
|
||||||
color $ui-dark-text-color
|
|
||||||
background-color transparent
|
background-color transparent
|
||||||
transition color background-color 0.15s
|
transition color background-color 0.15s
|
||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
|
||||||
color $ui-dark-text-color
|
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
|
||||||
|
|
||||||
.input
|
.input
|
||||||
background-color $ui-dark-button--hover-backgroundColor
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
|
||||||
color alpha($ui-dark-text-color, 30%)
|
|
||||||
|
|
||||||
body[data-theme="solarized-dark"]
|
body[data-theme="solarized-dark"]
|
||||||
.root
|
.root
|
||||||
color $ui-solarized-dark-text-color
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:hover
|
|
||||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
|
||||||
.deleteButton
|
|
||||||
color $ui-solarized-dark-text-color
|
|
||||||
&:hover
|
|
||||||
background-color darken($ui-solarized-dark-noteDetail-backgroundColor, 15%)
|
|
||||||
&:active
|
|
||||||
color $ui-solarized-dark-text-color
|
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
|
||||||
.root--active
|
|
||||||
color $ui-solarized-dark-text-color
|
|
||||||
border-color $ui-solarized-dark-borderColor
|
border-color $ui-solarized-dark-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
.deleteButton
|
||||||
color $ui-solarized-dark-text-color
|
color $ui-solarized-dark-button--active-color
|
||||||
&:hover
|
transition 0.15s
|
||||||
background-color darken($ui-solarized-dark-noteDetail-backgroundColor, 15%)
|
.button
|
||||||
&:active
|
color $ui-solarized-dark-button--active-color
|
||||||
color $ui-solarized-dark-text-color
|
transition 0.15s
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
.root--active
|
||||||
|
color $ui-solarized-dark-button--active-color
|
||||||
|
background-color $ui-solarized-dark-button-backgroundColor
|
||||||
|
border-color $ui-solarized-dark-borderColor
|
||||||
|
.deleteButton
|
||||||
|
color $ui-solarized-dark-button--active-color
|
||||||
|
.button
|
||||||
|
color $ui-solarized-dark-button--active-color
|
||||||
|
|
||||||
.button
|
.button
|
||||||
border none
|
border none
|
||||||
@@ -127,101 +128,75 @@ body[data-theme="solarized-dark"]
|
|||||||
background-color transparent
|
background-color transparent
|
||||||
transition color background-color 0.15s
|
transition color background-color 0.15s
|
||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
|
||||||
color $ui-solarized-dark-text-color
|
|
||||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
|
||||||
|
|
||||||
.input
|
.input
|
||||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
color $ui-solarized-dark-text-color
|
color $ui-solarized-dark-button--active-color
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
|
||||||
color alpha($ui-solarized-dark-text-color, 30%)
|
|
||||||
|
|
||||||
body[data-theme="monokai"]
|
body[data-theme="monokai"]
|
||||||
.root
|
.root
|
||||||
color $ui-monokai-text-color
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:hover
|
|
||||||
background-color $ui-monokai-noteDetail-backgroundColor
|
|
||||||
.deleteButton
|
|
||||||
color $ui-monokai-text-color
|
|
||||||
&:hover
|
|
||||||
background-color darken($ui-monokai-noteDetail-backgroundColor, 15%)
|
|
||||||
&:active
|
|
||||||
color $ui-monokai-text-color
|
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
|
||||||
.root--active
|
|
||||||
color $ui-monokai-text-color
|
|
||||||
border-color $ui-monokai-borderColor
|
border-color $ui-monokai-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-monokai-noteDetail-backgroundColor
|
background-color $ui-monokai-noteDetail-backgroundColor
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
.deleteButton
|
||||||
color $ui-monokai-text-color
|
color $ui-monokai-text-color
|
||||||
&:hover
|
transition 0.15s
|
||||||
background-color darken($ui-monokai-noteDetail-backgroundColor, 15%)
|
.button
|
||||||
&:active
|
color $ui-monokai-text-color
|
||||||
color $ui-monokai-text-color
|
transition 0.15s
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
.root--active
|
||||||
|
color $ui-monokai-active-color
|
||||||
|
background-color $ui-monokai-button-backgroundColor
|
||||||
|
border-color $ui-monokai-borderColor
|
||||||
|
.deleteButton
|
||||||
|
color $ui-monokai-text-color
|
||||||
|
.button
|
||||||
|
color $ui-monokai-active-color
|
||||||
|
|
||||||
.button
|
.button
|
||||||
border none
|
border none
|
||||||
color $ui-monokai-text-color
|
color $ui-inactive-text-color
|
||||||
background-color transparent
|
background-color transparent
|
||||||
transition color background-color 0.15s
|
transition color background-color 0.15s
|
||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
|
||||||
color $ui-monokai-text-color
|
|
||||||
background-color $ui-monokai-noteDetail-backgroundColor
|
|
||||||
|
|
||||||
.input
|
.input
|
||||||
background-color $ui-monokai-noteDetail-backgroundColor
|
background-color $ui-monokai-noteDetail-backgroundColor
|
||||||
color $ui-monokai-text-color
|
color $ui-monokai-text-color
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
|
||||||
color alpha($ui-monokai-text-color, 30%)
|
|
||||||
|
|
||||||
body[data-theme="dracula"]
|
body[data-theme="dracula"]
|
||||||
.root
|
.root
|
||||||
color $ui-dracula-text-color
|
|
||||||
border-color $ui-dark-borderColor
|
|
||||||
&:hover
|
|
||||||
background-color $ui-dracula-noteDetail-backgroundColor
|
|
||||||
.deleteButton
|
|
||||||
color $ui-dracula-text-color
|
|
||||||
&:hover
|
|
||||||
background-color darken($ui-dracula-noteDetail-backgroundColor, 15%)
|
|
||||||
&:active
|
|
||||||
color $ui-dracula-text-color
|
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
|
||||||
.root--active
|
|
||||||
color $ui-dracula-text-color
|
|
||||||
border-color $ui-dracula-borderColor
|
border-color $ui-dracula-borderColor
|
||||||
&:hover
|
&:hover
|
||||||
background-color $ui-dracula-noteDetail-backgroundColor
|
background-color $ui-dracula-noteDetail-backgroundColor
|
||||||
|
transition 0.15s
|
||||||
.deleteButton
|
.deleteButton
|
||||||
color $ui-dracula-text-color
|
color $ui-dracula-text-color
|
||||||
&:hover
|
transition 0.15s
|
||||||
background-color darken($ui-dracula-noteDetail-backgroundColor, 15%)
|
.button
|
||||||
&:active
|
color $ui-dracula-text-color
|
||||||
color $ui-dracula-text-color
|
transition 0.15s
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
|
||||||
|
.root--active
|
||||||
|
color $ui-dracula-text-color
|
||||||
|
background-color $ui-dracula-button-backgroundColor
|
||||||
|
border-color $ui-dracula-borderColor
|
||||||
|
.deleteButton
|
||||||
|
color $ui-dracula-text-color
|
||||||
|
.button
|
||||||
|
color $ui-dracula-active-color
|
||||||
|
|
||||||
.button
|
.button
|
||||||
border none
|
border none
|
||||||
color $ui-dracula-text-color
|
color $ui-inactive-text-color
|
||||||
background-color transparent
|
background-color transparent
|
||||||
transition color background-color 0.15s
|
transition color background-color 0.15s
|
||||||
border-left 4px solid transparent
|
border-left 4px solid transparent
|
||||||
&:hover
|
|
||||||
color $ui-dracula-text-color
|
|
||||||
background-color $ui-dracula-noteDetail-backgroundColor
|
|
||||||
|
|
||||||
.input
|
.input
|
||||||
background-color $ui-dracula-noteDetail-backgroundColor
|
background-color $ui-dracula-noteDetail-backgroundColor
|
||||||
color $ui-dracula-text-color
|
color $ui-dracula-text-color
|
||||||
|
|
||||||
.deleteButton
|
|
||||||
color alpha($ui-dracula-text-color, 30%)
|
|
||||||
@@ -55,11 +55,12 @@ body
|
|||||||
line-height 1.6
|
line-height 1.6
|
||||||
overflow-x hidden
|
overflow-x hidden
|
||||||
background-color $ui-noteDetail-backgroundColor
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
// do not allow display line breaks
|
||||||
|
.katex-display > .katex
|
||||||
|
white-space nowrap
|
||||||
|
// allow inline line breaks
|
||||||
.katex
|
.katex
|
||||||
font 400 1.2em 'KaTeX_Main'
|
|
||||||
line-height 1.2em
|
|
||||||
white-space initial
|
white-space initial
|
||||||
text-indent 0
|
|
||||||
.katex .mfrac>.vlist>span:nth-child(2)
|
.katex .mfrac>.vlist>span:nth-child(2)
|
||||||
top 0 !important
|
top 0 !important
|
||||||
.katex-error
|
.katex-error
|
||||||
@@ -183,6 +184,10 @@ ul
|
|||||||
display list-item
|
display list-item
|
||||||
&.taskListItem
|
&.taskListItem
|
||||||
list-style none
|
list-style none
|
||||||
|
&>input
|
||||||
|
margin-left -1.6em
|
||||||
|
&>p
|
||||||
|
margin-left -1.8em
|
||||||
p
|
p
|
||||||
margin 0
|
margin 0
|
||||||
&>li>ul, &>li>ol
|
&>li>ul, &>li>ol
|
||||||
@@ -416,6 +421,26 @@ pre.fence
|
|||||||
canvas, svg
|
canvas, svg
|
||||||
max-width 100% !important
|
max-width 100% !important
|
||||||
|
|
||||||
|
.gallery
|
||||||
|
width 100%
|
||||||
|
height 50vh
|
||||||
|
|
||||||
|
.carousel
|
||||||
|
.carousel-main img
|
||||||
|
min-width auto
|
||||||
|
max-width 100%
|
||||||
|
min-height auto
|
||||||
|
max-height 100%
|
||||||
|
|
||||||
|
.carousel-footer::-webkit-scrollbar-corner
|
||||||
|
background-color transparent
|
||||||
|
|
||||||
|
.carousel-main, .carousel-footer
|
||||||
|
background-color $ui-noteDetail-backgroundColor
|
||||||
|
.prev, .next
|
||||||
|
color $ui-text-color
|
||||||
|
background-color $ui-tag-backgroundColor
|
||||||
|
|
||||||
themeDarkBackground = darken(#21252B, 10%)
|
themeDarkBackground = darken(#21252B, 10%)
|
||||||
themeDarkText = #f9f9f9
|
themeDarkText = #f9f9f9
|
||||||
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
||||||
@@ -475,6 +500,14 @@ body[data-theme="dark"]
|
|||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
background-color themeDarkPreview
|
background-color themeDarkPreview
|
||||||
|
|
||||||
|
pre.fence
|
||||||
|
.gallery
|
||||||
|
.carousel-main, .carousel-footer
|
||||||
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
.prev, .next
|
||||||
|
color $ui-dark-text-color
|
||||||
|
background-color $ui-dark-tag-backgroundColor
|
||||||
|
|
||||||
themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor
|
themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%)
|
themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%)
|
||||||
themeSolarizedDarkTableHead = themeSolarizedDarkTableEven
|
themeSolarizedDarkTableHead = themeSolarizedDarkTableEven
|
||||||
@@ -510,6 +543,14 @@ body[data-theme="solarized-dark"]
|
|||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
|
pre.fence
|
||||||
|
.gallery
|
||||||
|
.carousel-main, .carousel-footer
|
||||||
|
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
|
.prev, .next
|
||||||
|
color $ui-solarized-dark-button--active-color
|
||||||
|
background-color $ui-solarized-dark-button-backgroundColor
|
||||||
|
|
||||||
themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor
|
themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor
|
||||||
themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%)
|
themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%)
|
||||||
themeMonokaiTableHead = themeMonokaiTableEven
|
themeMonokaiTableHead = themeMonokaiTableEven
|
||||||
@@ -538,6 +579,7 @@ body[data-theme="monokai"]
|
|||||||
border-right solid 1px themeMonokaiTableBorder
|
border-right solid 1px themeMonokaiTableBorder
|
||||||
kbd
|
kbd
|
||||||
background-color themeDarkBackground
|
background-color themeDarkBackground
|
||||||
|
|
||||||
dl
|
dl
|
||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
background-color themeMonokaiTableHead
|
background-color themeMonokaiTableHead
|
||||||
@@ -547,6 +589,14 @@ body[data-theme="monokai"]
|
|||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
background-color $ui-monokai-noteDetail-backgroundColor
|
background-color $ui-monokai-noteDetail-backgroundColor
|
||||||
|
|
||||||
|
pre.fence
|
||||||
|
.gallery
|
||||||
|
.carousel-main, .carousel-footer
|
||||||
|
background-color $ui-monokai-noteDetail-backgroundColor
|
||||||
|
.prev, .next
|
||||||
|
color $ui-monokai-button--active-color
|
||||||
|
background-color $ui-monokai-button-backgroundColor
|
||||||
|
|
||||||
themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor
|
themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor
|
||||||
themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%)
|
themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%)
|
||||||
themeDraculaTableHead = themeDraculaTableEven
|
themeDraculaTableHead = themeDraculaTableEven
|
||||||
@@ -575,6 +625,7 @@ body[data-theme="dracula"]
|
|||||||
border-right solid 1px themeDraculaTableBorder
|
border-right solid 1px themeDraculaTableBorder
|
||||||
kbd
|
kbd
|
||||||
background-color themeDarkBackground
|
background-color themeDarkBackground
|
||||||
|
|
||||||
dl
|
dl
|
||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
background-color themeDraculaTableHead
|
background-color themeDraculaTableHead
|
||||||
@@ -583,3 +634,11 @@ body[data-theme="dracula"]
|
|||||||
dd
|
dd
|
||||||
border-color themeDarkBorder
|
border-color themeDarkBorder
|
||||||
background-color $ui-dracula-noteDetail-backgroundColor
|
background-color $ui-dracula-noteDetail-backgroundColor
|
||||||
|
|
||||||
|
pre.fence
|
||||||
|
.gallery
|
||||||
|
.carousel-main, .carousel-footer
|
||||||
|
background-color $ui-dracula-noteDetail-backgroundColor
|
||||||
|
.prev, .next
|
||||||
|
color $ui-dracula-button--active-color
|
||||||
|
background-color $ui-dracula-button-backgroundColor
|
||||||
|
|||||||
@@ -2,51 +2,27 @@
|
|||||||
* @fileoverview Markdown table of contents generator
|
* @fileoverview Markdown table of contents generator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { EOL } from 'os'
|
||||||
import toc from 'markdown-toc'
|
import toc from 'markdown-toc'
|
||||||
import diacritics from 'diacritics-map'
|
|
||||||
import stripColor from 'strip-color'
|
|
||||||
import mdlink from 'markdown-link'
|
import mdlink from 'markdown-link'
|
||||||
|
import slugify from './slugify'
|
||||||
|
|
||||||
const EOL = require('os').EOL
|
const hasProp = Object.prototype.hasOwnProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @caseSensitiveSlugify Custom slugify function
|
* From @enyaxu/markdown-it-anchor
|
||||||
* Same implementation that the original used by markdown-toc (node_modules/markdown-toc/lib/utils.js),
|
|
||||||
* but keeps original case to properly handle https://github.com/BoostIO/Boostnote/issues/2067
|
|
||||||
*/
|
*/
|
||||||
function caseSensitiveSlugify (str) {
|
function uniqueSlug (slug, slugs, opts) {
|
||||||
function replaceDiacritics (str) {
|
let uniq = slug
|
||||||
return str.replace(/[À-ž]/g, function (ch) {
|
let i = opts.uniqueSlugStartIndex
|
||||||
return diacritics[ch] || ch
|
while (hasProp.call(slugs, uniq)) uniq = `${slug}-${i++}`
|
||||||
})
|
slugs[uniq] = true
|
||||||
}
|
return uniq
|
||||||
|
|
||||||
function getTitle (str) {
|
|
||||||
if (/^\[[^\]]+\]\(/.test(str)) {
|
|
||||||
var m = /^\[([^\]]+)\]/.exec(str)
|
|
||||||
if (m) return m[1]
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
str = getTitle(str)
|
|
||||||
str = stripColor(str)
|
|
||||||
// str = str.toLowerCase() //let's be case sensitive
|
|
||||||
|
|
||||||
// `.split()` is often (but not always) faster than `.replace()`
|
|
||||||
str = str.split(' ').join('-')
|
|
||||||
str = str.split(/\t/).join('--')
|
|
||||||
str = str.split(/<\/?[^>]+>/).join('')
|
|
||||||
str = str.split(/[|$&`~=\\\/@+*!?({[\]})<>=.,;:'"^]/).join('')
|
|
||||||
str = str.split(/[。?!,、;:“”【】()〔〕[]﹃﹄“ ”‘’﹁﹂—…-~《》〈〉「」]/).join('')
|
|
||||||
str = replaceDiacritics(str)
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function linkify (tok, text, slug, opts) {
|
function linkify (token) {
|
||||||
var uniqeID = opts.num === 0 ? '' : '-' + opts.num
|
token.content = mdlink(token.content, '#' + token.slug)
|
||||||
tok.content = mdlink(text, '#' + slug + uniqeID)
|
return token
|
||||||
return tok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOC_MARKER_START = '<!-- toc -->'
|
const TOC_MARKER_START = '<!-- toc -->'
|
||||||
@@ -91,8 +67,23 @@ export function generateInEditor (editor) {
|
|||||||
* @returns generatedTOC String containing generated TOC
|
* @returns generatedTOC String containing generated TOC
|
||||||
*/
|
*/
|
||||||
export function generate (markdownText) {
|
export function generate (markdownText) {
|
||||||
const generatedToc = toc(markdownText, {slugify: caseSensitiveSlugify, linkify: linkify})
|
const slugs = {}
|
||||||
return TOC_MARKER_START + EOL + EOL + generatedToc.content + EOL + EOL + TOC_MARKER_END
|
const opts = {
|
||||||
|
uniqueSlugStartIndex: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = toc(markdownText, {
|
||||||
|
slugify: title => {
|
||||||
|
return uniqueSlug(slugify(title), slugs, opts)
|
||||||
|
},
|
||||||
|
linkify: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const md = toc.bullets(result.json.map(linkify), {
|
||||||
|
highest: result.highest
|
||||||
|
})
|
||||||
|
|
||||||
|
return TOC_MARKER_START + EOL + EOL + md + EOL + EOL + TOC_MARKER_END
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapTocWithEol (toc, editor) {
|
function wrapTocWithEol (toc, editor) {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import _ from 'lodash'
|
|||||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||||
import katex from 'katex'
|
import katex from 'katex'
|
||||||
import { lastFindInArray } from './utils'
|
import { lastFindInArray } from './utils'
|
||||||
import anchor from '@enyaxu/markdown-it-anchor'
|
|
||||||
|
|
||||||
function createGutter (str, firstLineNumber) {
|
function createGutter (str, firstLineNumber) {
|
||||||
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
|
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
|
||||||
@@ -118,14 +117,8 @@ class Markdown {
|
|||||||
this.md.use(require('markdown-it-imsize'))
|
this.md.use(require('markdown-it-imsize'))
|
||||||
this.md.use(require('markdown-it-footnote'))
|
this.md.use(require('markdown-it-footnote'))
|
||||||
this.md.use(require('markdown-it-multimd-table'))
|
this.md.use(require('markdown-it-multimd-table'))
|
||||||
this.md.use(anchor, {
|
this.md.use(require('@enyaxu/markdown-it-anchor'), {
|
||||||
slugify: (title) => {
|
slugify: require('./slugify')
|
||||||
var slug = encodeURI(title.trim()
|
|
||||||
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
|
|
||||||
.replace(/\s+/g, '-'))
|
|
||||||
.replace(/\-+$/, '')
|
|
||||||
return slug
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
this.md.use(require('markdown-it-kbd'))
|
this.md.use(require('markdown-it-kbd'))
|
||||||
this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']})
|
this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']})
|
||||||
@@ -152,6 +145,21 @@ class Markdown {
|
|||||||
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
|
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
|
||||||
</pre>`
|
</pre>`
|
||||||
},
|
},
|
||||||
|
gallery: token => {
|
||||||
|
const content = token.content.split('\n').slice(0, -1).map(line => {
|
||||||
|
const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line)
|
||||||
|
if (match) {
|
||||||
|
return match[1]
|
||||||
|
} else {
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
}).join('\n')
|
||||||
|
|
||||||
|
return `<pre class="fence" data-line="${token.map[0]}">
|
||||||
|
<span class="filename">${token.fileName}</span>
|
||||||
|
<div class="gallery" data-autoplay="${token.parameters.autoplay}" data-height="${token.parameters.height}">${content}</div>
|
||||||
|
</pre>`
|
||||||
|
},
|
||||||
mermaid: token => {
|
mermaid: token => {
|
||||||
return `<pre class="fence" data-line="${token.map[0]}">
|
return `<pre class="fence" data-line="${token.map[0]}">
|
||||||
<span class="filename">${token.fileName}</span>
|
<span class="filename">${token.fileName}</span>
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export function createMarkdownNote (storage, folder, dispatch, location, params,
|
|||||||
folder: folder,
|
folder: folder,
|
||||||
title: '',
|
title: '',
|
||||||
tags,
|
tags,
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
})
|
})
|
||||||
.then(note => {
|
.then(note => {
|
||||||
const noteHash = note.key
|
const noteHash = note.key
|
||||||
@@ -56,7 +57,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params,
|
|||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
mode: config.editor.snippetDefaultLanguage || 'text',
|
mode: config.editor.snippetDefaultLanguage || 'text',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
17
browser/lib/slugify.js
Normal file
17
browser/lib/slugify.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import diacritics from 'diacritics-map'
|
||||||
|
|
||||||
|
function replaceDiacritics (str) {
|
||||||
|
return str.replace(/[À-ž]/g, function (ch) {
|
||||||
|
return diacritics[ch] || ch
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function slugify (title) {
|
||||||
|
let slug = title.trim()
|
||||||
|
|
||||||
|
slug = replaceDiacritics(slug)
|
||||||
|
|
||||||
|
slug = slug.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-')
|
||||||
|
|
||||||
|
return encodeURI(slug).replace(/\-+$/, '')
|
||||||
|
}
|
||||||
@@ -70,22 +70,22 @@ class InfoPanel extends React.Component {
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div id='export-wrap'>
|
<div id='export-wrap'>
|
||||||
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
|
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
|
||||||
<i className='fa fa-file-code-o' />
|
<i className='fa fa-file-code-o' />
|
||||||
<p>{i18n.__('.md')}</p>
|
<p>{i18n.__('.md')}</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
|
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
|
||||||
<i className='fa fa-file-text-o' />
|
<i className='fa fa-file-text-o' />
|
||||||
<p>{i18n.__('.txt')}</p>
|
<p>{i18n.__('.txt')}</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
|
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
|
||||||
<i className='fa fa-html5' />
|
<i className='fa fa-html5' />
|
||||||
<p>{i18n.__('.html')}</p>
|
<p>{i18n.__('.html')}</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button styleName='export--enable' onClick={(e) => print(e)}>
|
<button styleName='export--enable' onClick={(e) => print(e, 'print')}>
|
||||||
<i className='fa fa-print' />
|
<i className='fa fa-print' />
|
||||||
<p>{i18n.__('Print')}</p>
|
<p>{i18n.__('Print')}</p>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ const InfoPanelTrashed = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='export-wrap'>
|
<div id='export-wrap'>
|
||||||
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
|
<button styleName='export--enable' onClick={(e) => exportAsMd(e, 'export-md')}>
|
||||||
<i className='fa fa-file-code-o' />
|
<i className='fa fa-file-code-o' />
|
||||||
<p>.md</p>
|
<p>.md</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
|
<button styleName='export--enable' onClick={(e) => exportAsTxt(e, 'export-txt')}>
|
||||||
<i className='fa fa-file-text-o' />
|
<i className='fa fa-file-text-o' />
|
||||||
<p>.txt</p>
|
<p>.txt</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
|
<button styleName='export--enable' onClick={(e) => exportAsHtml(e, 'export-html')}>
|
||||||
<i className='fa fa-html5' />
|
<i className='fa fa-html5' />
|
||||||
<p>.html</p>
|
<p>.html</p>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
isMovingNote: false,
|
isMovingNote: false,
|
||||||
note: Object.assign({
|
note: Object.assign({
|
||||||
title: '',
|
title: '',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}, props.note),
|
}, props.note),
|
||||||
isLockButtonShown: false,
|
isLockButtonShown: false,
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
editorType: props.config.editor.type
|
editorType: props.config.editor.type
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchTimer = null
|
this.dispatchTimer = null
|
||||||
|
|
||||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||||
@@ -71,7 +73,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
|
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
|
||||||
if (this.saveQueue != null) this.saveNow()
|
if (this.saveQueue != null) this.saveNow()
|
||||||
this.setState({
|
this.setState({
|
||||||
note: Object.assign({}, nextProps.note)
|
note: Object.assign({linesHighlighted: []}, nextProps.note)
|
||||||
}, () => {
|
}, () => {
|
||||||
this.refs.content.reload()
|
this.refs.content.reload()
|
||||||
if (this.refs.tags) this.refs.tags.reset()
|
if (this.refs.tags) this.refs.tags.reset()
|
||||||
@@ -361,6 +363,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
value={note.content}
|
value={note.content}
|
||||||
storageKey={note.storage}
|
storageKey={note.storage}
|
||||||
noteKey={note.key}
|
noteKey={note.key}
|
||||||
|
linesHighlighted={note.linesHighlighted}
|
||||||
onChange={this.handleUpdateContent.bind(this)}
|
onChange={this.handleUpdateContent.bind(this)}
|
||||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||||
/>
|
/>
|
||||||
@@ -371,6 +374,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
value={note.content}
|
value={note.content}
|
||||||
storageKey={note.storage}
|
storageKey={note.storage}
|
||||||
noteKey={note.key}
|
noteKey={note.key}
|
||||||
|
linesHighlighted={note.linesHighlighted}
|
||||||
onChange={this.handleUpdateContent.bind(this)}
|
onChange={this.handleUpdateContent.bind(this)}
|
||||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
note: Object.assign({
|
note: Object.assign({
|
||||||
description: ''
|
description: ''
|
||||||
}, props.note, {
|
}, props.note, {
|
||||||
snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet))
|
snippets: props.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,8 +76,9 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
const nextNote = Object.assign({
|
const nextNote = Object.assign({
|
||||||
description: ''
|
description: ''
|
||||||
}, nextProps.note, {
|
}, nextProps.note, {
|
||||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
|
snippets: nextProps.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
snippetIndex: 0,
|
snippetIndex: 0,
|
||||||
note: nextNote
|
note: nextNote
|
||||||
@@ -410,6 +411,8 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
return (e) => {
|
return (e) => {
|
||||||
const snippets = this.state.note.snippets.slice()
|
const snippets = this.state.note.snippets.slice()
|
||||||
snippets[index].content = this.refs['code-' + index].value
|
snippets[index].content = this.refs['code-' + index].value
|
||||||
|
snippets[index].linesHighlighted = e.options.linesHighlighted
|
||||||
|
|
||||||
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
|
this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})}))
|
||||||
this.setState(state => ({
|
this.setState(state => ({
|
||||||
note: state.note
|
note: state.note
|
||||||
@@ -602,7 +605,8 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
note.snippets = note.snippets.concat([{
|
note.snippets = note.snippets.concat([{
|
||||||
name: '',
|
name: '',
|
||||||
mode: config.editor.snippetDefaultLanguage || 'text',
|
mode: config.editor.snippetDefaultLanguage || 'text',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}])
|
}])
|
||||||
const snippetIndex = note.snippets.length - 1
|
const snippetIndex = note.snippets.length - 1
|
||||||
|
|
||||||
@@ -645,11 +649,18 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
|
if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
showWarning () {
|
showWarning (e, msg) {
|
||||||
|
const warningMessage = (msg) => ({
|
||||||
|
'export-txt': 'Text export',
|
||||||
|
'export-md': 'Markdown export',
|
||||||
|
'export-html': 'HTML export',
|
||||||
|
'print': 'Print'
|
||||||
|
})[msg]
|
||||||
|
|
||||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: i18n.__('Sorry!'),
|
message: i18n.__('Sorry!'),
|
||||||
detail: i18n.__('md/text import is available only a markdown note.'),
|
detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'),
|
||||||
buttons: [i18n.__('OK')]
|
buttons: [i18n.__('OK')]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -685,10 +696,8 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
|
|
||||||
const viewList = note.snippets.map((snippet, index) => {
|
const viewList = note.snippets.map((snippet, index) => {
|
||||||
const isActive = this.state.snippetIndex === index
|
const isActive = this.state.snippetIndex === index
|
||||||
|
|
||||||
let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode))
|
let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode))
|
||||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||||
|
|
||||||
return <div styleName='tabView'
|
return <div styleName='tabView'
|
||||||
key={index}
|
key={index}
|
||||||
style={{zIndex: isActive ? 5 : 4}}
|
style={{zIndex: isActive ? 5 : 4}}
|
||||||
@@ -697,6 +706,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
? <MarkdownEditor styleName='tabView-content'
|
? <MarkdownEditor styleName='tabView-content'
|
||||||
value={snippet.content}
|
value={snippet.content}
|
||||||
config={config}
|
config={config}
|
||||||
|
linesHighlighted={snippet.linesHighlighted}
|
||||||
onChange={(e) => this.handleCodeChange(index)(e)}
|
onChange={(e) => this.handleCodeChange(index)(e)}
|
||||||
ref={'code-' + index}
|
ref={'code-' + index}
|
||||||
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
||||||
@@ -705,6 +715,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
: <CodeEditor styleName='tabView-content'
|
: <CodeEditor styleName='tabView-content'
|
||||||
mode={snippet.mode}
|
mode={snippet.mode}
|
||||||
value={snippet.content}
|
value={snippet.content}
|
||||||
|
linesHighlighted={snippet.linesHighlighted}
|
||||||
theme={config.editor.theme}
|
theme={config.editor.theme}
|
||||||
fontFamily={config.editor.fontFamily}
|
fontFamily={config.editor.fontFamily}
|
||||||
fontSize={editorFontSize}
|
fontSize={editorFontSize}
|
||||||
@@ -717,6 +728,8 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
enableTableEditor={config.editor.enableTableEditor}
|
enableTableEditor={config.editor.enableTableEditor}
|
||||||
onChange={(e) => this.handleCodeChange(index)(e)}
|
onChange={(e) => this.handleCodeChange(index)(e)}
|
||||||
ref={'code-' + index}
|
ref={'code-' + index}
|
||||||
|
enableSmartPaste={config.editor.enableSmartPaste}
|
||||||
|
hotkey={config.hotkey}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -800,7 +813,9 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
createdAt={formatDate(note.createdAt)}
|
createdAt={formatDate(note.createdAt)}
|
||||||
exportAsMd={this.showWarning}
|
exportAsMd={this.showWarning}
|
||||||
exportAsTxt={this.showWarning}
|
exportAsTxt={this.showWarning}
|
||||||
|
exportAsHtml={this.showWarning}
|
||||||
type={note.type}
|
type={note.type}
|
||||||
|
print={this.showWarning}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
absolute left right
|
absolute left right
|
||||||
top 55px
|
top 70px
|
||||||
height 30px
|
height 30px
|
||||||
display flex
|
display flex
|
||||||
background-color $ui-noteDetail-backgroundColor
|
background-color $ui-noteDetail-backgroundColor
|
||||||
@@ -57,6 +57,9 @@
|
|||||||
.tabList .tabButton
|
.tabList .tabButton
|
||||||
navWhiteButtonColor()
|
navWhiteButtonColor()
|
||||||
width 30px
|
width 30px
|
||||||
|
border-left 1px solid $ui-borderColor
|
||||||
|
border-top 1px solid $ui-borderColor
|
||||||
|
border-right 1px solid $ui-borderColor
|
||||||
|
|
||||||
.tabView
|
.tabView
|
||||||
absolute left right bottom
|
absolute left right bottom
|
||||||
@@ -98,17 +101,34 @@
|
|||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
|
||||||
body[data-theme="white"]
|
body[data-theme="white"], body[data-theme="default"]
|
||||||
.root
|
.root
|
||||||
box-shadow $note-detail-box-shadow
|
box-shadow $note-detail-box-shadow
|
||||||
border none
|
border none
|
||||||
|
|
||||||
|
.tabButton
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||||
|
color $ui-text-color
|
||||||
|
transition 0.15s
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.root
|
.root
|
||||||
border-left 1px solid $ui-dark-borderColor
|
border-left 1px solid $ui-dark-borderColor
|
||||||
background-color $ui-dark-noteDetail-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
box-shadow none
|
box-shadow none
|
||||||
|
|
||||||
|
.tabList .tabButton
|
||||||
|
border-color $ui-dark-borderColor
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
|
||||||
|
.tabButton
|
||||||
|
&:hover
|
||||||
|
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||||
|
color $ui-dark-text-color
|
||||||
|
transition 0.15s
|
||||||
|
|
||||||
.body
|
.body
|
||||||
background-color $ui-dark-noteDetail-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
@@ -118,7 +138,6 @@ body[data-theme="dark"]
|
|||||||
border 1px solid $ui-dark-borderColor
|
border 1px solid $ui-dark-borderColor
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
background-color $ui-button--active-backgroundColor
|
|
||||||
background-color $ui-dark-noteDetail-backgroundColor
|
background-color $ui-dark-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList .list
|
.tabList .list
|
||||||
@@ -150,6 +169,15 @@ body[data-theme="solarized-dark"]
|
|||||||
color $ui-solarized-dark-text-color
|
color $ui-solarized-dark-text-color
|
||||||
border 1px solid $ui-solarized-dark-borderColor
|
border 1px solid $ui-solarized-dark-borderColor
|
||||||
|
|
||||||
|
.tabList .tabButton
|
||||||
|
border-color $ui-solarized-dark-borderColor
|
||||||
|
|
||||||
|
.tabButton
|
||||||
|
&:hover
|
||||||
|
color $ui-solarized-dark-button--active-color
|
||||||
|
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
|
transition 0.15s
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||||
color $ui-solarized-dark-text-color
|
color $ui-solarized-dark-text-color
|
||||||
@@ -167,6 +195,14 @@ body[data-theme="monokai"]
|
|||||||
color $ui-monokai-text-color
|
color $ui-monokai-text-color
|
||||||
border 1px solid $ui-monokai-borderColor
|
border 1px solid $ui-monokai-borderColor
|
||||||
|
|
||||||
|
.tabList .tabButton
|
||||||
|
border-color $ui-monokai-borderColor
|
||||||
|
|
||||||
|
.tabButton
|
||||||
|
&:hover
|
||||||
|
color $ui-monokai-text-color
|
||||||
|
background-color $ui-monokai-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
background-color $ui-monokai-noteDetail-backgroundColor
|
background-color $ui-monokai-noteDetail-backgroundColor
|
||||||
color $ui-monokai-text-color
|
color $ui-monokai-text-color
|
||||||
@@ -184,6 +220,14 @@ body[data-theme="dracula"]
|
|||||||
color $ui-dracula-text-color
|
color $ui-dracula-text-color
|
||||||
border 1px solid $ui-dracula-borderColor
|
border 1px solid $ui-dracula-borderColor
|
||||||
|
|
||||||
|
.tabList .tabButton
|
||||||
|
border-color $ui-dracula-borderColor
|
||||||
|
|
||||||
|
.tabButton
|
||||||
|
&:hover
|
||||||
|
color $ui-dracula-text-color
|
||||||
|
background-color $ui-dracula-noteDetail-backgroundColor
|
||||||
|
|
||||||
.tabList
|
.tabList
|
||||||
background-color $ui-dracula-noteDetail-backgroundColor
|
background-color $ui-dracula-noteDetail-backgroundColor
|
||||||
color $ui-dracula-text-color
|
color $ui-dracula-text-color
|
||||||
@@ -96,12 +96,14 @@ class Main extends React.Component {
|
|||||||
{
|
{
|
||||||
name: 'example.html',
|
name: 'example.html',
|
||||||
mode: '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>",
|
||||||
|
linesHighlighted: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'example.js',
|
name: 'example.js',
|
||||||
mode: 'javascript',
|
mode: 'javascript',
|
||||||
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)"
|
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)",
|
||||||
|
linesHighlighted: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -234,8 +236,8 @@ class Main extends React.Component {
|
|||||||
if (this.state.isRightSliderFocused) {
|
if (this.state.isRightSliderFocused) {
|
||||||
const offset = this.refs.body.getBoundingClientRect().left
|
const offset = this.refs.body.getBoundingClientRect().left
|
||||||
let newListWidth = e.pageX - offset
|
let newListWidth = e.pageX - offset
|
||||||
if (newListWidth < 10) {
|
if (newListWidth < 180) {
|
||||||
newListWidth = 10
|
newListWidth = 180
|
||||||
} else if (newListWidth > 600) {
|
} else if (newListWidth > 600) {
|
||||||
newListWidth = 600
|
newListWidth = 600
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import debounceRender from 'react-debounce-render'
|
|
||||||
import styles from './NoteList.styl'
|
import styles from './NoteList.styl'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
@@ -64,13 +63,14 @@ class NoteList extends React.Component {
|
|||||||
this.focusHandler = () => {
|
this.focusHandler = () => {
|
||||||
this.refs.list.focus()
|
this.refs.list.focus()
|
||||||
}
|
}
|
||||||
this.alertIfSnippetHandler = () => {
|
this.alertIfSnippetHandler = (event, msg) => {
|
||||||
this.alertIfSnippet()
|
this.alertIfSnippet(msg)
|
||||||
}
|
}
|
||||||
this.importFromFileHandler = this.importFromFile.bind(this)
|
this.importFromFileHandler = this.importFromFile.bind(this)
|
||||||
this.jumpNoteByHash = this.jumpNoteByHashHandler.bind(this)
|
this.jumpNoteByHash = this.jumpNoteByHashHandler.bind(this)
|
||||||
this.handleNoteListKeyUp = this.handleNoteListKeyUp.bind(this)
|
this.handleNoteListKeyUp = this.handleNoteListKeyUp.bind(this)
|
||||||
this.getNoteKeyFromTargetIndex = this.getNoteKeyFromTargetIndex.bind(this)
|
this.getNoteKeyFromTargetIndex = this.getNoteKeyFromTargetIndex.bind(this)
|
||||||
|
this.cloneNote = this.cloneNote.bind(this)
|
||||||
this.deleteNote = this.deleteNote.bind(this)
|
this.deleteNote = this.deleteNote.bind(this)
|
||||||
this.focusNote = this.focusNote.bind(this)
|
this.focusNote = this.focusNote.bind(this)
|
||||||
this.pinToTop = this.pinToTop.bind(this)
|
this.pinToTop = this.pinToTop.bind(this)
|
||||||
@@ -96,6 +96,7 @@ class NoteList extends React.Component {
|
|||||||
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
|
this.refreshTimer = setInterval(() => this.forceUpdate(), 60 * 1000)
|
||||||
ee.on('list:next', this.selectNextNoteHandler)
|
ee.on('list:next', this.selectNextNoteHandler)
|
||||||
ee.on('list:prior', this.selectPriorNoteHandler)
|
ee.on('list:prior', this.selectPriorNoteHandler)
|
||||||
|
ee.on('list:clone', this.cloneNote)
|
||||||
ee.on('list:focus', this.focusHandler)
|
ee.on('list:focus', this.focusHandler)
|
||||||
ee.on('list:isMarkdownNote', this.alertIfSnippetHandler)
|
ee.on('list:isMarkdownNote', this.alertIfSnippetHandler)
|
||||||
ee.on('import:file', this.importFromFileHandler)
|
ee.on('import:file', this.importFromFileHandler)
|
||||||
@@ -118,6 +119,7 @@ class NoteList extends React.Component {
|
|||||||
|
|
||||||
ee.off('list:next', this.selectNextNoteHandler)
|
ee.off('list:next', this.selectNextNoteHandler)
|
||||||
ee.off('list:prior', this.selectPriorNoteHandler)
|
ee.off('list:prior', this.selectPriorNoteHandler)
|
||||||
|
ee.off('list:clone', this.cloneNote)
|
||||||
ee.off('list:focus', this.focusHandler)
|
ee.off('list:focus', this.focusHandler)
|
||||||
ee.off('list:isMarkdownNote', this.alertIfSnippetHandler)
|
ee.off('list:isMarkdownNote', this.alertIfSnippetHandler)
|
||||||
ee.off('import:file', this.importFromFileHandler)
|
ee.off('import:file', this.importFromFileHandler)
|
||||||
@@ -173,16 +175,15 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focusNote (selectedNoteKeys, noteKey) {
|
focusNote (selectedNoteKeys, noteKey, pathname) {
|
||||||
const { router } = this.context
|
const { router } = this.context
|
||||||
const { location } = this.props
|
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedNoteKeys
|
selectedNoteKeys
|
||||||
})
|
})
|
||||||
|
|
||||||
router.push({
|
router.push({
|
||||||
pathname: location.pathname,
|
pathname,
|
||||||
query: {
|
query: {
|
||||||
key: noteKey
|
key: noteKey
|
||||||
}
|
}
|
||||||
@@ -201,6 +202,7 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
let { selectedNoteKeys } = this.state
|
let { selectedNoteKeys } = this.state
|
||||||
const { shiftKeyDown } = this.state
|
const { shiftKeyDown } = this.state
|
||||||
|
const { location } = this.props
|
||||||
|
|
||||||
let targetIndex = this.getTargetIndex()
|
let targetIndex = this.getTargetIndex()
|
||||||
|
|
||||||
@@ -217,7 +219,7 @@ class NoteList extends React.Component {
|
|||||||
selectedNoteKeys.push(priorNoteKey)
|
selectedNoteKeys.push(priorNoteKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.focusNote(selectedNoteKeys, priorNoteKey)
|
this.focusNote(selectedNoteKeys, priorNoteKey, location.pathname)
|
||||||
|
|
||||||
ee.emit('list:moved')
|
ee.emit('list:moved')
|
||||||
}
|
}
|
||||||
@@ -228,6 +230,7 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
let { selectedNoteKeys } = this.state
|
let { selectedNoteKeys } = this.state
|
||||||
const { shiftKeyDown } = this.state
|
const { shiftKeyDown } = this.state
|
||||||
|
const { location } = this.props
|
||||||
|
|
||||||
let targetIndex = this.getTargetIndex()
|
let targetIndex = this.getTargetIndex()
|
||||||
const isTargetLastNote = targetIndex === this.notes.length - 1
|
const isTargetLastNote = targetIndex === this.notes.length - 1
|
||||||
@@ -250,7 +253,7 @@ class NoteList extends React.Component {
|
|||||||
selectedNoteKeys.push(nextNoteKey)
|
selectedNoteKeys.push(nextNoteKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.focusNote(selectedNoteKeys, nextNoteKey)
|
this.focusNote(selectedNoteKeys, nextNoteKey, location.pathname)
|
||||||
|
|
||||||
ee.emit('list:moved')
|
ee.emit('list:moved')
|
||||||
}
|
}
|
||||||
@@ -262,7 +265,7 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectedNoteKeys = [noteHash]
|
const selectedNoteKeys = [noteHash]
|
||||||
this.focusNote(selectedNoteKeys, noteHash)
|
this.focusNote(selectedNoteKeys, noteHash, '/home')
|
||||||
|
|
||||||
ee.emit('list:moved')
|
ee.emit('list:moved')
|
||||||
}
|
}
|
||||||
@@ -276,12 +279,6 @@ class NoteList extends React.Component {
|
|||||||
ee.emit('top:new-note')
|
ee.emit('top:new-note')
|
||||||
}
|
}
|
||||||
|
|
||||||
// D key
|
|
||||||
if (e.keyCode === 68) {
|
|
||||||
e.preventDefault()
|
|
||||||
this.deleteNote()
|
|
||||||
}
|
|
||||||
|
|
||||||
// E key
|
// E key
|
||||||
if (e.keyCode === 69) {
|
if (e.keyCode === 69) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -494,14 +491,21 @@ class NoteList extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
alertIfSnippet () {
|
alertIfSnippet (msg) {
|
||||||
|
const warningMessage = (msg) => ({
|
||||||
|
'export-txt': 'Text export',
|
||||||
|
'export-md': 'Markdown export',
|
||||||
|
'export-html': 'HTML export',
|
||||||
|
'print': 'Print'
|
||||||
|
})[msg]
|
||||||
|
|
||||||
const targetIndex = this.getTargetIndex()
|
const targetIndex = this.getTargetIndex()
|
||||||
if (this.notes[targetIndex].type === 'SNIPPET_NOTE') {
|
if (this.notes[targetIndex].type === 'SNIPPET_NOTE') {
|
||||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
message: i18n.__('Sorry!'),
|
message: i18n.__('Sorry!'),
|
||||||
detail: i18n.__('md/text import is available only a markdown note.'),
|
detail: i18n.__(warningMessage(msg) + ' is available only in markdown notes.'),
|
||||||
buttons: [i18n.__('OK'), i18n.__('Cancel')]
|
buttons: [i18n.__('OK')]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -706,7 +710,8 @@ class NoteList extends React.Component {
|
|||||||
type: firstNote.type,
|
type: firstNote.type,
|
||||||
folder: folder.key,
|
folder: folder.key,
|
||||||
title: firstNote.title + ' ' + i18n.__('copy'),
|
title: firstNote.title + ' ' + i18n.__('copy'),
|
||||||
content: firstNote.content
|
content: firstNote.content,
|
||||||
|
linesHighlighted: firstNote.linesHighlighted
|
||||||
})
|
})
|
||||||
.then((note) => {
|
.then((note) => {
|
||||||
attachmentManagement.cloneAttachments(firstNote, note)
|
attachmentManagement.cloneAttachments(firstNote, note)
|
||||||
@@ -1124,4 +1129,4 @@ NoteList.propTypes = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default debounceRender(CSSModules(NoteList, styles))
|
export default CSSModules(NoteList, styles)
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class StorageItem extends React.Component {
|
|||||||
const { storage } = this.props
|
const { storage } = this.props
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpen: !!storage.isOpen
|
isOpen: !!storage.isOpen,
|
||||||
|
draggedOver: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +205,20 @@ class StorageItem extends React.Component {
|
|||||||
folderKey: data.folderKey,
|
folderKey: data.folderKey,
|
||||||
fileType: data.fileType
|
fileType: data.fileType
|
||||||
})
|
})
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||||
|
type: 'info',
|
||||||
|
message: 'Exported to "' + data.exportDir + '"'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
dialog.showErrorBox(
|
||||||
|
'Export error',
|
||||||
|
err ? err.message || err : 'Unexpected error during export'
|
||||||
|
)
|
||||||
|
throw err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -231,14 +246,20 @@ class StorageItem extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDragEnter (e) {
|
handleDragEnter (e, key) {
|
||||||
e.dataTransfer.setData('defaultColor', e.target.style.backgroundColor)
|
e.preventDefault()
|
||||||
e.target.style.backgroundColor = 'rgba(129, 130, 131, 0.08)'
|
if (this.state.draggedOver === key) { return }
|
||||||
|
this.setState({
|
||||||
|
draggedOver: key
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDragLeave (e) {
|
handleDragLeave (e) {
|
||||||
e.target.style.opacity = '1'
|
e.preventDefault()
|
||||||
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
|
if (this.state.draggedOver === null) { return }
|
||||||
|
this.setState({
|
||||||
|
draggedOver: null
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
dropNote (storage, folder, dispatch, location, noteData) {
|
dropNote (storage, folder, dispatch, location, noteData) {
|
||||||
@@ -263,8 +284,12 @@ class StorageItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDrop (e, storage, folder, dispatch, location) {
|
handleDrop (e, storage, folder, dispatch, location) {
|
||||||
e.target.style.opacity = '1'
|
e.preventDefault()
|
||||||
e.target.style.backgroundColor = e.dataTransfer.getData('defaultColor')
|
if (this.state.draggedOver !== null) {
|
||||||
|
this.setState({
|
||||||
|
draggedOver: null
|
||||||
|
})
|
||||||
|
}
|
||||||
const noteData = JSON.parse(e.dataTransfer.getData('note'))
|
const noteData = JSON.parse(e.dataTransfer.getData('note'))
|
||||||
this.dropNote(storage, folder, dispatch, location, noteData)
|
this.dropNote(storage, folder, dispatch, location, noteData)
|
||||||
}
|
}
|
||||||
@@ -291,16 +316,22 @@ class StorageItem extends React.Component {
|
|||||||
<SortableStorageItemChild
|
<SortableStorageItemChild
|
||||||
key={folder.key}
|
key={folder.key}
|
||||||
index={index}
|
index={index}
|
||||||
isActive={isActive}
|
isActive={isActive || folder.key === this.state.draggedOver}
|
||||||
handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
|
handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
|
||||||
handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
|
handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
|
||||||
folderName={folder.name}
|
folderName={folder.name}
|
||||||
folderColor={folder.color}
|
folderColor={folder.color}
|
||||||
isFolded={isFolded}
|
isFolded={isFolded}
|
||||||
noteCount={noteCount}
|
noteCount={noteCount}
|
||||||
handleDrop={(e) => this.handleDrop(e, storage, folder, dispatch, location)}
|
handleDrop={(e) => {
|
||||||
handleDragEnter={this.handleDragEnter}
|
this.handleDrop(e, storage, folder, dispatch, location)
|
||||||
handleDragLeave={this.handleDragLeave}
|
}}
|
||||||
|
handleDragEnter={(e) => {
|
||||||
|
this.handleDragEnter(e, folder.key)
|
||||||
|
}}
|
||||||
|
handleDragLeave={(e) => {
|
||||||
|
this.handleDragLeave(e, folder)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import _ from 'lodash'
|
|||||||
import ee from 'browser/main/lib/eventEmitter'
|
import ee from 'browser/main/lib/eventEmitter'
|
||||||
import NewNoteButton from 'browser/main/NewNoteButton'
|
import NewNoteButton from 'browser/main/NewNoteButton'
|
||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
|
import debounce from 'lodash/debounce'
|
||||||
|
|
||||||
class TopBar extends React.Component {
|
class TopBar extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -25,6 +26,10 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.codeInitHandler = this.handleCodeInit.bind(this)
|
this.codeInitHandler = this.handleCodeInit.bind(this)
|
||||||
|
|
||||||
|
this.updateKeyword = debounce(this.updateKeyword, 1000 / 60, {
|
||||||
|
maxWait: 1000 / 8
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
@@ -94,7 +99,6 @@ class TopBar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleKeyUp (e) {
|
handleKeyUp (e) {
|
||||||
const { router } = this.context
|
|
||||||
// reset states
|
// reset states
|
||||||
this.setState({
|
this.setState({
|
||||||
isConfirmTranslation: false
|
isConfirmTranslation: false
|
||||||
@@ -106,21 +110,21 @@ class TopBar extends React.Component {
|
|||||||
isConfirmTranslation: true
|
isConfirmTranslation: true
|
||||||
})
|
})
|
||||||
const keyword = this.refs.searchInput.value
|
const keyword = this.refs.searchInput.value
|
||||||
router.push(`/searched/${encodeURIComponent(keyword)}`)
|
this.updateKeyword(keyword)
|
||||||
this.setState({
|
|
||||||
search: keyword
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchChange (e) {
|
handleSearchChange (e) {
|
||||||
const { router } = this.context
|
|
||||||
const keyword = this.refs.searchInput.value
|
|
||||||
if (this.state.isAlphabet || this.state.isConfirmTranslation) {
|
if (this.state.isAlphabet || this.state.isConfirmTranslation) {
|
||||||
router.push(`/searched/${encodeURIComponent(keyword)}`)
|
const keyword = this.refs.searchInput.value
|
||||||
|
this.updateKeyword(keyword)
|
||||||
} else {
|
} else {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateKeyword (keyword) {
|
||||||
|
this.context.router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||||
this.setState({
|
this.setState({
|
||||||
search: keyword
|
search: keyword
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
hotkey: {
|
hotkey: {
|
||||||
toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E',
|
toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E',
|
||||||
toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M',
|
toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M',
|
||||||
deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace'
|
deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace',
|
||||||
|
pasteSmartly: OSX ? 'Command + Shift + V' : 'Ctrl + Shift + V'
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
language: 'en',
|
language: 'en',
|
||||||
@@ -52,7 +53,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
enableTableEditor: false,
|
enableTableEditor: false,
|
||||||
enableFrontMatterTitle: true,
|
enableFrontMatterTitle: true,
|
||||||
frontMatterTitleField: 'title',
|
frontMatterTitleField: 'title',
|
||||||
spellcheck: false
|
spellcheck: false,
|
||||||
|
enableSmartPaste: false
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
fontSize: '14',
|
fontSize: '14',
|
||||||
|
|||||||
@@ -227,7 +227,15 @@ function migrateAttachments (markdownContent, storagePath, noteKey) {
|
|||||||
* @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
|
* @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
|
||||||
*/
|
*/
|
||||||
function fixLocalURLS (renderedHTML, storagePath) {
|
function fixLocalURLS (renderedHTML, storagePath) {
|
||||||
return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?"', 'g'), function (match) {
|
/*
|
||||||
|
A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`.
|
||||||
|
|
||||||
|
- `STORAGE_FOLDER_PLACEHOLDER` will match `:storage`
|
||||||
|
- `(?:(?:\\\/|%5C)[\\w.]+)+` will match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`
|
||||||
|
- `(?:\\\/|%5C)[\\w.]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg`
|
||||||
|
- `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows.
|
||||||
|
*/
|
||||||
|
return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[\\w.]+)+', 'g'), function (match) {
|
||||||
var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
|
var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
|
||||||
return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
|
return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
|
||||||
})
|
})
|
||||||
@@ -316,6 +324,44 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem
|
|||||||
reader.readAsDataURL(blob)
|
reader.readAsDataURL(blob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Creates a new file in the storage folder belonging to the current note and inserts the correct markdown code
|
||||||
|
* @param {CodeEditor} codeEditor Markdown editor. Its insertAttachmentMd() method will be called to include the markdown code
|
||||||
|
* @param {String} storageKey Key of the current storage
|
||||||
|
* @param {String} noteKey Key of the current note
|
||||||
|
* @param {NativeImage} image The native image
|
||||||
|
*/
|
||||||
|
function handlePastNativeImage (codeEditor, storageKey, noteKey, image) {
|
||||||
|
if (!codeEditor) {
|
||||||
|
throw new Error('codeEditor has to be given')
|
||||||
|
}
|
||||||
|
if (!storageKey) {
|
||||||
|
throw new Error('storageKey has to be given')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noteKey) {
|
||||||
|
throw new Error('noteKey has to be given')
|
||||||
|
}
|
||||||
|
if (!image) {
|
||||||
|
throw new Error('image has to be given')
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetStorage = findStorage.findStorage(storageKey)
|
||||||
|
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
|
||||||
|
|
||||||
|
createAttachmentDestinationFolder(targetStorage.path, noteKey)
|
||||||
|
|
||||||
|
const imageName = `${uniqueSlug()}.png`
|
||||||
|
const imagePath = path.join(destinationDir, imageName)
|
||||||
|
|
||||||
|
const binaryData = image.toPNG()
|
||||||
|
fs.writeFileSync(imagePath, binaryData, 'binary')
|
||||||
|
|
||||||
|
const imageReferencePath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, imageName)
|
||||||
|
const imageMd = generateAttachmentMarkdown(imageName, imageReferencePath, true)
|
||||||
|
codeEditor.insertAttachmentMd(imageMd)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Returns all attachment paths of the given markdown
|
* @description Returns all attachment paths of the given markdown
|
||||||
* @param {String} markdownContent content in which the attachment paths should be found
|
* @param {String} markdownContent content in which the attachment paths should be found
|
||||||
@@ -539,6 +585,7 @@ module.exports = {
|
|||||||
generateAttachmentMarkdown,
|
generateAttachmentMarkdown,
|
||||||
handleAttachmentDrop,
|
handleAttachmentDrop,
|
||||||
handlePastImageEvent,
|
handlePastImageEvent,
|
||||||
|
handlePastNativeImage,
|
||||||
getAttachmentsInMarkdownContent,
|
getAttachmentsInMarkdownContent,
|
||||||
getAbsolutePathsOfAttachmentsInContent,
|
getAbsolutePathsOfAttachmentsInContent,
|
||||||
removeStorageAndNoteReferences,
|
removeStorageAndNoteReferences,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function copyFile (srcPath, dstPath) {
|
|||||||
const dstFolder = path.dirname(dstPath)
|
const dstFolder = path.dirname(dstPath)
|
||||||
if (!fs.existsSync(dstFolder)) fs.mkdirSync(dstFolder)
|
if (!fs.existsSync(dstFolder)) fs.mkdirSync(dstFolder)
|
||||||
|
|
||||||
const input = fs.createReadStream(srcPath)
|
const input = fs.createReadStream(decodeURI(srcPath))
|
||||||
const output = fs.createWriteStream(dstPath)
|
const output = fs.createWriteStream(dstPath)
|
||||||
|
|
||||||
output.on('error', reject)
|
output.on('error', reject)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ function validateInput (input) {
|
|||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case 'MARKDOWN_NOTE':
|
case 'MARKDOWN_NOTE':
|
||||||
if (!_.isString(input.content)) input.content = ''
|
if (!_.isString(input.content)) input.content = ''
|
||||||
|
if (!_.isArray(input.linesHighlighted)) input.linesHighlighted = []
|
||||||
break
|
break
|
||||||
case 'SNIPPET_NOTE':
|
case 'SNIPPET_NOTE':
|
||||||
if (!_.isString(input.description)) input.description = ''
|
if (!_.isString(input.description)) input.description = ''
|
||||||
@@ -23,7 +24,8 @@ function validateInput (input) {
|
|||||||
input.snippets = [{
|
input.snippets = [{
|
||||||
name: '',
|
name: '',
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ function createSnippet (snippetFile) {
|
|||||||
id: crypto.randomBytes(16).toString('hex'),
|
id: crypto.randomBytes(16).toString('hex'),
|
||||||
name: 'Unnamed snippet',
|
name: 'Unnamed snippet',
|
||||||
prefix: [],
|
prefix: [],
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}
|
}
|
||||||
fetchSnippet(null, snippetFile).then((snippets) => {
|
fetchSnippet(null, snippetFile).then((snippets) => {
|
||||||
snippets.push(newSnippet)
|
snippets.push(newSnippet)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { findStorage } from 'browser/lib/findStorage'
|
import { findStorage } from 'browser/lib/findStorage'
|
||||||
import resolveStorageData from './resolveStorageData'
|
import resolveStorageData from './resolveStorageData'
|
||||||
import resolveStorageNotes from './resolveStorageNotes'
|
import resolveStorageNotes from './resolveStorageNotes'
|
||||||
|
import exportNote from './exportNote'
|
||||||
import filenamify from 'filenamify'
|
import filenamify from 'filenamify'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as fs from 'fs'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} storageKey
|
* @param {String} storageKey
|
||||||
@@ -45,9 +45,9 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) {
|
|||||||
|
|
||||||
notes
|
notes
|
||||||
.filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE')
|
.filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE')
|
||||||
.forEach(snippet => {
|
.forEach(note => {
|
||||||
const notePath = path.join(exportDir, `${filenamify(snippet.title, {replacement: '_'})}.${fileType}`)
|
const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`)
|
||||||
fs.writeFileSync(notePath, snippet.content)
|
exportNote(note.key, storage.path, note.content, notePath, null)
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,27 +4,43 @@ import { findStorage } from 'browser/lib/findStorage'
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
const attachmentManagement = require('./attachmentManagement')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export note together with images
|
* Export note together with attachments
|
||||||
*
|
*
|
||||||
* If images is stored in the storage, creates 'images' subfolder in target directory
|
* If attachments are stored in the storage, creates 'attachments' subfolder in target directory
|
||||||
* and copies images to it. Changes links to images in the content of the note
|
* and copies attachments to it. Changes links to images in the content of the note
|
||||||
*
|
*
|
||||||
|
* @param {String} nodeKey key of the node that should be exported
|
||||||
* @param {String} storageKey or storage path
|
* @param {String} storageKey or storage path
|
||||||
* @param {String} noteContent Content to export
|
* @param {String} noteContent Content to export
|
||||||
* @param {String} targetPath Path to exported file
|
* @param {String} targetPath Path to exported file
|
||||||
* @param {function} outputFormatter
|
* @param {function} outputFormatter
|
||||||
* @return {Promise.<*[]>}
|
* @return {Promise.<*[]>}
|
||||||
*/
|
*/
|
||||||
function exportNote (storageKey, noteContent, targetPath, outputFormatter) {
|
function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatter) {
|
||||||
const storagePath = path.isAbsolute(storageKey) ? storageKey : findStorage(storageKey).path
|
const storagePath = path.isAbsolute(storageKey) ? storageKey : findStorage(storageKey).path
|
||||||
const exportTasks = []
|
const exportTasks = []
|
||||||
|
|
||||||
if (!storagePath) {
|
if (!storagePath) {
|
||||||
throw new Error('Storage path is not found')
|
throw new Error('Storage path is not found')
|
||||||
}
|
}
|
||||||
|
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
|
||||||
|
noteContent,
|
||||||
|
storagePath
|
||||||
|
)
|
||||||
|
attachmentsAbsolutePaths.forEach(attachment => {
|
||||||
|
exportTasks.push({
|
||||||
|
src: attachment,
|
||||||
|
dst: attachmentManagement.DESTINATION_FOLDER
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
let exportedData = noteContent
|
let exportedData = attachmentManagement.removeStorageAndNoteReferences(
|
||||||
|
noteContent,
|
||||||
|
nodeKey
|
||||||
|
)
|
||||||
|
|
||||||
if (outputFormatter) {
|
if (outputFormatter) {
|
||||||
exportedData = outputFormatter(exportedData, exportTasks)
|
exportedData = outputFormatter(exportedData, exportTasks)
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ function importAll (storage, data) {
|
|||||||
isStarred: false,
|
isStarred: false,
|
||||||
title: article.title,
|
title: article.title,
|
||||||
content: '# ' + article.title + '\n\n' + article.content,
|
content: '# ' + article.title + '\n\n' + article.content,
|
||||||
key: noteKey
|
key: noteKey,
|
||||||
|
linesHighlighted: article.linesHighlighted
|
||||||
}
|
}
|
||||||
notes.push(newNote)
|
notes.push(newNote)
|
||||||
} else {
|
} else {
|
||||||
@@ -87,7 +88,8 @@ function importAll (storage, data) {
|
|||||||
snippets: [{
|
snippets: [{
|
||||||
name: article.mode,
|
name: article.mode,
|
||||||
mode: article.mode,
|
mode: article.mode,
|
||||||
content: article.content
|
content: article.content,
|
||||||
|
linesHighlighted: article.linesHighlighted
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
notes.push(newNote)
|
notes.push(newNote)
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ function validateInput (input) {
|
|||||||
if (input.content != null) {
|
if (input.content != null) {
|
||||||
if (!_.isString(input.content)) validatedInput.content = ''
|
if (!_.isString(input.content)) validatedInput.content = ''
|
||||||
else validatedInput.content = input.content
|
else validatedInput.content = input.content
|
||||||
|
|
||||||
|
if (!_.isArray(input.linesHighlighted)) validatedInput.linesHighlighted = []
|
||||||
|
else validatedInput.linesHighlighted = input.linesHighlighted
|
||||||
}
|
}
|
||||||
return validatedInput
|
return validatedInput
|
||||||
case 'SNIPPET_NOTE':
|
case 'SNIPPET_NOTE':
|
||||||
@@ -51,7 +54,8 @@ function validateInput (input) {
|
|||||||
validatedInput.snippets = [{
|
validatedInput.snippets = [{
|
||||||
name: '',
|
name: '',
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}]
|
}]
|
||||||
} else {
|
} else {
|
||||||
validatedInput.snippets = input.snippets
|
validatedInput.snippets = input.snippets
|
||||||
@@ -96,12 +100,14 @@ function updateNote (storageKey, noteKey, input) {
|
|||||||
snippets: [{
|
snippets: [{
|
||||||
name: '',
|
name: '',
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
type: 'MARKDOWN_NOTE',
|
type: 'MARKDOWN_NOTE',
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}
|
}
|
||||||
noteData.title = ''
|
noteData.title = ''
|
||||||
if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.')
|
if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.')
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ function updateSnippet (snippet, snippetFile) {
|
|||||||
if (
|
if (
|
||||||
currentSnippet.name === snippet.name &&
|
currentSnippet.name === snippet.name &&
|
||||||
currentSnippet.prefix === snippet.prefix &&
|
currentSnippet.prefix === snippet.prefix &&
|
||||||
currentSnippet.content === snippet.content
|
currentSnippet.content === snippet.content &&
|
||||||
|
currentSnippet.linesHighlighted === snippet.linesHighlighted
|
||||||
) {
|
) {
|
||||||
// if everything is the same then don't write to disk
|
// if everything is the same then don't write to disk
|
||||||
resolve(snippets)
|
resolve(snippets)
|
||||||
@@ -20,6 +21,7 @@ function updateSnippet (snippet, snippetFile) {
|
|||||||
currentSnippet.name = snippet.name
|
currentSnippet.name = snippet.name
|
||||||
currentSnippet.prefix = snippet.prefix
|
currentSnippet.prefix = snippet.prefix
|
||||||
currentSnippet.content = snippet.content
|
currentSnippet.content = snippet.content
|
||||||
|
currentSnippet.linesHighlighted = (snippet.linesHighlighted)
|
||||||
fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => {
|
fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => {
|
||||||
if (err) reject(err)
|
if (err) reject(err)
|
||||||
resolve(snippets)
|
resolve(snippets)
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ class HotkeyTab extends React.Component {
|
|||||||
config.hotkey = {
|
config.hotkey = {
|
||||||
toggleMain: this.refs.toggleMain.value,
|
toggleMain: this.refs.toggleMain.value,
|
||||||
toggleMode: this.refs.toggleMode.value,
|
toggleMode: this.refs.toggleMode.value,
|
||||||
deleteNote: this.refs.deleteNote.value
|
deleteNote: this.refs.deleteNote.value,
|
||||||
|
pasteSmartly: this.refs.pasteSmartly.value
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
config
|
config
|
||||||
@@ -149,6 +150,17 @@ class HotkeyTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>{i18n.__('Paste Smartly')}</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
onChange={(e) => this.handleHotkeyChange(e)}
|
||||||
|
ref='pasteSmartly'
|
||||||
|
value={config.hotkey.pasteSmartly}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div styleName='group-control'>
|
<div styleName='group-control'>
|
||||||
<button styleName='group-control-leftButton'
|
<button styleName='group-control-leftButton'
|
||||||
onClick={(e) => this.handleHintToggleButtonClick(e)}
|
onClick={(e) => this.handleHintToggleButtonClick(e)}
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ class InfoTab extends React.Component {
|
|||||||
<div styleName='header--sub'>{i18n.__('Community')}</div>
|
<div styleName='header--sub'>{i18n.__('Community')}</div>
|
||||||
<div styleName='top'>
|
<div styleName='top'>
|
||||||
<ul styleName='list'>
|
<ul styleName='list'>
|
||||||
|
<li>
|
||||||
|
<a href='https://issuehunt.io/repos/53266139'
|
||||||
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
|
>{i18n.__('Bounty on IssueHunt')}</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href='https://boostnote.io/#subscribe'
|
<a href='https://boostnote.io/#subscribe'
|
||||||
onClick={(e) => this.handleLinkClick(e)}
|
onClick={(e) => this.handleLinkClick(e)}
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ class UiTab extends React.Component {
|
|||||||
enableTableEditor: this.refs.enableTableEditor.checked,
|
enableTableEditor: this.refs.enableTableEditor.checked,
|
||||||
enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked,
|
enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked,
|
||||||
frontMatterTitleField: this.refs.frontMatterTitleField.value,
|
frontMatterTitleField: this.refs.frontMatterTitleField.value,
|
||||||
spellcheck: this.refs.spellcheck.checked
|
spellcheck: this.refs.spellcheck.checked,
|
||||||
|
enableSmartPaste: this.refs.enableSmartPaste.checked
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
fontSize: this.refs.previewFontSize.value,
|
fontSize: this.refs.previewFontSize.value,
|
||||||
@@ -552,6 +553,18 @@ class UiTab extends React.Component {
|
|||||||
{i18n.__('Enable smart table editor')}
|
{i18n.__('Enable smart table editor')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div styleName='group-checkBoxSection'>
|
||||||
|
<label>
|
||||||
|
<input onChange={(e) => this.handleUIChange(e)}
|
||||||
|
checked={this.state.config.editor.enableSmartPaste}
|
||||||
|
ref='enableSmartPaste'
|
||||||
|
type='checkbox'
|
||||||
|
/>
|
||||||
|
{i18n.__('Enable smart paste')}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div styleName='group-checkBoxSection'>
|
<div styleName='group-checkBoxSection'>
|
||||||
<label>
|
<label>
|
||||||
<input onChange={(e) => this.handleUIChange(e)}
|
<input onChange={(e) => this.handleUIChange(e)}
|
||||||
|
|||||||
@@ -240,10 +240,8 @@ navWhiteButtonColor()
|
|||||||
&:hover
|
&:hover
|
||||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
color $ui-text-color
|
|
||||||
&:active, &:active:hover
|
&:active, &:active:hover
|
||||||
background-color $ui-button--active-backgroundColor
|
background-color $ui-button--active-backgroundColor
|
||||||
color $ui-text-color
|
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
|
|
||||||
// UI Button
|
// UI Button
|
||||||
|
|||||||
@@ -85,39 +85,24 @@ const file = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Focus Note',
|
label: 'Focus Note',
|
||||||
accelerator: 'Control+E',
|
accelerator: macOS ? 'Command+E' : 'Control+E',
|
||||||
click () {
|
click () {
|
||||||
mainWindow.webContents.send('detail:focus')
|
mainWindow.webContents.send('detail:focus')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
label: 'Delete Note',
|
||||||
|
accelerator: macOS ? 'Command+Shift+Backspace' : 'Control+Shift+Backspace',
|
||||||
|
click () {
|
||||||
|
mainWindow.webContents.send('detail:delete')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Export as',
|
label: 'Clone Note',
|
||||||
submenu: [
|
accelerator: macOS ? 'Command+D' : 'Control+D',
|
||||||
{
|
click () {
|
||||||
label: 'Plain Text (.txt)',
|
mainWindow.webContents.send('list:clone')
|
||||||
click () {
|
}
|
||||||
mainWindow.webContents.send('list:isMarkdownNote')
|
|
||||||
mainWindow.webContents.send('export:save-text')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'MarkDown (.md)',
|
|
||||||
click () {
|
|
||||||
mainWindow.webContents.send('list:isMarkdownNote')
|
|
||||||
mainWindow.webContents.send('export:save-md')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'HTML (.html)',
|
|
||||||
click () {
|
|
||||||
mainWindow.webContents.send('list:isMarkdownNote')
|
|
||||||
mainWindow.webContents.send('export:save-html')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
@@ -134,13 +119,30 @@ const file = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
label: 'Export as',
|
||||||
},
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Format Table',
|
label: 'Plain Text (.txt)',
|
||||||
click () {
|
click () {
|
||||||
mainWindow.webContents.send('code:format-table')
|
mainWindow.webContents.send('list:isMarkdownNote', 'export-txt')
|
||||||
}
|
mainWindow.webContents.send('export:save-text')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'MarkDown (.md)',
|
||||||
|
click () {
|
||||||
|
mainWindow.webContents.send('list:isMarkdownNote', 'export-md')
|
||||||
|
mainWindow.webContents.send('export:save-md')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'HTML (.html)',
|
||||||
|
click () {
|
||||||
|
mainWindow.webContents.send('list:isMarkdownNote', 'export-html')
|
||||||
|
mainWindow.webContents.send('export:save-html')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
@@ -153,24 +155,20 @@ const file = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
label: 'Format Table',
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Print',
|
|
||||||
accelerator: 'CommandOrControl+P',
|
|
||||||
click () {
|
click () {
|
||||||
mainWindow.webContents.send('list:isMarkdownNote')
|
mainWindow.webContents.send('code:format-table')
|
||||||
mainWindow.webContents.send('print')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'separator'
|
type: 'separator'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Delete Note',
|
label: 'Print',
|
||||||
accelerator: macOS ? 'Control+Backspace' : 'Control+Delete',
|
accelerator: 'CommandOrControl+P',
|
||||||
click () {
|
click () {
|
||||||
mainWindow.webContents.send('detail:delete')
|
mainWindow.webContents.send('list:isMarkdownNote', 'print')
|
||||||
|
mainWindow.webContents.send('print')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -296,9 +294,6 @@ const view = {
|
|||||||
mainWindow.setFullScreen(!mainWindow.isFullScreen())
|
mainWindow.setFullScreen(!mainWindow.isFullScreen())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'separator'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Toggle Side Bar',
|
label: 'Toggle Side Bar',
|
||||||
accelerator: 'CommandOrControl+B',
|
accelerator: 'CommandOrControl+B',
|
||||||
|
|||||||
@@ -37,9 +37,15 @@
|
|||||||
"White": "白",
|
"White": "白",
|
||||||
"Solarized Dark": "明灰",
|
"Solarized Dark": "明灰",
|
||||||
"Dark": "暗灰",
|
"Dark": "暗灰",
|
||||||
|
"Default New Note": "新規ノートの形式",
|
||||||
|
"Always Ask": "作成時に聞く",
|
||||||
"Show a confirmation dialog when deleting notes": "ノートを削除する時に確認ダイアログを表示する",
|
"Show a confirmation dialog when deleting notes": "ノートを削除する時に確認ダイアログを表示する",
|
||||||
"Disable Direct Write (It will be applied after restarting)": "Disable Direct Write (It will be applied after restarting)",
|
"Disable Direct Write (It will be applied after restarting)": "Disable Direct Write (It will be applied after restarting)",
|
||||||
|
"Save tags of a note in alphabetical order": "ノートのタグをアルファベット順に保存する",
|
||||||
|
"Show tags of a note in alphabetical order": "ノートのタグをアルファベット順に表示する",
|
||||||
"Show only related tags": "関連するタグのみ表示する",
|
"Show only related tags": "関連するタグのみ表示する",
|
||||||
|
"Enable live count of notes": "タグ選択時にノート数を再計算して表示する",
|
||||||
|
"New notes are tagged with the filtering tags": "新規ノートに選択中のタグを付与する",
|
||||||
"Editor Theme": "エディタのテーマ",
|
"Editor Theme": "エディタのテーマ",
|
||||||
"Editor Font Size": "エディタのフォントサイズ",
|
"Editor Font Size": "エディタのフォントサイズ",
|
||||||
"Editor Font Family": "エディタのフォント",
|
"Editor Font Family": "エディタのフォント",
|
||||||
@@ -55,21 +61,28 @@
|
|||||||
"vim": "vim",
|
"vim": "vim",
|
||||||
"emacs": "emacs",
|
"emacs": "emacs",
|
||||||
"⚠️ Please restart boostnote after you change the keymap": "⚠️ キーマップ変更後は Boostnote を再起動してください",
|
"⚠️ Please restart boostnote after you change the keymap": "⚠️ キーマップ変更後は Boostnote を再起動してください",
|
||||||
|
"Snippet Default Language": "スニペットのデフォルト言語",
|
||||||
|
"Extract title from front matter": "Front matterからタイトルを抽出する",
|
||||||
"Show line numbers in the editor": "エディタ内に行番号を表示",
|
"Show line numbers in the editor": "エディタ内に行番号を表示",
|
||||||
"Allow editor to scroll past the last line": "エディタが最終行以降にスクロールできるようにする",
|
"Allow editor to scroll past the last line": "エディタが最終行以降にスクロールできるようにする",
|
||||||
"Enable smart quotes": "スマートクォートを有効にする",
|
"Enable smart quotes": "スマートクォートを有効にする",
|
||||||
"Bring in web page title when pasting URL on editor": "URLを貼り付けた時にWebページのタイトルを取得する",
|
"Bring in web page title when pasting URL on editor": "URLを貼り付けた時にWebページのタイトルを取得する",
|
||||||
|
"Enable smart table editor": "スマートテーブルエディタを有効にする",
|
||||||
"Preview": "プレビュー",
|
"Preview": "プレビュー",
|
||||||
"Preview Font Size": "プレビュー時フォントサイズ",
|
"Preview Font Size": "プレビュー時フォントサイズ",
|
||||||
"Preview Font Family": "プレビュー時フォント",
|
"Preview Font Family": "プレビュー時フォント",
|
||||||
"Code Block Theme": "コードブロックのテーマ",
|
"Code Block Theme": "コードブロックのテーマ",
|
||||||
|
"Allow line through checkbox": "チェック済みチェックボックスのテキストに取り消し線を付与する",
|
||||||
"Allow preview to scroll past the last line": "プレビュー時に最終行以降にスクロールできるようにする",
|
"Allow preview to scroll past the last line": "プレビュー時に最終行以降にスクロールできるようにする",
|
||||||
|
"When scrolling, synchronize preview with editor": "エディタとプレビューのスクロールを同期する",
|
||||||
"Show line numbers for preview code blocks": "プレビュー時のコードブロック内に行番号を表示する",
|
"Show line numbers for preview code blocks": "プレビュー時のコードブロック内に行番号を表示する",
|
||||||
"LaTeX Inline Open Delimiter": "LaTeX 開始デリミタ(インライン)",
|
"LaTeX Inline Open Delimiter": "LaTeX 開始デリミタ(インライン)",
|
||||||
"LaTeX Inline Close Delimiter": "LaTeX 終了デリミタ(インライン)",
|
"LaTeX Inline Close Delimiter": "LaTeX 終了デリミタ(インライン)",
|
||||||
"LaTeX Block Open Delimiter": "LaTeX 開始デリミタ(ブロック)",
|
"LaTeX Block Open Delimiter": "LaTeX 開始デリミタ(ブロック)",
|
||||||
"LaTeX Block Close Delimiter": "LaTeX 終了デリミタ(ブロック)",
|
"LaTeX Block Close Delimiter": "LaTeX 終了デリミタ(ブロック)",
|
||||||
"PlantUML Server": "PlantUML サーバー",
|
"PlantUML Server": "PlantUML サーバー",
|
||||||
|
"Custom CSS": "カスタムCSS",
|
||||||
|
"Allow custom CSS for preview": "プレビュー用のカスタムCSSを許可する",
|
||||||
"Community": "コミュニティ",
|
"Community": "コミュニティ",
|
||||||
"Subscribe to Newsletter": "ニュースレターを購読する",
|
"Subscribe to Newsletter": "ニュースレターを購読する",
|
||||||
"GitHub": "GitHub",
|
"GitHub": "GitHub",
|
||||||
@@ -135,6 +148,7 @@
|
|||||||
"Hotkeys": "ホットキー",
|
"Hotkeys": "ホットキー",
|
||||||
"Show/Hide Boostnote": "Boostnote の表示/非表示",
|
"Show/Hide Boostnote": "Boostnote の表示/非表示",
|
||||||
"Toggle Editor Mode": "エディタモードの切替",
|
"Toggle Editor Mode": "エディタモードの切替",
|
||||||
|
"Delete Note": "ノート削除",
|
||||||
"Restore": "リストア",
|
"Restore": "リストア",
|
||||||
"Permanent Delete": "永久に削除",
|
"Permanent Delete": "永久に削除",
|
||||||
"Confirm note deletion": "ノート削除確認",
|
"Confirm note deletion": "ノート削除確認",
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "boost",
|
"name": "boost",
|
||||||
"productName": "Boostnote",
|
"productName": "Boostnote",
|
||||||
"version": "0.11.11",
|
"version": "0.11.12",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"description": "Boostnote",
|
"description": "Boostnote",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"escape-string-regexp": "^1.0.5",
|
"escape-string-regexp": "^1.0.5",
|
||||||
"file-uri-to-path": "^1.0.0",
|
"file-uri-to-path": "^1.0.0",
|
||||||
"file-url": "^2.0.2",
|
"file-url": "^2.0.2",
|
||||||
"filenamify": "^2.0.0",
|
"filenamify": "^2.1.0",
|
||||||
"flowchart.js": "^1.6.5",
|
"flowchart.js": "^1.6.5",
|
||||||
"font-awesome": "^4.3.0",
|
"font-awesome": "^4.3.0",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
@@ -99,8 +99,10 @@
|
|||||||
"react-codemirror": "^0.3.0",
|
"react-codemirror": "^0.3.0",
|
||||||
"react-debounce-render": "^4.0.1",
|
"react-debounce-render": "^4.0.1",
|
||||||
"react-dom": "^15.0.2",
|
"react-dom": "^15.0.2",
|
||||||
|
"react-image-carousel": "^2.0.18",
|
||||||
"react-redux": "^4.4.5",
|
"react-redux": "^4.4.5",
|
||||||
"react-sortable-hoc": "^0.6.7",
|
"react-sortable-hoc": "^0.6.7",
|
||||||
|
"react-transition-group": "^2.5.0",
|
||||||
"redux": "^3.5.2",
|
"redux": "^3.5.2",
|
||||||
"sander": "^0.5.1",
|
"sander": "^0.5.1",
|
||||||
"sanitize-html": "^1.18.2",
|
"sanitize-html": "^1.18.2",
|
||||||
@@ -142,6 +144,7 @@
|
|||||||
"grunt": "^0.4.5",
|
"grunt": "^0.4.5",
|
||||||
"grunt-electron-installer": "2.1.0",
|
"grunt-electron-installer": "2.1.0",
|
||||||
"history": "^1.17.0",
|
"history": "^1.17.0",
|
||||||
|
"husky": "^1.1.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^22.4.3",
|
"jest": "^22.4.3",
|
||||||
"jest-localstorage-mock": "^2.2.0",
|
"jest-localstorage-mock": "^2.2.0",
|
||||||
@@ -189,5 +192,10 @@
|
|||||||
"<rootDir>/tests/jest.js",
|
"<rootDir>/tests/jest.js",
|
||||||
"jest-localstorage-mock"
|
"jest-localstorage-mock"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "npm run lint"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
<h4 align="center">Note-taking app for programmers. </h4>
|
<h4 align="center">Note-taking app for programmers. </h4>
|
||||||
<h5 align="center">Apps available for Mac, Windows, Linux, Android, and iOS.</h5>
|
<h5 align="center">Apps available for Mac, Windows, Linux, Android, and iOS.</h5>
|
||||||
<h5 align="center">Built with Electron, React + Redux, Webpack, and CSSModules.</h5>
|
<h5 align="center">Built with Electron, React + Redux, Webpack, and CSSModules.</h5>
|
||||||
|
<p align="center">
|
||||||
[](https://travis-ci.org/BoostIO/Boostnote)
|
<a href="https://travis-ci.org/BoostIO/Boostnote">
|
||||||
|
<img src="https://travis-ci.org/BoostIO/Boostnote.svg?branch=master" alt="Build Status" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Authors & Maintainers
|
## Authors & Maintainers
|
||||||
- [Rokt33r](https://github.com/rokt33r)
|
- [Rokt33r](https://github.com/rokt33r)
|
||||||
@@ -24,7 +27,7 @@ Boostnote is an open source project. It's an independent project with its ongoin
|
|||||||
|
|
||||||
Issues on Boostnote can be funded by anyone and the money will be distributed to contributors and maintainers. If you use Boostnote please consider becoming a backer:
|
Issues on Boostnote can be funded by anyone and the money will be distributed to contributors and maintainers. If you use Boostnote please consider becoming a backer:
|
||||||
|
|
||||||
[](https://issuehunt.io/repos/53266139)
|
[](https://issuehunt.io/repos/53266139)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
|
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
|
||||||
|
|||||||
35
tests/dataApi/copyFile-test.js
Normal file
35
tests/dataApi/copyFile-test.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const test = require('ava')
|
||||||
|
const copyFile = require('browser/main/lib/dataApi/copyFile')
|
||||||
|
|
||||||
|
const path = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
const testFile = 'test.txt'
|
||||||
|
const srcFolder = path.join(__dirname, '🤔')
|
||||||
|
const srcPath = path.join(srcFolder, testFile)
|
||||||
|
const dstFolder = path.join(__dirname, '😇')
|
||||||
|
const dstPath = path.join(dstFolder, testFile)
|
||||||
|
|
||||||
|
test.before((t) => {
|
||||||
|
if (!fs.existsSync(srcFolder)) fs.mkdirSync(srcFolder)
|
||||||
|
|
||||||
|
fs.writeFileSync(srcPath, 'test')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('`copyFile` should handle encoded URI on src path', (t) => {
|
||||||
|
return copyFile(encodeURI(srcPath), dstPath)
|
||||||
|
.then(() => {
|
||||||
|
t.true(true)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
t.true(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test.after((t) => {
|
||||||
|
fs.unlinkSync(srcPath)
|
||||||
|
fs.unlinkSync(dstPath)
|
||||||
|
fs.rmdirSync(srcFolder)
|
||||||
|
fs.rmdirSync(dstFolder)
|
||||||
|
})
|
||||||
|
|
||||||
@@ -25,13 +25,16 @@ test.serial('Create a note', (t) => {
|
|||||||
const storageKey = t.context.storage.cache.key
|
const storageKey = t.context.storage.cache.key
|
||||||
const folderKey = t.context.storage.json.folders[0].key
|
const folderKey = t.context.storage.json.folders[0].key
|
||||||
|
|
||||||
|
const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10))
|
||||||
|
|
||||||
const input1 = {
|
const input1 = {
|
||||||
type: 'SNIPPET_NOTE',
|
type: 'SNIPPET_NOTE',
|
||||||
description: faker.lorem.lines(),
|
description: faker.lorem.lines(),
|
||||||
snippets: [{
|
snippets: [{
|
||||||
name: faker.system.fileName(),
|
name: faker.system.fileName(),
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
content: faker.lorem.lines()
|
content: faker.lorem.lines(),
|
||||||
|
linesHighlighted: randLinesHighlightedArray
|
||||||
}],
|
}],
|
||||||
tags: faker.lorem.words().split(' '),
|
tags: faker.lorem.words().split(' '),
|
||||||
folder: folderKey
|
folder: folderKey
|
||||||
@@ -42,7 +45,8 @@ test.serial('Create a note', (t) => {
|
|||||||
type: 'MARKDOWN_NOTE',
|
type: 'MARKDOWN_NOTE',
|
||||||
content: faker.lorem.lines(),
|
content: faker.lorem.lines(),
|
||||||
tags: faker.lorem.words().split(' '),
|
tags: faker.lorem.words().split(' '),
|
||||||
folder: folderKey
|
folder: folderKey,
|
||||||
|
linesHighlighted: randLinesHighlightedArray
|
||||||
}
|
}
|
||||||
input2.title = input2.content.split('\n').shift()
|
input2.title = input2.content.split('\n').shift()
|
||||||
|
|
||||||
@@ -59,6 +63,7 @@ test.serial('Create a note', (t) => {
|
|||||||
|
|
||||||
t.is(storageKey, data1.storage)
|
t.is(storageKey, data1.storage)
|
||||||
const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson'))
|
const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson'))
|
||||||
|
|
||||||
t.is(input1.title, data1.title)
|
t.is(input1.title, data1.title)
|
||||||
t.is(input1.title, jsonData1.title)
|
t.is(input1.title, jsonData1.title)
|
||||||
t.is(input1.description, data1.description)
|
t.is(input1.description, data1.description)
|
||||||
@@ -71,6 +76,8 @@ test.serial('Create a note', (t) => {
|
|||||||
t.is(input1.snippets[0].content, jsonData1.snippets[0].content)
|
t.is(input1.snippets[0].content, jsonData1.snippets[0].content)
|
||||||
t.is(input1.snippets[0].name, data1.snippets[0].name)
|
t.is(input1.snippets[0].name, data1.snippets[0].name)
|
||||||
t.is(input1.snippets[0].name, jsonData1.snippets[0].name)
|
t.is(input1.snippets[0].name, jsonData1.snippets[0].name)
|
||||||
|
t.deepEqual(input1.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted)
|
||||||
|
t.deepEqual(input1.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted)
|
||||||
|
|
||||||
t.is(storageKey, data2.storage)
|
t.is(storageKey, data2.storage)
|
||||||
const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson'))
|
const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson'))
|
||||||
@@ -80,6 +87,8 @@ test.serial('Create a note', (t) => {
|
|||||||
t.is(input2.content, jsonData2.content)
|
t.is(input2.content, jsonData2.content)
|
||||||
t.is(input2.tags.length, data2.tags.length)
|
t.is(input2.tags.length, data2.tags.length)
|
||||||
t.is(input2.tags.length, jsonData2.tags.length)
|
t.is(input2.tags.length, jsonData2.tags.length)
|
||||||
|
t.deepEqual(input2.linesHighlighted, data2.linesHighlighted)
|
||||||
|
t.deepEqual(input2.linesHighlighted, jsonData2.linesHighlighted)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ test.serial('Create a snippet', (t) => {
|
|||||||
t.is(snippet.name, data.name)
|
t.is(snippet.name, data.name)
|
||||||
t.deepEqual(snippet.prefix, data.prefix)
|
t.deepEqual(snippet.prefix, data.prefix)
|
||||||
t.is(snippet.content, data.content)
|
t.is(snippet.content, data.content)
|
||||||
|
t.deepEqual(snippet.linesHighlighted, data.linesHighlighted)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,17 @@ test.serial('Update a note', (t) => {
|
|||||||
const storageKey = t.context.storage.cache.key
|
const storageKey = t.context.storage.cache.key
|
||||||
const folderKey = t.context.storage.json.folders[0].key
|
const folderKey = t.context.storage.json.folders[0].key
|
||||||
|
|
||||||
|
const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10))
|
||||||
|
const randLinesHighlightedArray2 = new Array(15).fill().map(() => Math.round(Math.random() * 15))
|
||||||
|
|
||||||
const input1 = {
|
const input1 = {
|
||||||
type: 'SNIPPET_NOTE',
|
type: 'SNIPPET_NOTE',
|
||||||
description: faker.lorem.lines(),
|
description: faker.lorem.lines(),
|
||||||
snippets: [{
|
snippets: [{
|
||||||
name: faker.system.fileName(),
|
name: faker.system.fileName(),
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
content: faker.lorem.lines()
|
content: faker.lorem.lines(),
|
||||||
|
linesHighlighted: randLinesHighlightedArray
|
||||||
}],
|
}],
|
||||||
tags: faker.lorem.words().split(' '),
|
tags: faker.lorem.words().split(' '),
|
||||||
folder: folderKey
|
folder: folderKey
|
||||||
@@ -43,7 +47,8 @@ test.serial('Update a note', (t) => {
|
|||||||
type: 'MARKDOWN_NOTE',
|
type: 'MARKDOWN_NOTE',
|
||||||
content: faker.lorem.lines(),
|
content: faker.lorem.lines(),
|
||||||
tags: faker.lorem.words().split(' '),
|
tags: faker.lorem.words().split(' '),
|
||||||
folder: folderKey
|
folder: folderKey,
|
||||||
|
linesHighlighted: randLinesHighlightedArray
|
||||||
}
|
}
|
||||||
input2.title = input2.content.split('\n').shift()
|
input2.title = input2.content.split('\n').shift()
|
||||||
|
|
||||||
@@ -53,7 +58,8 @@ test.serial('Update a note', (t) => {
|
|||||||
snippets: [{
|
snippets: [{
|
||||||
name: faker.system.fileName(),
|
name: faker.system.fileName(),
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
content: faker.lorem.lines()
|
content: faker.lorem.lines(),
|
||||||
|
linesHighlighted: randLinesHighlightedArray2
|
||||||
}],
|
}],
|
||||||
tags: faker.lorem.words().split(' ')
|
tags: faker.lorem.words().split(' ')
|
||||||
}
|
}
|
||||||
@@ -62,7 +68,8 @@ test.serial('Update a note', (t) => {
|
|||||||
const input4 = {
|
const input4 = {
|
||||||
type: 'MARKDOWN_NOTE',
|
type: 'MARKDOWN_NOTE',
|
||||||
content: faker.lorem.lines(),
|
content: faker.lorem.lines(),
|
||||||
tags: faker.lorem.words().split(' ')
|
tags: faker.lorem.words().split(' '),
|
||||||
|
linesHighlighted: randLinesHighlightedArray2
|
||||||
}
|
}
|
||||||
input4.title = input4.content.split('\n').shift()
|
input4.title = input4.content.split('\n').shift()
|
||||||
|
|
||||||
@@ -99,6 +106,8 @@ test.serial('Update a note', (t) => {
|
|||||||
t.is(input3.snippets[0].content, jsonData1.snippets[0].content)
|
t.is(input3.snippets[0].content, jsonData1.snippets[0].content)
|
||||||
t.is(input3.snippets[0].name, data1.snippets[0].name)
|
t.is(input3.snippets[0].name, data1.snippets[0].name)
|
||||||
t.is(input3.snippets[0].name, jsonData1.snippets[0].name)
|
t.is(input3.snippets[0].name, jsonData1.snippets[0].name)
|
||||||
|
t.deepEqual(input3.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted)
|
||||||
|
t.deepEqual(input3.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted)
|
||||||
|
|
||||||
const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson'))
|
const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson'))
|
||||||
t.is(input4.title, data2.title)
|
t.is(input4.title, data2.title)
|
||||||
@@ -107,6 +116,8 @@ test.serial('Update a note', (t) => {
|
|||||||
t.is(input4.content, jsonData2.content)
|
t.is(input4.content, jsonData2.content)
|
||||||
t.is(input4.tags.length, data2.tags.length)
|
t.is(input4.tags.length, data2.tags.length)
|
||||||
t.is(input4.tags.length, jsonData2.tags.length)
|
t.is(input4.tags.length, jsonData2.tags.length)
|
||||||
|
t.deepEqual(input4.linesHighlighted, data2.linesHighlighted)
|
||||||
|
t.deepEqual(input4.linesHighlighted, jsonData2.linesHighlighted)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
161
yarn.lock
161
yarn.lock
@@ -61,7 +61,6 @@
|
|||||||
"@enyaxu/markdown-it-anchor@^5.0.2":
|
"@enyaxu/markdown-it-anchor@^5.0.2":
|
||||||
version "5.0.2"
|
version "5.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@enyaxu/markdown-it-anchor/-/markdown-it-anchor-5.0.2.tgz#d173f7b60b492aabc17dfba864c4d071f5595f72"
|
resolved "https://registry.yarnpkg.com/@enyaxu/markdown-it-anchor/-/markdown-it-anchor-5.0.2.tgz#d173f7b60b492aabc17dfba864c4d071f5595f72"
|
||||||
integrity sha512-HBQ+by3IFHh2i5nw8fzn9qrdA+6uwzre68EzHpBX/WrwgnKrfvckPzdi7MphKp2C617edfpeibucslHDNPYkvQ==
|
|
||||||
|
|
||||||
"@ladjs/time-require@^0.1.4":
|
"@ladjs/time-require@^0.1.4":
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
@@ -1690,6 +1689,10 @@ ci-info@^1.0.0:
|
|||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2"
|
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2"
|
||||||
|
|
||||||
|
ci-info@^1.5.0:
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
|
||||||
|
|
||||||
circular-json@^0.3.1:
|
circular-json@^0.3.1:
|
||||||
version "0.3.3"
|
version "0.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
|
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
|
||||||
@@ -2107,6 +2110,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
|
|||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
|
|
||||||
|
cosmiconfig@^5.0.6:
|
||||||
|
version "5.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39"
|
||||||
|
dependencies:
|
||||||
|
is-directory "^0.3.1"
|
||||||
|
js-yaml "^3.9.0"
|
||||||
|
parse-json "^4.0.0"
|
||||||
|
|
||||||
create-error-class@^3.0.0, create-error-class@^3.0.1:
|
create-error-class@^3.0.0, create-error-class@^3.0.1:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
|
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
|
||||||
@@ -2749,6 +2760,10 @@ doctrine@^2.0.0, doctrine@^2.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
|
|
||||||
|
dom-helpers@^3.3.1:
|
||||||
|
version "3.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
|
||||||
|
|
||||||
dom-serializer@0:
|
dom-serializer@0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
|
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
|
||||||
@@ -3364,6 +3379,18 @@ execa@^0.7.0:
|
|||||||
signal-exit "^3.0.0"
|
signal-exit "^3.0.0"
|
||||||
strip-eof "^1.0.0"
|
strip-eof "^1.0.0"
|
||||||
|
|
||||||
|
execa@^0.9.0:
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01"
|
||||||
|
dependencies:
|
||||||
|
cross-spawn "^5.0.1"
|
||||||
|
get-stream "^3.0.0"
|
||||||
|
is-stream "^1.1.0"
|
||||||
|
npm-run-path "^2.0.0"
|
||||||
|
p-finally "^1.0.0"
|
||||||
|
signal-exit "^3.0.0"
|
||||||
|
strip-eof "^1.0.0"
|
||||||
|
|
||||||
exit-hook@^1.0.0:
|
exit-hook@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
||||||
@@ -3591,9 +3618,9 @@ filename-reserved-regex@^2.0.0:
|
|||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229"
|
resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229"
|
||||||
|
|
||||||
filenamify@^2.0.0:
|
filenamify@^2.1.0:
|
||||||
version "2.0.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.0.0.tgz#bd162262c0b6e94bfbcdcf19a3bbb3764f785695"
|
resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9"
|
||||||
dependencies:
|
dependencies:
|
||||||
filename-reserved-regex "^2.0.0"
|
filename-reserved-regex "^2.0.0"
|
||||||
strip-outer "^1.0.0"
|
strip-outer "^1.0.0"
|
||||||
@@ -3670,6 +3697,12 @@ find-up@^2.0.0, find-up@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
locate-path "^2.0.0"
|
locate-path "^2.0.0"
|
||||||
|
|
||||||
|
find-up@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||||
|
dependencies:
|
||||||
|
locate-path "^3.0.0"
|
||||||
|
|
||||||
findup-sync@~0.1.2:
|
findup-sync@~0.1.2:
|
||||||
version "0.1.3"
|
version "0.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.1.3.tgz#7f3e7a97b82392c653bf06589bd85190e93c3683"
|
resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.1.3.tgz#7f3e7a97b82392c653bf06589bd85190e93c3683"
|
||||||
@@ -3915,6 +3948,10 @@ get-stdin@^5.0.1:
|
|||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
|
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
|
||||||
|
|
||||||
|
get-stdin@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
|
||||||
|
|
||||||
get-stream@^3.0.0:
|
get-stream@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||||
@@ -4452,6 +4489,21 @@ humanize-plus@^1.8.1:
|
|||||||
version "1.8.2"
|
version "1.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030"
|
resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030"
|
||||||
|
|
||||||
|
husky@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/husky/-/husky-1.1.0.tgz#7271e85f5d98b54349788839b720c9a60cd95dba"
|
||||||
|
dependencies:
|
||||||
|
cosmiconfig "^5.0.6"
|
||||||
|
execa "^0.9.0"
|
||||||
|
find-up "^3.0.0"
|
||||||
|
get-stdin "^6.0.0"
|
||||||
|
is-ci "^1.2.1"
|
||||||
|
pkg-dir "^3.0.0"
|
||||||
|
please-upgrade-node "^3.1.1"
|
||||||
|
read-pkg "^4.0.1"
|
||||||
|
run-node "^1.0.0"
|
||||||
|
slash "^2.0.0"
|
||||||
|
|
||||||
i18n-2@^0.7.2:
|
i18n-2@^0.7.2:
|
||||||
version "0.7.2"
|
version "0.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/i18n-2/-/i18n-2-0.7.2.tgz#7efb1a7adc67869adea0688951577464aa793aaf"
|
resolved "https://registry.yarnpkg.com/i18n-2/-/i18n-2-0.7.2.tgz#7efb1a7adc67869adea0688951577464aa793aaf"
|
||||||
@@ -4662,6 +4714,12 @@ is-ci@^1.0.10, is-ci@^1.0.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ci-info "^1.0.0"
|
ci-info "^1.0.0"
|
||||||
|
|
||||||
|
is-ci@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
|
||||||
|
dependencies:
|
||||||
|
ci-info "^1.5.0"
|
||||||
|
|
||||||
is-data-descriptor@^0.1.4:
|
is-data-descriptor@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
||||||
@@ -4694,6 +4752,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2:
|
|||||||
is-data-descriptor "^1.0.0"
|
is-data-descriptor "^1.0.0"
|
||||||
kind-of "^6.0.2"
|
kind-of "^6.0.2"
|
||||||
|
|
||||||
|
is-directory@^0.3.1:
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
|
||||||
|
|
||||||
is-dotfile@^1.0.0:
|
is-dotfile@^1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
|
resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
|
||||||
@@ -5324,6 +5386,10 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
|
|||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||||
|
|
||||||
|
"js-tokens@^3.0.0 || ^4.0.0":
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
|
|
||||||
js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0:
|
js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0:
|
||||||
version "3.11.0"
|
version "3.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
|
||||||
@@ -5331,7 +5397,7 @@ js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0:
|
|||||||
argparse "^1.0.7"
|
argparse "^1.0.7"
|
||||||
esprima "^4.0.0"
|
esprima "^4.0.0"
|
||||||
|
|
||||||
js-yaml@^3.12.0, js-yaml@^3.8.1:
|
js-yaml@^3.12.0, js-yaml@^3.8.1, js-yaml@^3.9.0:
|
||||||
version "3.12.0"
|
version "3.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
|
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5688,6 +5754,13 @@ locate-path@^2.0.0:
|
|||||||
p-locate "^2.0.0"
|
p-locate "^2.0.0"
|
||||||
path-exists "^3.0.0"
|
path-exists "^3.0.0"
|
||||||
|
|
||||||
|
locate-path@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
||||||
|
dependencies:
|
||||||
|
p-locate "^3.0.0"
|
||||||
|
path-exists "^3.0.0"
|
||||||
|
|
||||||
lodash-es@^4.2.1:
|
lodash-es@^4.2.1:
|
||||||
version "4.17.10"
|
version "4.17.10"
|
||||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05"
|
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05"
|
||||||
@@ -5805,6 +5878,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
|
|||||||
dependencies:
|
dependencies:
|
||||||
js-tokens "^3.0.0"
|
js-tokens "^3.0.0"
|
||||||
|
|
||||||
|
loose-envify@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
|
dependencies:
|
||||||
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
loud-rejection@^1.0.0, loud-rejection@^1.2.0:
|
loud-rejection@^1.0.0, loud-rejection@^1.2.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
|
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
|
||||||
@@ -6681,16 +6760,32 @@ p-limit@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-try "^1.0.0"
|
p-try "^1.0.0"
|
||||||
|
|
||||||
|
p-limit@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec"
|
||||||
|
dependencies:
|
||||||
|
p-try "^2.0.0"
|
||||||
|
|
||||||
p-locate@^2.0.0:
|
p-locate@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit "^1.1.0"
|
p-limit "^1.1.0"
|
||||||
|
|
||||||
|
p-locate@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
|
||||||
|
dependencies:
|
||||||
|
p-limit "^2.0.0"
|
||||||
|
|
||||||
p-try@^1.0.0:
|
p-try@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
||||||
|
|
||||||
|
p-try@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
|
||||||
|
|
||||||
package-hash@^1.2.0:
|
package-hash@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44"
|
resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44"
|
||||||
@@ -6886,12 +6981,24 @@ pkg-dir@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up "^2.1.0"
|
find-up "^2.1.0"
|
||||||
|
|
||||||
|
pkg-dir@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
|
||||||
|
dependencies:
|
||||||
|
find-up "^3.0.0"
|
||||||
|
|
||||||
pkg-up@^2.0.0:
|
pkg-up@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
|
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
|
||||||
dependencies:
|
dependencies:
|
||||||
find-up "^2.1.0"
|
find-up "^2.1.0"
|
||||||
|
|
||||||
|
please-upgrade-node@^3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac"
|
||||||
|
dependencies:
|
||||||
|
semver-compare "^1.0.0"
|
||||||
|
|
||||||
plist@^2.0.0, plist@^2.1.0:
|
plist@^2.0.0, plist@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025"
|
resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025"
|
||||||
@@ -7241,6 +7348,13 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8,
|
|||||||
loose-envify "^1.3.1"
|
loose-envify "^1.3.1"
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
prop-types@^15.6.2:
|
||||||
|
version "15.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
proxy-addr@~2.0.3:
|
proxy-addr@~2.0.3:
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
|
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
|
||||||
@@ -7420,6 +7534,10 @@ react-dom@^15.0.2:
|
|||||||
object-assign "^4.1.0"
|
object-assign "^4.1.0"
|
||||||
prop-types "^15.5.10"
|
prop-types "^15.5.10"
|
||||||
|
|
||||||
|
react-image-carousel@^2.0.18:
|
||||||
|
version "2.0.18"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-image-carousel/-/react-image-carousel-2.0.18.tgz#5868ea09bd9cca09c4467d3d02695cd4e7792f28"
|
||||||
|
|
||||||
react-input-autosize@^1.1.0:
|
react-input-autosize@^1.1.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.2.0.tgz#87241071159f742123897691da6796ec33b57d05"
|
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.2.0.tgz#87241071159f742123897691da6796ec33b57d05"
|
||||||
@@ -7427,6 +7545,10 @@ react-input-autosize@^1.1.0:
|
|||||||
create-react-class "^15.5.2"
|
create-react-class "^15.5.2"
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
|
react-lifecycles-compat@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
|
|
||||||
react-proxy@^1.1.7:
|
react-proxy@^1.1.7:
|
||||||
version "1.1.8"
|
version "1.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
|
resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
|
||||||
@@ -7492,6 +7614,15 @@ react-transform-hmr@^1.0.3:
|
|||||||
global "^4.3.0"
|
global "^4.3.0"
|
||||||
react-proxy "^1.1.7"
|
react-proxy "^1.1.7"
|
||||||
|
|
||||||
|
react-transition-group@^2.5.0:
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874"
|
||||||
|
dependencies:
|
||||||
|
dom-helpers "^3.3.1"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
react-lifecycles-compat "^3.0.4"
|
||||||
|
|
||||||
react@^15.5.4:
|
react@^15.5.4:
|
||||||
version "15.6.2"
|
version "15.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"
|
||||||
@@ -7545,6 +7676,14 @@ read-pkg@^2.0.0:
|
|||||||
normalize-package-data "^2.3.2"
|
normalize-package-data "^2.3.2"
|
||||||
path-type "^2.0.0"
|
path-type "^2.0.0"
|
||||||
|
|
||||||
|
read-pkg@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237"
|
||||||
|
dependencies:
|
||||||
|
normalize-package-data "^2.3.2"
|
||||||
|
parse-json "^4.0.0"
|
||||||
|
pify "^3.0.0"
|
||||||
|
|
||||||
readable-stream@^1.1.8, readable-stream@~1.1.9:
|
readable-stream@^1.1.8, readable-stream@~1.1.9:
|
||||||
version "1.1.14"
|
version "1.1.14"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||||
@@ -7895,6 +8034,10 @@ run-async@^0.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
|
|
||||||
|
run-node@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e"
|
||||||
|
|
||||||
run-parallel@^1.1.2:
|
run-parallel@^1.1.2:
|
||||||
version "1.1.9"
|
version "1.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
|
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
|
||||||
@@ -7994,6 +8137,10 @@ section-iterator@^2.0.0:
|
|||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a"
|
resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a"
|
||||||
|
|
||||||
|
semver-compare@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||||
|
|
||||||
semver-diff@^2.0.0:
|
semver-diff@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
|
||||||
@@ -8153,6 +8300,10 @@ slash@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
|
||||||
|
|
||||||
|
slash@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
|
||||||
|
|
||||||
slice-ansi@0.0.4:
|
slice-ansi@0.0.4:
|
||||||
version "0.0.4"
|
version "0.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
|
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
|
||||||
|
|||||||
Reference in New Issue
Block a user