mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
Merge branch 'master' into fix-issue#2644-and-#2662
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
|
||||
root = true
|
||||
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -17,7 +17,7 @@
|
||||
"${workspaceFolder}/index.js"
|
||||
],
|
||||
"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
|
||||
@@ -38,6 +38,7 @@ export default class CodeEditor extends React.Component {
|
||||
trailing: true
|
||||
})
|
||||
this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject)
|
||||
this.highlightHandler = (editor, changeObject) => this.handleHighlight(editor, changeObject)
|
||||
this.focusHandler = () => {
|
||||
ipcRenderer.send('editor:focused', true)
|
||||
}
|
||||
@@ -177,6 +178,9 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
},
|
||||
'Cmd-Left': function (cm) {
|
||||
cm.execCommand('goLineLeft')
|
||||
},
|
||||
'Cmd-T': function (cm) {
|
||||
// Do nothing
|
||||
},
|
||||
@@ -235,6 +239,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor = CodeMirror(this.refs.root, {
|
||||
rulers: buildCMRulers(rulers, enableRulers),
|
||||
value: this.props.value,
|
||||
linesHighlighted: this.props.linesHighlighted,
|
||||
lineNumbers: this.props.displayLineNumbers,
|
||||
lineWrapping: true,
|
||||
theme: this.props.theme,
|
||||
@@ -261,6 +266,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.on('focus', this.focusHandler)
|
||||
this.editor.on('blur', this.blurHandler)
|
||||
this.editor.on('change', this.changeHandler)
|
||||
this.editor.on('gutterClick', this.highlightHandler)
|
||||
this.editor.on('paste', this.pasteHandler)
|
||||
if (this.props.switchPreview !== 'RIGHTCLICK') {
|
||||
this.editor.on('contextmenu', this.contextMenuHandler)
|
||||
@@ -339,6 +345,8 @@ export default class CodeEditor extends React.Component {
|
||||
this.setState({
|
||||
clientWidth: this.refs.root.clientWidth
|
||||
})
|
||||
|
||||
this.initialHighlighting()
|
||||
}
|
||||
|
||||
expandSnippet (line, cursor, cm, snippets) {
|
||||
@@ -537,12 +545,96 @@ export default class CodeEditor extends React.Component {
|
||||
|
||||
handleChange (editor, changeObject) {
|
||||
spellcheck.handleChange(editor, changeObject)
|
||||
|
||||
this.updateHighlight(editor, changeObject)
|
||||
|
||||
this.value = editor.getValue()
|
||||
if (this.props.onChange) {
|
||||
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) {}
|
||||
|
||||
scrollToLine (event, num) {
|
||||
@@ -567,6 +659,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.value = this.props.value
|
||||
this.editor.setValue(this.props.value)
|
||||
this.editor.clearHistory()
|
||||
this.restartHighlighting()
|
||||
this.editor.on('change', this.changeHandler)
|
||||
this.editor.refresh()
|
||||
}
|
||||
@@ -758,6 +851,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) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
|
||||
@@ -7,6 +7,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import { findStorage } from 'browser/lib/findStorage'
|
||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
||||
|
||||
class MarkdownEditor extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -221,6 +222,28 @@ class MarkdownEditor extends React.Component {
|
||||
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) {
|
||||
const keyPressed = this.state.keyPressed
|
||||
keyPressed.delete(e.keyCode)
|
||||
@@ -232,7 +255,7 @@ class MarkdownEditor extends React.Component {
|
||||
}
|
||||
|
||||
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)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||
@@ -275,6 +298,7 @@ class MarkdownEditor extends React.Component {
|
||||
noteKey={noteKey}
|
||||
fetchUrlTitle={config.editor.fetchUrlTitle}
|
||||
enableTableEditor={config.editor.enableTableEditor}
|
||||
linesHighlighted={linesHighlighted}
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
onBlur={(e) => this.handleBlur(e)}
|
||||
spellCheck={config.editor.spellcheck}
|
||||
@@ -314,6 +338,7 @@ class MarkdownEditor extends React.Component {
|
||||
customCSS={config.preview.customCSS}
|
||||
allowCustomCSS={config.preview.allowCustomCSS}
|
||||
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
||||
onDrop={(e) => this.handleDropImage(e)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -21,6 +21,8 @@ import yaml from 'js-yaml'
|
||||
import context from 'browser/lib/context'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import fs from 'fs'
|
||||
import { render } from 'react-dom'
|
||||
import Carousel from 'react-image-carousel'
|
||||
import ConfigManager from '../main/lib/ConfigManager'
|
||||
|
||||
const { remote, shell } = require('electron')
|
||||
@@ -40,7 +42,8 @@ const appPath = fileUrl(
|
||||
)
|
||||
const CSS_FILES = [
|
||||
`${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 (
|
||||
@@ -207,7 +210,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
|
||||
this.printHandler = () => this.handlePrint()
|
||||
|
||||
this.linkClickHandler = this.handlelinkClick.bind(this)
|
||||
this.linkClickHandler = this.handleLinkClick.bind(this)
|
||||
this.initMarkdown = this.initMarkdown.bind(this)
|
||||
this.initMarkdown()
|
||||
}
|
||||
@@ -410,6 +413,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { onDrop } = this.props
|
||||
|
||||
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
||||
this.refs.root.contentWindow.document.body.addEventListener(
|
||||
'contextmenu',
|
||||
@@ -447,7 +452,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
)
|
||||
this.refs.root.contentWindow.document.addEventListener(
|
||||
'drop',
|
||||
this.preventImageDroppedHandler
|
||||
onDrop || this.preventImageDroppedHandler
|
||||
)
|
||||
this.refs.root.contentWindow.document.addEventListener(
|
||||
'dragover',
|
||||
@@ -464,6 +469,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
const { onDrop } = this.props
|
||||
|
||||
this.refs.root.contentWindow.document.body.removeEventListener(
|
||||
'contextmenu',
|
||||
this.contextMenuHandler
|
||||
@@ -482,7 +489,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
)
|
||||
this.refs.root.contentWindow.document.removeEventListener(
|
||||
'drop',
|
||||
this.preventImageDroppedHandler
|
||||
onDrop || this.preventImageDroppedHandler
|
||||
)
|
||||
this.refs.root.contentWindow.document.removeEventListener(
|
||||
'dragover',
|
||||
@@ -772,6 +779,34 @@ export default class MarkdownPreview extends React.Component {
|
||||
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 () {
|
||||
@@ -814,7 +849,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
return new window.Notification(title, options)
|
||||
}
|
||||
|
||||
handlelinkClick (e) {
|
||||
handleLinkClick (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ class MarkdownSplitEditor extends React.Component {
|
||||
this.refs.code.setValue(value)
|
||||
}
|
||||
|
||||
handleOnChange () {
|
||||
handleOnChange (e) {
|
||||
this.value = this.refs.code.value
|
||||
this.props.onChange()
|
||||
this.props.onChange(e)
|
||||
}
|
||||
|
||||
handleScroll (e) {
|
||||
@@ -136,7 +136,7 @@ class MarkdownSplitEditor extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {config, value, storageKey, noteKey} = this.props
|
||||
const {config, value, storageKey, noteKey, linesHighlighted} = this.props
|
||||
const storage = findStorage(storageKey)
|
||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||
@@ -169,7 +169,8 @@ class MarkdownSplitEditor extends React.Component {
|
||||
enableTableEditor={config.editor.enableTableEditor}
|
||||
storageKey={storageKey}
|
||||
noteKey={noteKey}
|
||||
onChange={this.handleOnChange.bind(this)}
|
||||
linesHighlighted={linesHighlighted}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
onScroll={this.handleScroll.bind(this)}
|
||||
spellCheck={config.editor.spellcheck}
|
||||
enableSmartPaste={config.editor.enableSmartPaste}
|
||||
|
||||
@@ -3,19 +3,30 @@
|
||||
flex 1
|
||||
min-width 70px
|
||||
overflow hidden
|
||||
border-left 1px solid $ui-borderColor
|
||||
border-top 1px solid $ui-borderColor
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
.deleteButton
|
||||
color $ui-inactive-text-color
|
||||
&:hover
|
||||
background-color darken($ui-backgroundColor, 15%)
|
||||
&:active
|
||||
color white
|
||||
background-color $ui-active-color
|
||||
color: $ui-text-color
|
||||
visibility visible
|
||||
transition 0.15s
|
||||
.button
|
||||
color: $ui-text-color
|
||||
transition 0.15s
|
||||
|
||||
.root--active
|
||||
@extend .root
|
||||
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
|
||||
width 100%
|
||||
@@ -27,8 +38,7 @@
|
||||
background-color transparent
|
||||
transition 0.15s
|
||||
border-left 4px solid transparent
|
||||
&:hover
|
||||
background-color $ui-button--hover-backgroundColor
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.deleteButton
|
||||
position absolute
|
||||
@@ -42,6 +52,7 @@
|
||||
color $ui-inactive-text-color
|
||||
background-color transparent
|
||||
border-radius 2px
|
||||
visibility hidden
|
||||
|
||||
.input
|
||||
height 29px
|
||||
@@ -50,76 +61,66 @@
|
||||
width 100%
|
||||
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"]
|
||||
.root
|
||||
color $ui-dark-text-color
|
||||
border-color $ui-dark-borderColor
|
||||
border-top 1px solid $ui-dark-borderColor
|
||||
&: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
|
||||
color $ui-dark-inactive-text-color
|
||||
&:hover
|
||||
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
||||
&:active
|
||||
color $ui-dark-text-color
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
color $ui-dark-text-color
|
||||
transition 0.15s
|
||||
|
||||
.root--active
|
||||
color $ui-dark-text-color
|
||||
border-color $ui-dark-borderColor
|
||||
&:hover
|
||||
background-color $ui-dark-button--hover-backgroundColor
|
||||
.deleteButton
|
||||
color $ui-dark-inactive-text-color
|
||||
&:hover
|
||||
background-color darken($ui-dark-button--hover-backgroundColor, 15%)
|
||||
&:active
|
||||
color $ui-dark-text-color
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
border-left 1px solid $ui-dark-borderColor
|
||||
border-top 1px solid $ui-dark-borderColor
|
||||
.button
|
||||
color $ui-dark-text-color
|
||||
.deleteButton
|
||||
color $ui-dark-text-color
|
||||
|
||||
.button
|
||||
border none
|
||||
color $ui-dark-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
&:hover
|
||||
color $ui-dark-text-color
|
||||
background-color $ui-dark-button--hover-backgroundColor
|
||||
|
||||
.input
|
||||
background-color $ui-dark-button--hover-backgroundColor
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
color $ui-dark-text-color
|
||||
|
||||
.deleteButton
|
||||
color alpha($ui-dark-text-color, 30%)
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
.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
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
transition 0.15s
|
||||
.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
|
||||
color $ui-solarized-dark-button--active-color
|
||||
transition 0.15s
|
||||
.button
|
||||
color $ui-solarized-dark-button--active-color
|
||||
transition 0.15s
|
||||
|
||||
.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
|
||||
border none
|
||||
@@ -127,101 +128,75 @@ body[data-theme="solarized-dark"]
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
&:hover
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
|
||||
.input
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
|
||||
.deleteButton
|
||||
color alpha($ui-solarized-dark-text-color, 30%)
|
||||
color $ui-solarized-dark-button--active-color
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.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
|
||||
&:hover
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
transition 0.15s
|
||||
.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
|
||||
transition 0.15s
|
||||
.button
|
||||
color $ui-monokai-text-color
|
||||
transition 0.15s
|
||||
|
||||
.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
|
||||
border none
|
||||
color $ui-monokai-text-color
|
||||
color $ui-inactive-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
&:hover
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
|
||||
.input
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
|
||||
.deleteButton
|
||||
color alpha($ui-monokai-text-color, 30%)
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.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
|
||||
&:hover
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
transition 0.15s
|
||||
.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
|
||||
transition 0.15s
|
||||
.button
|
||||
color $ui-dracula-text-color
|
||||
transition 0.15s
|
||||
|
||||
.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
|
||||
border none
|
||||
color $ui-dracula-text-color
|
||||
color $ui-inactive-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
&:hover
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
|
||||
.input
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.deleteButton
|
||||
color alpha($ui-dracula-text-color, 30%)
|
||||
color $ui-dracula-text-color
|
||||
@@ -55,11 +55,12 @@ body
|
||||
line-height 1.6
|
||||
overflow-x hidden
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
// do not allow display line breaks
|
||||
.katex-display > .katex
|
||||
white-space nowrap
|
||||
// allow inline line breaks
|
||||
.katex
|
||||
font 400 1.2em 'KaTeX_Main'
|
||||
line-height 1.2em
|
||||
white-space initial
|
||||
text-indent 0
|
||||
.katex .mfrac>.vlist>span:nth-child(2)
|
||||
top 0 !important
|
||||
.katex-error
|
||||
@@ -183,6 +184,10 @@ ul
|
||||
display list-item
|
||||
&.taskListItem
|
||||
list-style none
|
||||
&>input
|
||||
margin-left -1.6em
|
||||
&>p
|
||||
margin-left -1.8em
|
||||
p
|
||||
margin 0
|
||||
&>li>ul, &>li>ol
|
||||
@@ -416,6 +421,26 @@ pre.fence
|
||||
canvas, svg
|
||||
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%)
|
||||
themeDarkText = #f9f9f9
|
||||
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
||||
@@ -475,6 +500,14 @@ body[data-theme="dark"]
|
||||
border-color themeDarkBorder
|
||||
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
|
||||
themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%)
|
||||
themeSolarizedDarkTableHead = themeSolarizedDarkTableEven
|
||||
@@ -510,6 +543,14 @@ body[data-theme="solarized-dark"]
|
||||
border-color themeDarkBorder
|
||||
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
|
||||
themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%)
|
||||
themeMonokaiTableHead = themeMonokaiTableEven
|
||||
@@ -538,6 +579,7 @@ body[data-theme="monokai"]
|
||||
border-right solid 1px themeMonokaiTableBorder
|
||||
kbd
|
||||
background-color themeDarkBackground
|
||||
|
||||
dl
|
||||
border-color themeDarkBorder
|
||||
background-color themeMonokaiTableHead
|
||||
@@ -547,6 +589,14 @@ body[data-theme="monokai"]
|
||||
border-color themeDarkBorder
|
||||
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
|
||||
themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%)
|
||||
themeDraculaTableHead = themeDraculaTableEven
|
||||
@@ -575,6 +625,7 @@ body[data-theme="dracula"]
|
||||
border-right solid 1px themeDraculaTableBorder
|
||||
kbd
|
||||
background-color themeDarkBackground
|
||||
|
||||
dl
|
||||
border-color themeDarkBorder
|
||||
background-color themeDraculaTableHead
|
||||
@@ -583,3 +634,11 @@ body[data-theme="dracula"]
|
||||
dd
|
||||
border-color themeDarkBorder
|
||||
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
|
||||
*/
|
||||
|
||||
import { EOL } from 'os'
|
||||
import toc from 'markdown-toc'
|
||||
import diacritics from 'diacritics-map'
|
||||
import stripColor from 'strip-color'
|
||||
import mdlink from 'markdown-link'
|
||||
import slugify from './slugify'
|
||||
|
||||
const EOL = require('os').EOL
|
||||
const hasProp = Object.prototype.hasOwnProperty
|
||||
|
||||
/**
|
||||
* @caseSensitiveSlugify Custom slugify function
|
||||
* 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
|
||||
* From @enyaxu/markdown-it-anchor
|
||||
*/
|
||||
function caseSensitiveSlugify (str) {
|
||||
function replaceDiacritics (str) {
|
||||
return str.replace(/[À-ž]/g, function (ch) {
|
||||
return diacritics[ch] || ch
|
||||
})
|
||||
}
|
||||
|
||||
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 uniqueSlug (slug, slugs, opts) {
|
||||
let uniq = slug
|
||||
let i = opts.uniqueSlugStartIndex
|
||||
while (hasProp.call(slugs, uniq)) uniq = `${slug}-${i++}`
|
||||
slugs[uniq] = true
|
||||
return uniq
|
||||
}
|
||||
|
||||
function linkify (tok, text, slug, opts) {
|
||||
var uniqeID = opts.num === 0 ? '' : '-' + opts.num
|
||||
tok.content = mdlink(text, '#' + slug + uniqeID)
|
||||
return tok
|
||||
function linkify (token) {
|
||||
token.content = mdlink(token.content, '#' + token.slug)
|
||||
return token
|
||||
}
|
||||
|
||||
const TOC_MARKER_START = '<!-- toc -->'
|
||||
@@ -91,8 +67,23 @@ export function generateInEditor (editor) {
|
||||
* @returns generatedTOC String containing generated TOC
|
||||
*/
|
||||
export function generate (markdownText) {
|
||||
const generatedToc = toc(markdownText, {slugify: caseSensitiveSlugify, linkify: linkify})
|
||||
return TOC_MARKER_START + EOL + EOL + generatedToc.content + EOL + EOL + TOC_MARKER_END
|
||||
const slugs = {}
|
||||
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) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import _ from 'lodash'
|
||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
import katex from 'katex'
|
||||
import { lastFindInArray } from './utils'
|
||||
import anchor from '@enyaxu/markdown-it-anchor'
|
||||
|
||||
function createGutter (str, firstLineNumber) {
|
||||
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-footnote'))
|
||||
this.md.use(require('markdown-it-multimd-table'))
|
||||
this.md.use(anchor, {
|
||||
slugify: (title) => {
|
||||
var slug = encodeURI(title.trim()
|
||||
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
|
||||
.replace(/\s+/g, '-'))
|
||||
.replace(/\-+$/, '')
|
||||
return slug
|
||||
}
|
||||
this.md.use(require('@enyaxu/markdown-it-anchor'), {
|
||||
slugify: require('./slugify')
|
||||
})
|
||||
this.md.use(require('markdown-it-kbd'))
|
||||
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>
|
||||
</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 => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
|
||||
@@ -18,7 +18,8 @@ export function createMarkdownNote (storage, folder, dispatch, location, params,
|
||||
folder: folder,
|
||||
title: '',
|
||||
tags,
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
})
|
||||
.then(note => {
|
||||
const noteHash = note.key
|
||||
@@ -56,7 +57,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params,
|
||||
{
|
||||
name: '',
|
||||
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(/\-+$/, '')
|
||||
}
|
||||
@@ -39,12 +39,14 @@ class MarkdownNoteDetail extends React.Component {
|
||||
isMovingNote: false,
|
||||
note: Object.assign({
|
||||
title: '',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}, props.note),
|
||||
isLockButtonShown: false,
|
||||
isLocked: false,
|
||||
editorType: props.config.editor.type
|
||||
}
|
||||
|
||||
this.dispatchTimer = null
|
||||
|
||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||
@@ -71,7 +73,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
|
||||
if (this.saveQueue != null) this.saveNow()
|
||||
this.setState({
|
||||
note: Object.assign({}, nextProps.note)
|
||||
note: Object.assign({linesHighlighted: []}, nextProps.note)
|
||||
}, () => {
|
||||
this.refs.content.reload()
|
||||
if (this.refs.tags) this.refs.tags.reset()
|
||||
@@ -361,6 +363,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
value={note.content}
|
||||
storageKey={note.storage}
|
||||
noteKey={note.key}
|
||||
linesHighlighted={note.linesHighlighted}
|
||||
onChange={this.handleUpdateContent.bind(this)}
|
||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||
/>
|
||||
@@ -371,6 +374,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
value={note.content}
|
||||
storageKey={note.storage}
|
||||
noteKey={note.key}
|
||||
linesHighlighted={note.linesHighlighted}
|
||||
onChange={this.handleUpdateContent.bind(this)}
|
||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||
/>
|
||||
|
||||
@@ -48,7 +48,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
note: Object.assign({
|
||||
description: ''
|
||||
}, 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({
|
||||
description: ''
|
||||
}, nextProps.note, {
|
||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
|
||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet))
|
||||
})
|
||||
|
||||
this.setState({
|
||||
snippetIndex: 0,
|
||||
note: nextNote
|
||||
@@ -410,6 +411,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
return (e) => {
|
||||
const snippets = this.state.note.snippets.slice()
|
||||
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: state.note
|
||||
@@ -602,7 +605,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
note.snippets = note.snippets.concat([{
|
||||
name: '',
|
||||
mode: config.editor.snippetDefaultLanguage || 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}])
|
||||
const snippetIndex = note.snippets.length - 1
|
||||
|
||||
@@ -692,10 +696,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
const viewList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
|
||||
let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode))
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
|
||||
return <div styleName='tabView'
|
||||
key={index}
|
||||
style={{zIndex: isActive ? 5 : 4}}
|
||||
@@ -704,6 +706,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
? <MarkdownEditor styleName='tabView-content'
|
||||
value={snippet.content}
|
||||
config={config}
|
||||
linesHighlighted={snippet.linesHighlighted}
|
||||
onChange={(e) => this.handleCodeChange(index)(e)}
|
||||
ref={'code-' + index}
|
||||
ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents}
|
||||
@@ -712,6 +715,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
: <CodeEditor styleName='tabView-content'
|
||||
mode={snippet.mode}
|
||||
value={snippet.content}
|
||||
linesHighlighted={snippet.linesHighlighted}
|
||||
theme={config.editor.theme}
|
||||
fontFamily={config.editor.fontFamily}
|
||||
fontSize={editorFontSize}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
.tabList
|
||||
absolute left right
|
||||
top 55px
|
||||
top 70px
|
||||
height 30px
|
||||
display flex
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
@@ -57,6 +57,9 @@
|
||||
.tabList .tabButton
|
||||
navWhiteButtonColor()
|
||||
width 30px
|
||||
border-left 1px solid $ui-borderColor
|
||||
border-top 1px solid $ui-borderColor
|
||||
border-right 1px solid $ui-borderColor
|
||||
|
||||
.tabView
|
||||
absolute left right bottom
|
||||
@@ -98,17 +101,34 @@
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
|
||||
body[data-theme="white"]
|
||||
body[data-theme="white"], body[data-theme="default"]
|
||||
.root
|
||||
box-shadow $note-detail-box-shadow
|
||||
border none
|
||||
|
||||
.tabButton
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
color $ui-text-color
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
border-left 1px solid $ui-dark-borderColor
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
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
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
|
||||
@@ -118,7 +138,6 @@ body[data-theme="dark"]
|
||||
border 1px solid $ui-dark-borderColor
|
||||
|
||||
.tabList
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
|
||||
.tabList .list
|
||||
@@ -150,6 +169,15 @@ body[data-theme="solarized-dark"]
|
||||
color $ui-solarized-dark-text-color
|
||||
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
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
@@ -167,6 +195,14 @@ body[data-theme="monokai"]
|
||||
color $ui-monokai-text-color
|
||||
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
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
@@ -184,6 +220,14 @@ body[data-theme="dracula"]
|
||||
color $ui-dracula-text-color
|
||||
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
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
@@ -96,12 +96,14 @@ class Main extends React.Component {
|
||||
{
|
||||
name: 'example.html',
|
||||
mode: 'html',
|
||||
content: "<html>\n<body>\n<h1 id='hello'>Enjoy Boostnote!</h1>\n</body>\n</html>"
|
||||
content: "<html>\n<body>\n<h1 id='hello'>Enjoy Boostnote!</h1>\n</body>\n</html>",
|
||||
linesHighlighted: []
|
||||
},
|
||||
{
|
||||
name: 'example.js',
|
||||
mode: 'javascript',
|
||||
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)"
|
||||
content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)",
|
||||
linesHighlighted: []
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -234,8 +236,8 @@ class Main extends React.Component {
|
||||
if (this.state.isRightSliderFocused) {
|
||||
const offset = this.refs.body.getBoundingClientRect().left
|
||||
let newListWidth = e.pageX - offset
|
||||
if (newListWidth < 10) {
|
||||
newListWidth = 10
|
||||
if (newListWidth < 180) {
|
||||
newListWidth = 180
|
||||
} else if (newListWidth > 600) {
|
||||
newListWidth = 600
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import debounceRender from 'react-debounce-render'
|
||||
import styles from './NoteList.styl'
|
||||
import moment from 'moment'
|
||||
import _ from 'lodash'
|
||||
@@ -711,7 +710,8 @@ class NoteList extends React.Component {
|
||||
type: firstNote.type,
|
||||
folder: folder.key,
|
||||
title: firstNote.title + ' ' + i18n.__('copy'),
|
||||
content: firstNote.content
|
||||
content: firstNote.content,
|
||||
linesHighlighted: firstNote.linesHighlighted
|
||||
})
|
||||
.then((note) => {
|
||||
attachmentManagement.cloneAttachments(firstNote, note)
|
||||
@@ -1129,4 +1129,4 @@ NoteList.propTypes = {
|
||||
})
|
||||
}
|
||||
|
||||
export default debounceRender(CSSModules(NoteList, styles))
|
||||
export default CSSModules(NoteList, styles)
|
||||
|
||||
@@ -6,6 +6,7 @@ import _ from 'lodash'
|
||||
import ee from 'browser/main/lib/eventEmitter'
|
||||
import NewNoteButton from 'browser/main/NewNoteButton'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import debounce from 'lodash/debounce'
|
||||
|
||||
class TopBar extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -25,6 +26,10 @@ class TopBar extends React.Component {
|
||||
}
|
||||
|
||||
this.codeInitHandler = this.handleCodeInit.bind(this)
|
||||
|
||||
this.updateKeyword = debounce(this.updateKeyword, 1000 / 60, {
|
||||
maxWait: 1000 / 8
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -94,7 +99,6 @@ class TopBar extends React.Component {
|
||||
}
|
||||
|
||||
handleKeyUp (e) {
|
||||
const { router } = this.context
|
||||
// reset states
|
||||
this.setState({
|
||||
isConfirmTranslation: false
|
||||
@@ -106,21 +110,21 @@ class TopBar extends React.Component {
|
||||
isConfirmTranslation: true
|
||||
})
|
||||
const keyword = this.refs.searchInput.value
|
||||
router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||
this.setState({
|
||||
search: keyword
|
||||
})
|
||||
this.updateKeyword(keyword)
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchChange (e) {
|
||||
const { router } = this.context
|
||||
const keyword = this.refs.searchInput.value
|
||||
if (this.state.isAlphabet || this.state.isConfirmTranslation) {
|
||||
router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||
const keyword = this.refs.searchInput.value
|
||||
this.updateKeyword(keyword)
|
||||
} else {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
updateKeyword (keyword) {
|
||||
this.context.router.push(`/searched/${encodeURIComponent(keyword)}`)
|
||||
this.setState({
|
||||
search: keyword
|
||||
})
|
||||
|
||||
@@ -253,7 +253,15 @@ function escapeHtmlCharactersInCodeTag (splitWithCodeTag) {
|
||||
* @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
|
||||
*/
|
||||
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')
|
||||
return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ function validateInput (input) {
|
||||
switch (input.type) {
|
||||
case 'MARKDOWN_NOTE':
|
||||
if (!_.isString(input.content)) input.content = ''
|
||||
if (!_.isArray(input.linesHighlighted)) input.linesHighlighted = []
|
||||
break
|
||||
case 'SNIPPET_NOTE':
|
||||
if (!_.isString(input.description)) input.description = ''
|
||||
@@ -23,7 +24,8 @@ function validateInput (input) {
|
||||
input.snippets = [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}]
|
||||
}
|
||||
break
|
||||
|
||||
@@ -9,7 +9,8 @@ function createSnippet (snippetFile) {
|
||||
id: crypto.randomBytes(16).toString('hex'),
|
||||
name: 'Unnamed snippet',
|
||||
prefix: [],
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}
|
||||
fetchSnippet(null, snippetFile).then((snippets) => {
|
||||
snippets.push(newSnippet)
|
||||
|
||||
@@ -69,7 +69,8 @@ function importAll (storage, data) {
|
||||
isStarred: false,
|
||||
title: article.title,
|
||||
content: '# ' + article.title + '\n\n' + article.content,
|
||||
key: noteKey
|
||||
key: noteKey,
|
||||
linesHighlighted: article.linesHighlighted
|
||||
}
|
||||
notes.push(newNote)
|
||||
} else {
|
||||
@@ -87,7 +88,8 @@ function importAll (storage, data) {
|
||||
snippets: [{
|
||||
name: article.mode,
|
||||
mode: article.mode,
|
||||
content: article.content
|
||||
content: article.content,
|
||||
linesHighlighted: article.linesHighlighted
|
||||
}]
|
||||
}
|
||||
notes.push(newNote)
|
||||
|
||||
@@ -39,6 +39,9 @@ function validateInput (input) {
|
||||
if (input.content != null) {
|
||||
if (!_.isString(input.content)) validatedInput.content = ''
|
||||
else validatedInput.content = input.content
|
||||
|
||||
if (!_.isArray(input.linesHighlighted)) validatedInput.linesHighlighted = []
|
||||
else validatedInput.linesHighlighted = input.linesHighlighted
|
||||
}
|
||||
return validatedInput
|
||||
case 'SNIPPET_NOTE':
|
||||
@@ -51,7 +54,8 @@ function validateInput (input) {
|
||||
validatedInput.snippets = [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}]
|
||||
} else {
|
||||
validatedInput.snippets = input.snippets
|
||||
@@ -96,12 +100,14 @@ function updateNote (storageKey, noteKey, input) {
|
||||
snippets: [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}]
|
||||
}
|
||||
: {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
content: ''
|
||||
content: '',
|
||||
linesHighlighted: []
|
||||
}
|
||||
noteData.title = ''
|
||||
if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.')
|
||||
|
||||
@@ -12,7 +12,8 @@ function updateSnippet (snippet, snippetFile) {
|
||||
if (
|
||||
currentSnippet.name === snippet.name &&
|
||||
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
|
||||
resolve(snippets)
|
||||
@@ -20,6 +21,7 @@ function updateSnippet (snippet, snippetFile) {
|
||||
currentSnippet.name = snippet.name
|
||||
currentSnippet.prefix = snippet.prefix
|
||||
currentSnippet.content = snippet.content
|
||||
currentSnippet.linesHighlighted = (snippet.linesHighlighted)
|
||||
fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => {
|
||||
if (err) reject(err)
|
||||
resolve(snippets)
|
||||
|
||||
@@ -73,6 +73,11 @@ class InfoTab extends React.Component {
|
||||
<div styleName='header--sub'>{i18n.__('Community')}</div>
|
||||
<div styleName='top'>
|
||||
<ul styleName='list'>
|
||||
<li>
|
||||
<a href='https://issuehunt.io/repos/53266139'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>{i18n.__('Bounty on IssueHunt')}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://boostnote.io/#subscribe'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
|
||||
@@ -240,10 +240,8 @@ navWhiteButtonColor()
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
transition 0.15s
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color $ui-button--active-backgroundColor
|
||||
color $ui-text-color
|
||||
transition 0.15s
|
||||
|
||||
// UI Button
|
||||
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "boost",
|
||||
"productName": "Boostnote",
|
||||
"version": "0.11.11",
|
||||
"version": "0.11.12",
|
||||
"main": "index.js",
|
||||
"description": "Boostnote",
|
||||
"license": "GPL-3.0",
|
||||
@@ -99,8 +99,10 @@
|
||||
"react-codemirror": "^0.3.0",
|
||||
"react-debounce-render": "^4.0.1",
|
||||
"react-dom": "^15.0.2",
|
||||
"react-image-carousel": "^2.0.18",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-sortable-hoc": "^0.6.7",
|
||||
"react-transition-group": "^2.5.0",
|
||||
"redux": "^3.5.2",
|
||||
"sander": "^0.5.1",
|
||||
"sanitize-html": "^1.18.2",
|
||||
@@ -142,6 +144,7 @@
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-electron-installer": "2.1.0",
|
||||
"history": "^1.17.0",
|
||||
"husky": "^1.1.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^22.4.3",
|
||||
"jest-localstorage-mock": "^2.2.0",
|
||||
@@ -189,5 +192,10 @@
|
||||
"<rootDir>/tests/jest.js",
|
||||
"jest-localstorage-mock"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,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:
|
||||
|
||||
[](https://issuehunt.io/repos/53266139)
|
||||
[](https://issuehunt.io/repos/53266139)
|
||||
|
||||
## Community
|
||||
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
|
||||
|
||||
@@ -25,13 +25,16 @@ test.serial('Create a note', (t) => {
|
||||
const storageKey = t.context.storage.cache.key
|
||||
const folderKey = t.context.storage.json.folders[0].key
|
||||
|
||||
const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10))
|
||||
|
||||
const input1 = {
|
||||
type: 'SNIPPET_NOTE',
|
||||
description: faker.lorem.lines(),
|
||||
snippets: [{
|
||||
name: faker.system.fileName(),
|
||||
mode: 'text',
|
||||
content: faker.lorem.lines()
|
||||
content: faker.lorem.lines(),
|
||||
linesHighlighted: randLinesHighlightedArray
|
||||
}],
|
||||
tags: faker.lorem.words().split(' '),
|
||||
folder: folderKey
|
||||
@@ -42,7 +45,8 @@ test.serial('Create a note', (t) => {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
content: faker.lorem.lines(),
|
||||
tags: faker.lorem.words().split(' '),
|
||||
folder: folderKey
|
||||
folder: folderKey,
|
||||
linesHighlighted: randLinesHighlightedArray
|
||||
}
|
||||
input2.title = input2.content.split('\n').shift()
|
||||
|
||||
@@ -59,6 +63,7 @@ test.serial('Create a note', (t) => {
|
||||
|
||||
t.is(storageKey, data1.storage)
|
||||
const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson'))
|
||||
|
||||
t.is(input1.title, data1.title)
|
||||
t.is(input1.title, jsonData1.title)
|
||||
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].name, data1.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)
|
||||
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.tags.length, data2.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.deepEqual(snippet.prefix, data.prefix)
|
||||
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 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 = {
|
||||
type: 'SNIPPET_NOTE',
|
||||
description: faker.lorem.lines(),
|
||||
snippets: [{
|
||||
name: faker.system.fileName(),
|
||||
mode: 'text',
|
||||
content: faker.lorem.lines()
|
||||
content: faker.lorem.lines(),
|
||||
linesHighlighted: randLinesHighlightedArray
|
||||
}],
|
||||
tags: faker.lorem.words().split(' '),
|
||||
folder: folderKey
|
||||
@@ -43,7 +47,8 @@ test.serial('Update a note', (t) => {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
content: faker.lorem.lines(),
|
||||
tags: faker.lorem.words().split(' '),
|
||||
folder: folderKey
|
||||
folder: folderKey,
|
||||
linesHighlighted: randLinesHighlightedArray
|
||||
}
|
||||
input2.title = input2.content.split('\n').shift()
|
||||
|
||||
@@ -53,7 +58,8 @@ test.serial('Update a note', (t) => {
|
||||
snippets: [{
|
||||
name: faker.system.fileName(),
|
||||
mode: 'text',
|
||||
content: faker.lorem.lines()
|
||||
content: faker.lorem.lines(),
|
||||
linesHighlighted: randLinesHighlightedArray2
|
||||
}],
|
||||
tags: faker.lorem.words().split(' ')
|
||||
}
|
||||
@@ -62,7 +68,8 @@ test.serial('Update a note', (t) => {
|
||||
const input4 = {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
content: faker.lorem.lines(),
|
||||
tags: faker.lorem.words().split(' ')
|
||||
tags: faker.lorem.words().split(' '),
|
||||
linesHighlighted: randLinesHighlightedArray2
|
||||
}
|
||||
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].name, data1.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'))
|
||||
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.tags.length, data2.tags.length)
|
||||
t.is(input4.tags.length, jsonData2.tags.length)
|
||||
t.deepEqual(input4.linesHighlighted, data2.linesHighlighted)
|
||||
t.deepEqual(input4.linesHighlighted, jsonData2.linesHighlighted)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
155
yarn.lock
155
yarn.lock
@@ -61,7 +61,6 @@
|
||||
"@enyaxu/markdown-it-anchor@^5.0.2":
|
||||
version "5.0.2"
|
||||
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":
|
||||
version "0.1.4"
|
||||
@@ -1690,6 +1689,10 @@ ci-info@^1.0.0:
|
||||
version "1.1.3"
|
||||
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:
|
||||
version "0.3.3"
|
||||
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"
|
||||
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:
|
||||
version "3.0.2"
|
||||
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:
|
||||
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:
|
||||
version "0.1.0"
|
||||
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"
|
||||
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:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
|
||||
@@ -3670,6 +3697,12 @@ find-up@^2.0.0, find-up@^2.1.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "0.1.3"
|
||||
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"
|
||||
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:
|
||||
version "3.0.0"
|
||||
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"
|
||||
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:
|
||||
version "0.7.2"
|
||||
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:
|
||||
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:
|
||||
version "0.1.4"
|
||||
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"
|
||||
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:
|
||||
version "1.0.3"
|
||||
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"
|
||||
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:
|
||||
version "3.11.0"
|
||||
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"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
|
||||
dependencies:
|
||||
@@ -5688,6 +5754,13 @@ locate-path@^2.0.0:
|
||||
p-locate "^2.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:
|
||||
version "4.17.10"
|
||||
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:
|
||||
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:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
|
||||
@@ -6681,16 +6760,32 @@ p-limit@^1.1.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
||||
dependencies:
|
||||
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:
|
||||
version "1.0.0"
|
||||
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:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44"
|
||||
@@ -6886,12 +6981,24 @@ pkg-dir@^2.0.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
|
||||
dependencies:
|
||||
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:
|
||||
version "2.1.0"
|
||||
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"
|
||||
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:
|
||||
version "2.0.3"
|
||||
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"
|
||||
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:
|
||||
version "1.2.0"
|
||||
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"
|
||||
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:
|
||||
version "1.1.8"
|
||||
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"
|
||||
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:
|
||||
version "15.6.2"
|
||||
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"
|
||||
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:
|
||||
version "1.1.14"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
|
||||
@@ -7895,6 +8034,10 @@ run-async@^0.1.0:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.1.9"
|
||||
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"
|
||||
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:
|
||||
version "2.1.0"
|
||||
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"
|
||||
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:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
|
||||
|
||||
Reference in New Issue
Block a user