mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 17:56:25 +00:00
Merge branch 'master' into drop-browser-image
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
|
|
||||||
29
FAQ.md
Normal file
29
FAQ.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
|
||||||
|
<details><summary>Allowing dangerous HTML tags</summary>
|
||||||
|
|
||||||
|
Sometimes it is useful to allow dangerous HTML tags to add interactivity to your notebook. One of the example is to use details/summary as a way to expand/collaps your todo-list.
|
||||||
|
|
||||||
|
* How to enable:
|
||||||
|
* Go to **Preferences** → **Interface** → **Sanitization** → **Allow dangerous html tags**
|
||||||
|
* Example note: Multiple todo-list
|
||||||
|
* Create new notes
|
||||||
|
* Paste the below code, and you'll see that you can expand/collaps the todo-list, and you can have multiple todo-list in your note.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<details><summary>What I want to do</summary>
|
||||||
|
|
||||||
|
- [x] Create an awesome feature X
|
||||||
|
- [ ] Do my homework
|
||||||
|
|
||||||
|
</details>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Other questions
|
||||||
|
|
||||||
|
You can ask [here][ISSUES]
|
||||||
|
|
||||||
|
[ISSUES]: https://github.com/BoostIO/Boostnote/issues
|
||||||
2
LICENSE
2
LICENSE
@@ -2,7 +2,7 @@ GPL-3.0
|
|||||||
|
|
||||||
Boostnote - an open source note-taking app made for programmers just like you.
|
Boostnote - an open source note-taking app made for programmers just like you.
|
||||||
|
|
||||||
Copyright (C) 2017 - 2018 BoostIO
|
Copyright (C) 2017 - 2019 BoostIO
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ import PropTypes from 'prop-types'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import CodeMirror from 'codemirror'
|
import CodeMirror from 'codemirror'
|
||||||
|
import hljs from 'highlight.js'
|
||||||
import 'codemirror-mode-elixir'
|
import 'codemirror-mode-elixir'
|
||||||
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
||||||
import convertModeName from 'browser/lib/convertModeName'
|
import convertModeName from 'browser/lib/convertModeName'
|
||||||
import { options, TableEditor, Alignment } from '@susisu/mte-kernel'
|
import {
|
||||||
|
options,
|
||||||
|
TableEditor,
|
||||||
|
Alignment
|
||||||
|
} from '@susisu/mte-kernel'
|
||||||
import TextEditorInterface from 'browser/lib/TextEditorInterface'
|
import TextEditorInterface from 'browser/lib/TextEditorInterface'
|
||||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||||
import iconv from 'iconv-lite'
|
import iconv from 'iconv-lite'
|
||||||
@@ -13,17 +18,104 @@ 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')
|
||||||
import TurndownService from 'turndown'
|
import TurndownService from 'turndown'
|
||||||
import { gfm } from 'turndown-plugin-gfm'
|
import {
|
||||||
|
gfm
|
||||||
|
} from 'turndown-plugin-gfm'
|
||||||
|
|
||||||
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
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')
|
||||||
|
}
|
||||||
|
|
||||||
|
const languageMaps = {
|
||||||
|
brainfuck: 'Brainfuck',
|
||||||
|
cpp: 'C++',
|
||||||
|
cs: 'C#',
|
||||||
|
clojure: 'Clojure',
|
||||||
|
'clojure-repl': 'ClojureScript',
|
||||||
|
cmake: 'CMake',
|
||||||
|
coffeescript: 'CoffeeScript',
|
||||||
|
crystal: 'Crystal',
|
||||||
|
css: 'CSS',
|
||||||
|
d: 'D',
|
||||||
|
dart: 'Dart',
|
||||||
|
delphi: 'Pascal',
|
||||||
|
diff: 'Diff',
|
||||||
|
django: 'Django',
|
||||||
|
dockerfile: 'Dockerfile',
|
||||||
|
ebnf: 'EBNF',
|
||||||
|
elm: 'Elm',
|
||||||
|
erlang: 'Erlang',
|
||||||
|
'erlang-repl': 'Erlang',
|
||||||
|
fortran: 'Fortran',
|
||||||
|
fsharp: 'F#',
|
||||||
|
gherkin: 'Gherkin',
|
||||||
|
go: 'Go',
|
||||||
|
groovy: 'Groovy',
|
||||||
|
haml: 'HAML',
|
||||||
|
haskell: 'Haskell',
|
||||||
|
haxe: 'Haxe',
|
||||||
|
http: 'HTTP',
|
||||||
|
ini: 'toml',
|
||||||
|
java: 'Java',
|
||||||
|
javascript: 'JavaScript',
|
||||||
|
json: 'JSON',
|
||||||
|
julia: 'Julia',
|
||||||
|
kotlin: 'Kotlin',
|
||||||
|
less: 'LESS',
|
||||||
|
livescript: 'LiveScript',
|
||||||
|
lua: 'Lua',
|
||||||
|
markdown: 'Markdown',
|
||||||
|
mathematica: 'Mathematica',
|
||||||
|
nginx: 'Nginx',
|
||||||
|
nsis: 'NSIS',
|
||||||
|
objectivec: 'Objective-C',
|
||||||
|
ocaml: 'Ocaml',
|
||||||
|
perl: 'Perl',
|
||||||
|
php: 'PHP',
|
||||||
|
powershell: 'PowerShell',
|
||||||
|
properties: 'Properties files',
|
||||||
|
protobuf: 'ProtoBuf',
|
||||||
|
python: 'Python',
|
||||||
|
puppet: 'Puppet',
|
||||||
|
q: 'Q',
|
||||||
|
r: 'R',
|
||||||
|
ruby: 'Ruby',
|
||||||
|
rust: 'Rust',
|
||||||
|
sas: 'SAS',
|
||||||
|
scala: 'Scala',
|
||||||
|
scheme: 'Scheme',
|
||||||
|
scss: 'SCSS',
|
||||||
|
shell: 'Shell',
|
||||||
|
smalltalk: 'Smalltalk',
|
||||||
|
sml: 'SML',
|
||||||
|
sql: 'SQL',
|
||||||
|
stylus: 'Stylus',
|
||||||
|
swift: 'Swift',
|
||||||
|
tcl: 'Tcl',
|
||||||
|
tex: 'LaTex',
|
||||||
|
typescript: 'TypeScript',
|
||||||
|
twig: 'Twig',
|
||||||
|
vbnet: 'VB.NET',
|
||||||
|
vbscript: 'VBScript',
|
||||||
|
verilog: 'Verilog',
|
||||||
|
vhdl: 'VHDL',
|
||||||
|
xml: 'HTML',
|
||||||
|
xquery: 'XQuery',
|
||||||
|
yaml: 'YAML',
|
||||||
|
elixir: 'Elixir'
|
||||||
|
}
|
||||||
|
|
||||||
export default class CodeEditor extends React.Component {
|
export default class CodeEditor extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
@@ -34,6 +126,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)
|
||||||
}
|
}
|
||||||
@@ -49,14 +142,21 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
this.props.onBlur != null && this.props.onBlur(e)
|
this.props.onBlur != null && this.props.onBlur(e)
|
||||||
|
|
||||||
const { storageKey, noteKey } = this.props
|
const {
|
||||||
|
storageKey,
|
||||||
|
noteKey
|
||||||
|
} = this.props
|
||||||
attachmentManagement.deleteAttachmentsNotPresentInNote(
|
attachmentManagement.deleteAttachmentsNotPresentInNote(
|
||||||
this.editor.getValue(),
|
this.editor.getValue(),
|
||||||
storageKey,
|
storageKey,
|
||||||
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 +165,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()
|
||||||
@@ -111,7 +215,9 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleFormatTable () {
|
handleFormatTable () {
|
||||||
this.tableEditor.formatAll(options({textWidthOptions: {}}))
|
this.tableEditor.formatAll(options({
|
||||||
|
textWidthOptions: {}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditorActivity () {
|
handleEditorActivity () {
|
||||||
@@ -120,42 +226,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) {
|
||||||
@@ -198,6 +271,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
|
||||||
},
|
},
|
||||||
@@ -207,13 +283,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,
|
||||||
@@ -227,21 +346,28 @@ export default class CodeEditor extends React.Component {
|
|||||||
foldGutter: true,
|
foldGutter: true,
|
||||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||||
autoCloseBrackets: {
|
autoCloseBrackets: {
|
||||||
pairs: '()[]{}\'\'""$$**``',
|
pairs: this.props.matchingPairs,
|
||||||
triples: '```"""\'\'\'',
|
triples: this.props.matchingTriples,
|
||||||
explode: '[]{}``$$',
|
explode: this.props.explodingPairs,
|
||||||
override: true
|
override: true
|
||||||
},
|
},
|
||||||
extraKeys: this.defaultKeyMap
|
extraKeys: this.defaultKeyMap
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setMode(this.props.mode)
|
if (!this.props.mode && this.props.value && this.props.autoDetect) {
|
||||||
|
this.autoDetectLanguage(this.props.value)
|
||||||
|
} else {
|
||||||
|
this.setMode(this.props.mode)
|
||||||
|
}
|
||||||
|
|
||||||
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')
|
||||||
@@ -269,43 +395,117 @@ export default class CodeEditor extends React.Component {
|
|||||||
})
|
})
|
||||||
|
|
||||||
this.editorKeyMap = CodeMirror.normalizeKeyMap({
|
this.editorKeyMap = CodeMirror.normalizeKeyMap({
|
||||||
'Tab': () => { this.tableEditor.nextCell(this.tableEditorOptions) },
|
'Tab': () => {
|
||||||
'Shift-Tab': () => { this.tableEditor.previousCell(this.tableEditorOptions) },
|
this.tableEditor.nextCell(this.tableEditorOptions)
|
||||||
'Enter': () => { this.tableEditor.nextRow(this.tableEditorOptions) },
|
},
|
||||||
'Ctrl-Enter': () => { this.tableEditor.escape(this.tableEditorOptions) },
|
'Shift-Tab': () => {
|
||||||
'Cmd-Enter': () => { this.tableEditor.escape(this.tableEditorOptions) },
|
this.tableEditor.previousCell(this.tableEditorOptions)
|
||||||
'Shift-Ctrl-Left': () => { this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) },
|
},
|
||||||
'Shift-Cmd-Left': () => { this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) },
|
'Enter': () => {
|
||||||
'Shift-Ctrl-Right': () => { this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) },
|
this.tableEditor.nextRow(this.tableEditorOptions)
|
||||||
'Shift-Cmd-Right': () => { this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) },
|
},
|
||||||
'Shift-Ctrl-Up': () => { this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) },
|
'Ctrl-Enter': () => {
|
||||||
'Shift-Cmd-Up': () => { this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) },
|
this.tableEditor.escape(this.tableEditorOptions)
|
||||||
'Shift-Ctrl-Down': () => { this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) },
|
},
|
||||||
'Shift-Cmd-Down': () => { this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) },
|
'Cmd-Enter': () => {
|
||||||
'Ctrl-Left': () => { this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) },
|
this.tableEditor.escape(this.tableEditorOptions)
|
||||||
'Cmd-Left': () => { this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) },
|
},
|
||||||
'Ctrl-Right': () => { this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) },
|
'Shift-Ctrl-Left': () => {
|
||||||
'Cmd-Right': () => { this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions)
|
||||||
'Ctrl-Up': () => { this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) },
|
},
|
||||||
'Cmd-Up': () => { this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) },
|
'Shift-Cmd-Left': () => {
|
||||||
'Ctrl-Down': () => { this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions)
|
||||||
'Cmd-Down': () => { this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) },
|
},
|
||||||
'Ctrl-K Ctrl-I': () => { this.tableEditor.insertRow(this.tableEditorOptions) },
|
'Shift-Ctrl-Right': () => {
|
||||||
'Cmd-K Cmd-I': () => { this.tableEditor.insertRow(this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions)
|
||||||
'Ctrl-L Ctrl-I': () => { this.tableEditor.deleteRow(this.tableEditorOptions) },
|
},
|
||||||
'Cmd-L Cmd-I': () => { this.tableEditor.deleteRow(this.tableEditorOptions) },
|
'Shift-Cmd-Right': () => {
|
||||||
'Ctrl-K Ctrl-J': () => { this.tableEditor.insertColumn(this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions)
|
||||||
'Cmd-K Cmd-J': () => { this.tableEditor.insertColumn(this.tableEditorOptions) },
|
},
|
||||||
'Ctrl-L Ctrl-J': () => { this.tableEditor.deleteColumn(this.tableEditorOptions) },
|
'Shift-Ctrl-Up': () => {
|
||||||
'Cmd-L Cmd-J': () => { this.tableEditor.deleteColumn(this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions)
|
||||||
'Alt-Shift-Ctrl-Left': () => { this.tableEditor.moveColumn(-1, this.tableEditorOptions) },
|
},
|
||||||
'Alt-Shift-Cmd-Left': () => { this.tableEditor.moveColumn(-1, this.tableEditorOptions) },
|
'Shift-Cmd-Up': () => {
|
||||||
'Alt-Shift-Ctrl-Right': () => { this.tableEditor.moveColumn(1, this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions)
|
||||||
'Alt-Shift-Cmd-Right': () => { this.tableEditor.moveColumn(1, this.tableEditorOptions) },
|
},
|
||||||
'Alt-Shift-Ctrl-Up': () => { this.tableEditor.moveRow(-1, this.tableEditorOptions) },
|
'Shift-Ctrl-Down': () => {
|
||||||
'Alt-Shift-Cmd-Up': () => { this.tableEditor.moveRow(-1, this.tableEditorOptions) },
|
this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions)
|
||||||
'Alt-Shift-Ctrl-Down': () => { this.tableEditor.moveRow(1, this.tableEditorOptions) },
|
},
|
||||||
'Alt-Shift-Cmd-Down': () => { this.tableEditor.moveRow(1, this.tableEditorOptions) }
|
'Shift-Cmd-Down': () => {
|
||||||
|
this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-Left': () => {
|
||||||
|
this.tableEditor.moveFocus(0, -1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-Left': () => {
|
||||||
|
this.tableEditor.moveFocus(0, -1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-Right': () => {
|
||||||
|
this.tableEditor.moveFocus(0, 1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-Right': () => {
|
||||||
|
this.tableEditor.moveFocus(0, 1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-Up': () => {
|
||||||
|
this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-Up': () => {
|
||||||
|
this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-Down': () => {
|
||||||
|
this.tableEditor.moveFocus(1, 0, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-Down': () => {
|
||||||
|
this.tableEditor.moveFocus(1, 0, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-K Ctrl-I': () => {
|
||||||
|
this.tableEditor.insertRow(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-K Cmd-I': () => {
|
||||||
|
this.tableEditor.insertRow(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-L Ctrl-I': () => {
|
||||||
|
this.tableEditor.deleteRow(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-L Cmd-I': () => {
|
||||||
|
this.tableEditor.deleteRow(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-K Ctrl-J': () => {
|
||||||
|
this.tableEditor.insertColumn(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-K Cmd-J': () => {
|
||||||
|
this.tableEditor.insertColumn(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Ctrl-L Ctrl-J': () => {
|
||||||
|
this.tableEditor.deleteColumn(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Cmd-L Cmd-J': () => {
|
||||||
|
this.tableEditor.deleteColumn(this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Ctrl-Left': () => {
|
||||||
|
this.tableEditor.moveColumn(-1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Cmd-Left': () => {
|
||||||
|
this.tableEditor.moveColumn(-1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Ctrl-Right': () => {
|
||||||
|
this.tableEditor.moveColumn(1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Cmd-Right': () => {
|
||||||
|
this.tableEditor.moveColumn(1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Ctrl-Up': () => {
|
||||||
|
this.tableEditor.moveRow(-1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Cmd-Up': () => {
|
||||||
|
this.tableEditor.moveRow(-1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Ctrl-Down': () => {
|
||||||
|
this.tableEditor.moveRow(1, this.tableEditorOptions)
|
||||||
|
},
|
||||||
|
'Alt-Shift-Cmd-Down': () => {
|
||||||
|
this.tableEditor.moveRow(1, this.tableEditorOptions)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (this.props.enableTableEditor) {
|
if (this.props.enableTableEditor) {
|
||||||
@@ -316,6 +516,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) {
|
||||||
@@ -392,8 +594,14 @@ export default class CodeEditor extends React.Component {
|
|||||||
return {
|
return {
|
||||||
text: wordBeforeCursor,
|
text: wordBeforeCursor,
|
||||||
range: {
|
range: {
|
||||||
from: { line: lineNumber, ch: originCursorPosition },
|
from: {
|
||||||
to: { line: lineNumber, ch: cursorPosition }
|
line: lineNumber,
|
||||||
|
ch: originCursorPosition
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
line: lineNumber,
|
||||||
|
ch: cursorPosition
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,7 +627,10 @@ export default class CodeEditor extends React.Component {
|
|||||||
|
|
||||||
componentDidUpdate (prevProps, prevState) {
|
componentDidUpdate (prevProps, prevState) {
|
||||||
let needRefresh = false
|
let needRefresh = false
|
||||||
const { rulers, enableRulers } = this.props
|
const {
|
||||||
|
rulers,
|
||||||
|
enableRulers
|
||||||
|
} = this.props
|
||||||
if (prevProps.mode !== this.props.mode) {
|
if (prevProps.mode !== this.props.mode) {
|
||||||
this.setMode(this.props.mode)
|
this.setMode(this.props.mode)
|
||||||
}
|
}
|
||||||
@@ -460,6 +671,18 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd)
|
this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prevProps.matchingPairs !== this.props.matchingPairs ||
|
||||||
|
prevProps.matchingTriples !== this.props.matchingTriples ||
|
||||||
|
prevProps.explodingPairs !== this.props.explodingPairs) {
|
||||||
|
const bracketObject = {
|
||||||
|
pairs: this.props.matchingPairs,
|
||||||
|
triples: this.props.matchingTriples,
|
||||||
|
explode: this.props.explodingPairs,
|
||||||
|
override: true
|
||||||
|
}
|
||||||
|
this.editor.setOption('autoCloseBrackets', bracketObject)
|
||||||
|
}
|
||||||
|
|
||||||
if (prevProps.enableTableEditor !== this.props.enableTableEditor) {
|
if (prevProps.enableTableEditor !== this.props.enableTableEditor) {
|
||||||
if (this.props.enableTableEditor) {
|
if (this.props.enableTableEditor) {
|
||||||
this.editor.on('cursorActivity', this.editorActivityHandler)
|
this.editor.on('cursorActivity', this.editorActivityHandler)
|
||||||
@@ -473,6 +696,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
|
||||||
@@ -484,7 +715,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
if (prevProps.spellCheck !== this.props.spellCheck) {
|
if (prevProps.spellCheck !== this.props.spellCheck) {
|
||||||
if (this.props.spellCheck === false) {
|
if (this.props.spellCheck === false) {
|
||||||
spellcheck.setLanguage(this.editor, spellcheck.SPELLCHECK_DISABLED)
|
spellcheck.setLanguage(this.editor, spellcheck.SPELLCHECK_DISABLED)
|
||||||
let elem = document.getElementById('editor-bottom-panel')
|
const elem = document.getElementById('editor-bottom-panel')
|
||||||
elem.parentNode.removeChild(elem)
|
elem.parentNode.removeChild(elem)
|
||||||
} else {
|
} else {
|
||||||
this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'})
|
this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'})
|
||||||
@@ -497,7 +728,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setMode (mode) {
|
setMode (mode) {
|
||||||
let syntax = CodeMirror.findModeByName(convertModeName(mode))
|
let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text'))
|
||||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||||
|
|
||||||
this.editor.setOption('mode', syntax.mime)
|
this.editor.setOption('mode', syntax.mime)
|
||||||
@@ -506,12 +737,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) {
|
||||||
@@ -536,6 +851,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()
|
||||||
}
|
}
|
||||||
@@ -548,7 +864,10 @@ export default class CodeEditor extends React.Component {
|
|||||||
|
|
||||||
handleDropImage (dropEvent) {
|
handleDropImage (dropEvent) {
|
||||||
dropEvent.preventDefault()
|
dropEvent.preventDefault()
|
||||||
const { storageKey, noteKey } = this.props
|
const {
|
||||||
|
storageKey,
|
||||||
|
noteKey
|
||||||
|
} = this.props
|
||||||
attachmentManagement.handleAttachmentDrop(
|
attachmentManagement.handleAttachmentDrop(
|
||||||
this,
|
this,
|
||||||
storageKey,
|
storageKey,
|
||||||
@@ -561,53 +880,107 @@ export default class CodeEditor extends React.Component {
|
|||||||
this.editor.replaceSelection(imageMd)
|
this.editor.replaceSelection(imageMd)
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePaste (editor, e) {
|
autoDetectLanguage (content) {
|
||||||
const clipboardData = e.clipboardData
|
const res = hljs.highlightAuto(content, Object.keys(languageMaps))
|
||||||
const { storageKey, noteKey } = this.props
|
this.setMode(languageMaps[res.language])
|
||||||
const dataTransferItem = clipboardData.items[0]
|
}
|
||||||
const pastedTxt = clipboardData.getData('text')
|
|
||||||
const isURL = str => {
|
handlePaste (editor, forceSmartPaste) {
|
||||||
const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/
|
const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props
|
||||||
return matcher.test(str)
|
|
||||||
}
|
const isURL = str => /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.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({
|
||||||
{ line: startCursor.line, ch: startCursor.ch - 2 },
|
line: startCursor.line,
|
||||||
{ line: startCursor.line, ch: startCursor.ch }
|
ch: startCursor.ch - 2
|
||||||
)
|
}, {
|
||||||
|
line: startCursor.line,
|
||||||
|
ch: startCursor.ch
|
||||||
|
})
|
||||||
const endCursor = editor.getCursor('end')
|
const endCursor = editor.getCursor('end')
|
||||||
const nextChar = editor.getRange(
|
const nextChar = editor.getRange({
|
||||||
{ line: endCursor.line, ch: endCursor.ch },
|
line: endCursor.line,
|
||||||
{ line: endCursor.line, ch: endCursor.ch + 1 }
|
ch: endCursor.ch
|
||||||
)
|
}, {
|
||||||
|
line: endCursor.line,
|
||||||
|
ch: endCursor.ch + 1
|
||||||
|
})
|
||||||
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 (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 {
|
||||||
|
const image = clipboard.readImage()
|
||||||
|
if (!image.isEmpty()) {
|
||||||
|
attachmentManagement.handlePasteNativeImage(
|
||||||
|
this,
|
||||||
|
storageKey,
|
||||||
|
noteKey,
|
||||||
|
image
|
||||||
|
)
|
||||||
|
} else if (enableSmartPaste || forceSmartPaste) {
|
||||||
|
const pastedHtml = clipboard.readHTML()
|
||||||
|
if (pastedHtml.length > 0) {
|
||||||
|
this.handlePasteHtml(editor, pastedHtml)
|
||||||
|
} else {
|
||||||
|
this.handlePasteText(editor, pastedTxt)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.handlePasteText(editor, pastedTxt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.props.mode && this.props.autoDetect) {
|
||||||
|
this.autoDetectLanguage(editor.doc.getValue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,8 +990,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)
|
||||||
|
|
||||||
@@ -657,12 +1029,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) => {
|
||||||
@@ -683,6 +1058,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 {
|
||||||
@@ -708,7 +1106,7 @@ export default class CodeEditor extends React.Component {
|
|||||||
iconv.encodingExists(_charset)
|
iconv.encodingExists(_charset)
|
||||||
? _charset
|
? _charset
|
||||||
: 'utf-8'
|
: 'utf-8'
|
||||||
resolve(iconv.decode(new Buffer(buff), charset).toString())
|
resolve(iconv.decode(Buffer.from(buff), charset).toString())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e)
|
reject(e)
|
||||||
}
|
}
|
||||||
@@ -728,20 +1126,28 @@ export default class CodeEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {className, fontSize} = this.props
|
const {
|
||||||
|
className,
|
||||||
|
fontSize
|
||||||
|
} = this.props
|
||||||
const fontFamily = normalizeEditorFontFamily(this.props.fontFamily)
|
const fontFamily = normalizeEditorFontFamily(this.props.fontFamily)
|
||||||
const width = this.props.width
|
const width = this.props.width
|
||||||
return (
|
return (<
|
||||||
<div
|
div className={
|
||||||
className={className == null ? 'CodeEditor' : `CodeEditor ${className}`}
|
className == null ? 'CodeEditor' : `CodeEditor ${className}`
|
||||||
ref='root'
|
}
|
||||||
tabIndex='-1'
|
ref='root'
|
||||||
style={{
|
tabIndex='-1'
|
||||||
fontFamily,
|
style={
|
||||||
fontSize: fontSize,
|
{
|
||||||
width: width
|
fontFamily,
|
||||||
}}
|
fontSize: fontSize,
|
||||||
onDrop={e => this.handleDropImage(e)}
|
width: width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDrop={
|
||||||
|
e => this.handleDropImage(e)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -775,6 +1181,7 @@ CodeEditor.propTypes = {
|
|||||||
onBlur: PropTypes.func,
|
onBlur: PropTypes.func,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
readOnly: PropTypes.bool,
|
readOnly: PropTypes.bool,
|
||||||
|
autoDetect: PropTypes.bool,
|
||||||
spellCheck: PropTypes.bool
|
spellCheck: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,5 +1193,6 @@ CodeEditor.defaultProps = {
|
|||||||
fontFamily: 'Monaco, Consolas',
|
fontFamily: 'Monaco, Consolas',
|
||||||
indentSize: 4,
|
indentSize: 4,
|
||||||
indentType: 'space',
|
indentType: 'space',
|
||||||
|
autoDetect: false,
|
||||||
spellCheck: false
|
spellCheck: false
|
||||||
}
|
}
|
||||||
|
|||||||
68
browser/components/ColorPicker.js
Normal file
68
browser/components/ColorPicker.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { SketchPicker } from 'react-color'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './ColorPicker.styl'
|
||||||
|
|
||||||
|
const componentHeight = 330
|
||||||
|
|
||||||
|
class ColorPicker extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
color: this.props.color || '#939395'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onColorChange = this.onColorChange.bind(this)
|
||||||
|
this.handleConfirm = this.handleConfirm.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
this.onColorChange(nextProps.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
onColorChange (color) {
|
||||||
|
this.setState({
|
||||||
|
color
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirm () {
|
||||||
|
this.props.onConfirm(this.state.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { onReset, onCancel, targetRect } = this.props
|
||||||
|
const { color } = this.state
|
||||||
|
|
||||||
|
const clientHeight = document.body.clientHeight
|
||||||
|
const alignX = targetRect.right + 4
|
||||||
|
let alignY = targetRect.top
|
||||||
|
if (targetRect.top + componentHeight > clientHeight) {
|
||||||
|
alignY = targetRect.bottom - componentHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div styleName='colorPicker' style={{top: `${alignY}px`, left: `${alignX}px`}}>
|
||||||
|
<div styleName='cover' onClick={onCancel} />
|
||||||
|
<SketchPicker color={color} onChange={this.onColorChange} />
|
||||||
|
<div styleName='footer'>
|
||||||
|
<button styleName='btn-reset' onClick={onReset}>Reset</button>
|
||||||
|
<button styleName='btn-cancel' onClick={onCancel}>Cancel</button>
|
||||||
|
<button styleName='btn-confirm' onClick={this.handleConfirm}>Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorPicker.propTypes = {
|
||||||
|
color: PropTypes.string,
|
||||||
|
targetRect: PropTypes.object,
|
||||||
|
onConfirm: PropTypes.func.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
onReset: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(ColorPicker, styles)
|
||||||
39
browser/components/ColorPicker.styl
Normal file
39
browser/components/ColorPicker.styl
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
.colorPicker
|
||||||
|
position fixed
|
||||||
|
z-index 2
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
|
||||||
|
.cover
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
|
||||||
|
.footer
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
z-index 2
|
||||||
|
align-items center
|
||||||
|
& > button + button
|
||||||
|
margin-left 10px
|
||||||
|
|
||||||
|
.btn-cancel,
|
||||||
|
.btn-confirm,
|
||||||
|
.btn-reset
|
||||||
|
vertical-align middle
|
||||||
|
height 25px
|
||||||
|
margin-top 2.5px
|
||||||
|
border-radius 2px
|
||||||
|
border none
|
||||||
|
padding 0 5px
|
||||||
|
background-color $default-button-background
|
||||||
|
&:hover
|
||||||
|
background-color $default-button-background--hover
|
||||||
|
.btn-confirm
|
||||||
|
background-color #1EC38B
|
||||||
|
&:hover
|
||||||
|
background-color darken(#1EC38B, 25%)
|
||||||
|
|
||||||
|
|
||||||
@@ -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) {
|
||||||
@@ -19,10 +20,10 @@ class MarkdownEditor extends React.Component {
|
|||||||
this.supportMdSelectionBold = [16, 17, 186]
|
this.supportMdSelectionBold = [16, 17, 186]
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'PREVIEW',
|
status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'CODE',
|
||||||
renderValue: props.value,
|
renderValue: props.value,
|
||||||
keyPressed: new Set(),
|
keyPressed: new Set(),
|
||||||
isLocked: false
|
isLocked: props.isLocked
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lockEditorCode = () => this.handleLockEditor()
|
this.lockEditorCode = () => this.handleLockEditor()
|
||||||
@@ -75,6 +76,7 @@ class MarkdownEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleContextMenu (e) {
|
handleContextMenu (e) {
|
||||||
|
if (this.state.isLocked) return
|
||||||
const { config } = this.props
|
const { config } = this.props
|
||||||
if (config.editor.switchPreview === 'RIGHTCLICK') {
|
if (config.editor.switchPreview === 'RIGHTCLICK') {
|
||||||
const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW'
|
const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW'
|
||||||
@@ -221,6 +223,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 +256,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
|
||||||
@@ -270,14 +294,21 @@ class MarkdownEditor extends React.Component {
|
|||||||
enableRulers={config.editor.enableRulers}
|
enableRulers={config.editor.enableRulers}
|
||||||
rulers={config.editor.rulers}
|
rulers={config.editor.rulers}
|
||||||
displayLineNumbers={config.editor.displayLineNumbers}
|
displayLineNumbers={config.editor.displayLineNumbers}
|
||||||
|
matchingPairs={config.editor.matchingPairs}
|
||||||
|
matchingTriples={config.editor.matchingTriples}
|
||||||
|
explodingPairs={config.editor.explodingPairs}
|
||||||
scrollPastEnd={config.editor.scrollPastEnd}
|
scrollPastEnd={config.editor.scrollPastEnd}
|
||||||
storageKey={storageKey}
|
storageKey={storageKey}
|
||||||
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 +342,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',
|
||||||
@@ -428,6 +398,31 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Convert special characters between three ```
|
||||||
|
* @param {string[]} splitWithCodeTag Array of HTML strings separated by three ```
|
||||||
|
* @returns {string} HTML in which special characters between three ``` have been converted
|
||||||
|
*/
|
||||||
|
escapeHtmlCharactersInCodeTag (splitWithCodeTag) {
|
||||||
|
for (let index = 0; index < splitWithCodeTag.length; index++) {
|
||||||
|
const codeTagRequired = (splitWithCodeTag[index] !== '\`\`\`' && index < splitWithCodeTag.length - 1)
|
||||||
|
if (codeTagRequired) {
|
||||||
|
splitWithCodeTag.splice((index + 1), 0, '\`\`\`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let inCodeTag = false
|
||||||
|
let result = ''
|
||||||
|
for (let content of splitWithCodeTag) {
|
||||||
|
if (content === '\`\`\`') {
|
||||||
|
inCodeTag = !inCodeTag
|
||||||
|
} else if (inCodeTag) {
|
||||||
|
content = escapeHtmlCharacters(content)
|
||||||
|
}
|
||||||
|
result += content
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
getScrollBarStyle () {
|
getScrollBarStyle () {
|
||||||
const { theme } = this.props
|
const { theme } = this.props
|
||||||
|
|
||||||
@@ -443,6 +438,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 +477,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 +494,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 +514,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',
|
||||||
@@ -658,11 +657,16 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
indentSize,
|
indentSize,
|
||||||
showCopyNotification,
|
showCopyNotification,
|
||||||
storagePath,
|
storagePath,
|
||||||
noteKey
|
noteKey,
|
||||||
|
sanitize
|
||||||
} = this.props
|
} = this.props
|
||||||
let { value, codeBlockTheme } = this.props
|
let { value, codeBlockTheme } = this.props
|
||||||
|
|
||||||
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
|
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
|
||||||
|
if (sanitize === 'NONE') {
|
||||||
|
const splitWithCodeTag = value.split('```')
|
||||||
|
value = this.escapeHtmlCharactersInCodeTag(splitWithCodeTag)
|
||||||
|
}
|
||||||
const renderedHTML = this.markdown.render(value)
|
const renderedHTML = this.markdown.render(value)
|
||||||
attachmentManagement.migrateAttachments(value, storagePath, noteKey)
|
attachmentManagement.migrateAttachments(value, storagePath, noteKey)
|
||||||
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(
|
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(
|
||||||
@@ -800,6 +804,109 @@ 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const markdownPreviewIframe = document.querySelector('.MarkdownPreview')
|
||||||
|
const rect = markdownPreviewIframe.getBoundingClientRect()
|
||||||
|
const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('img')
|
||||||
|
for (const img of imgList) {
|
||||||
|
img.onclick = () => {
|
||||||
|
const widthMagnification = document.body.clientWidth / img.width
|
||||||
|
const heightMagnification = document.body.clientHeight / img.height
|
||||||
|
const baseOnWidth = widthMagnification < heightMagnification
|
||||||
|
const magnification = baseOnWidth ? widthMagnification : heightMagnification
|
||||||
|
|
||||||
|
const zoomImgWidth = img.width * magnification
|
||||||
|
const zoomImgHeight = img.height * magnification
|
||||||
|
const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2
|
||||||
|
const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2
|
||||||
|
const originalImgTop = img.y + rect.top
|
||||||
|
const originalImgLeft = img.x + rect.left
|
||||||
|
const originalImgRect = {
|
||||||
|
top: `${originalImgTop}px`,
|
||||||
|
left: `${originalImgLeft}px`,
|
||||||
|
width: `${img.width}px`,
|
||||||
|
height: `${img.height}px`
|
||||||
|
}
|
||||||
|
const zoomInImgRect = {
|
||||||
|
top: `${baseOnWidth ? zoomImgTop : 0}px`,
|
||||||
|
left: `${baseOnWidth ? 0 : zoomImgLeft}px`,
|
||||||
|
width: `${zoomImgWidth}px`,
|
||||||
|
height: `${zoomImgHeight}px`
|
||||||
|
}
|
||||||
|
const animationSpeed = 300
|
||||||
|
|
||||||
|
const zoomImg = document.createElement('img')
|
||||||
|
zoomImg.src = img.src
|
||||||
|
zoomImg.style = `
|
||||||
|
position: absolute;
|
||||||
|
top: ${baseOnWidth ? zoomImgTop : 0}px;
|
||||||
|
left: ${baseOnWidth ? 0 : zoomImgLeft}px;
|
||||||
|
width: ${zoomImgWidth};
|
||||||
|
height: ${zoomImgHeight}px;
|
||||||
|
`
|
||||||
|
zoomImg.animate([
|
||||||
|
originalImgRect,
|
||||||
|
zoomInImgRect
|
||||||
|
], animationSpeed)
|
||||||
|
|
||||||
|
const overlay = document.createElement('div')
|
||||||
|
overlay.style = `
|
||||||
|
background-color: rgba(0,0,0,0.5);
|
||||||
|
cursor: zoom-out;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: ${document.body.clientHeight}px;
|
||||||
|
z-index: 100;
|
||||||
|
`
|
||||||
|
overlay.onclick = () => {
|
||||||
|
zoomImg.style = `
|
||||||
|
position: absolute;
|
||||||
|
top: ${originalImgTop}px;
|
||||||
|
left: ${originalImgLeft}px;
|
||||||
|
width: ${img.width}px;
|
||||||
|
height: ${img.height}px;
|
||||||
|
`
|
||||||
|
const zoomOutImgAnimation = zoomImg.animate([
|
||||||
|
zoomInImgRect,
|
||||||
|
originalImgRect
|
||||||
|
], animationSpeed)
|
||||||
|
zoomOutImgAnimation.onfinish = () => overlay.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.appendChild(zoomImg)
|
||||||
|
document.body.appendChild(overlay)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focus () {
|
focus () {
|
||||||
@@ -842,7 +949,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()
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
this.refs.code.setValue(value)
|
this.refs.code.setValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleOnChange () {
|
handleOnChange (e) {
|
||||||
this.value = this.refs.code.value
|
this.value = this.refs.code.value
|
||||||
this.props.onChange()
|
this.props.onChange(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleScroll (e) {
|
handleScroll (e) {
|
||||||
@@ -136,7 +136,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
|
||||||
@@ -160,6 +160,9 @@ class MarkdownSplitEditor extends React.Component {
|
|||||||
fontFamily={config.editor.fontFamily}
|
fontFamily={config.editor.fontFamily}
|
||||||
fontSize={editorFontSize}
|
fontSize={editorFontSize}
|
||||||
displayLineNumbers={config.editor.displayLineNumbers}
|
displayLineNumbers={config.editor.displayLineNumbers}
|
||||||
|
matchingPairs={config.editor.matchingPairs}
|
||||||
|
matchingTriples={config.editor.matchingTriples}
|
||||||
|
explodingPairs={config.editor.explodingPairs}
|
||||||
indentType={config.editor.indentType}
|
indentType={config.editor.indentType}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
enableRulers={config.editor.enableRulers}
|
enableRulers={config.editor.enableRulers}
|
||||||
@@ -169,9 +172,13 @@ 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}
|
||||||
|
onChange={(e) => this.handleOnChange(e)}
|
||||||
onScroll={this.handleScroll.bind(this)}
|
onScroll={this.handleScroll.bind(this)}
|
||||||
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' />
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { isArray } from 'lodash'
|
import { isArray } from 'lodash'
|
||||||
|
import invertColor from 'invert-color'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
||||||
import styles from './NoteItem.styl'
|
import styles from './NoteItem.styl'
|
||||||
@@ -13,29 +14,38 @@ import i18n from 'browser/lib/i18n'
|
|||||||
/**
|
/**
|
||||||
* @description Tag element component.
|
* @description Tag element component.
|
||||||
* @param {string} tagName
|
* @param {string} tagName
|
||||||
|
* @param {string} color
|
||||||
* @return {React.Component}
|
* @return {React.Component}
|
||||||
*/
|
*/
|
||||||
const TagElement = ({ tagName }) => (
|
const TagElement = ({ tagName, color }) => {
|
||||||
<span styleName='item-bottom-tagList-item' key={tagName}>
|
const style = {}
|
||||||
#{tagName}
|
if (color) {
|
||||||
</span>
|
style.backgroundColor = color
|
||||||
)
|
style.color = invertColor(color, { black: '#222', white: '#f1f1f1', threshold: 0.3 })
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span styleName='item-bottom-tagList-item' key={tagName} style={style}>
|
||||||
|
#{tagName}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Tag element list component.
|
* @description Tag element list component.
|
||||||
* @param {Array|null} tags
|
* @param {Array|null} tags
|
||||||
* @param {boolean} showTagsAlphabetically
|
* @param {boolean} showTagsAlphabetically
|
||||||
|
* @param {Object} coloredTags
|
||||||
* @return {React.Component}
|
* @return {React.Component}
|
||||||
*/
|
*/
|
||||||
const TagElementList = (tags, showTagsAlphabetically) => {
|
const TagElementList = (tags, showTagsAlphabetically, coloredTags) => {
|
||||||
if (!isArray(tags)) {
|
if (!isArray(tags)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showTagsAlphabetically) {
|
if (showTagsAlphabetically) {
|
||||||
return _.sortBy(tags).map(tag => TagElement({ tagName: tag }))
|
return _.sortBy(tags).map(tag => TagElement({ tagName: tag, color: coloredTags[tag] }))
|
||||||
} else {
|
} else {
|
||||||
return tags.map(tag => TagElement({ tagName: tag }))
|
return tags.map(tag => TagElement({ tagName: tag, color: coloredTags[tag] }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +56,7 @@ const TagElementList = (tags, showTagsAlphabetically) => {
|
|||||||
* @param {Function} handleNoteClick
|
* @param {Function} handleNoteClick
|
||||||
* @param {Function} handleNoteContextMenu
|
* @param {Function} handleNoteContextMenu
|
||||||
* @param {Function} handleDragStart
|
* @param {Function} handleDragStart
|
||||||
|
* @param {Object} coloredTags
|
||||||
* @param {string} dateDisplay
|
* @param {string} dateDisplay
|
||||||
*/
|
*/
|
||||||
const NoteItem = ({
|
const NoteItem = ({
|
||||||
@@ -59,7 +70,8 @@ const NoteItem = ({
|
|||||||
storageName,
|
storageName,
|
||||||
folderName,
|
folderName,
|
||||||
viewType,
|
viewType,
|
||||||
showTagsAlphabetically
|
showTagsAlphabetically,
|
||||||
|
coloredTags
|
||||||
}) => (
|
}) => (
|
||||||
<div
|
<div
|
||||||
styleName={isActive ? 'item--active' : 'item'}
|
styleName={isActive ? 'item--active' : 'item'}
|
||||||
@@ -97,7 +109,7 @@ const NoteItem = ({
|
|||||||
<div styleName='item-bottom'>
|
<div styleName='item-bottom'>
|
||||||
<div styleName='item-bottom-tagList'>
|
<div styleName='item-bottom-tagList'>
|
||||||
{note.tags.length > 0
|
{note.tags.length > 0
|
||||||
? TagElementList(note.tags, showTagsAlphabetically)
|
? TagElementList(note.tags, showTagsAlphabetically, coloredTags)
|
||||||
: <span
|
: <span
|
||||||
style={{ fontStyle: 'italic', opacity: 0.5 }}
|
style={{ fontStyle: 'italic', opacity: 0.5 }}
|
||||||
styleName='item-bottom-tagList-empty'
|
styleName='item-bottom-tagList-empty'
|
||||||
@@ -127,6 +139,7 @@ const NoteItem = ({
|
|||||||
NoteItem.propTypes = {
|
NoteItem.propTypes = {
|
||||||
isActive: PropTypes.bool.isRequired,
|
isActive: PropTypes.bool.isRequired,
|
||||||
dateDisplay: PropTypes.string.isRequired,
|
dateDisplay: PropTypes.string.isRequired,
|
||||||
|
coloredTags: PropTypes.object,
|
||||||
note: PropTypes.shape({
|
note: PropTypes.shape({
|
||||||
storage: PropTypes.string.isRequired,
|
storage: PropTypes.string.isRequired,
|
||||||
key: PropTypes.string.isRequired,
|
key: PropTypes.string.isRequired,
|
||||||
|
|||||||
@@ -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%)
|
|
||||||
@@ -10,11 +10,12 @@ import CSSModules from 'browser/lib/CSSModules'
|
|||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {Function} handleClickTagListItem
|
* @param {Function} handleClickTagListItem
|
||||||
* @param {Function} handleClickNarrowToTag
|
* @param {Function} handleClickNarrowToTag
|
||||||
* @param {bool} isActive
|
* @param {boolean} isActive
|
||||||
* @param {bool} isRelated
|
* @param {boolean} isRelated
|
||||||
|
* @param {string} bgColor tab backgroundColor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count}) => (
|
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count, color}) => (
|
||||||
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
|
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
|
||||||
{isRelated
|
{isRelated
|
||||||
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
||||||
@@ -23,6 +24,7 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
|
|||||||
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
|
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
|
||||||
}
|
}
|
||||||
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
||||||
|
<span styleName='tagList-item-color' style={{backgroundColor: color || 'transparent'}} />
|
||||||
<span styleName='tagList-item-name'>
|
<span styleName='tagList-item-name'>
|
||||||
{`# ${name}`}
|
{`# ${name}`}
|
||||||
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
|
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
|
||||||
@@ -33,7 +35,8 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
|
|||||||
|
|
||||||
TagListItem.propTypes = {
|
TagListItem.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
handleClickTagListItem: PropTypes.func.isRequired
|
handleClickTagListItem: PropTypes.func.isRequired,
|
||||||
|
color: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CSSModules(TagListItem, styles)
|
export default CSSModules(TagListItem, styles)
|
||||||
|
|||||||
@@ -71,6 +71,11 @@
|
|||||||
padding-right 15px
|
padding-right 15px
|
||||||
font-size 13px
|
font-size 13px
|
||||||
|
|
||||||
|
.tagList-item-color
|
||||||
|
height 26px
|
||||||
|
width 3px
|
||||||
|
display inline-block
|
||||||
|
|
||||||
body[data-theme="white"]
|
body[data-theme="white"]
|
||||||
.tagList-item
|
.tagList-item
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|||||||
@@ -55,11 +55,14 @@ 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 .katex-html
|
||||||
|
display inline-flex
|
||||||
.katex .mfrac>.vlist>span:nth-child(2)
|
.katex .mfrac>.vlist>span:nth-child(2)
|
||||||
top 0 !important
|
top 0 !important
|
||||||
.katex-error
|
.katex-error
|
||||||
@@ -162,6 +165,7 @@ p
|
|||||||
white-space pre-line
|
white-space pre-line
|
||||||
word-wrap break-word
|
word-wrap break-word
|
||||||
img
|
img
|
||||||
|
cursor zoom-in
|
||||||
max-width 100%
|
max-width 100%
|
||||||
strong, b
|
strong, b
|
||||||
font-weight bold
|
font-weight bold
|
||||||
@@ -183,6 +187,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 +424,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 +503,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 +546,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 +582,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 +592,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 +628,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 +637,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
|
||||||
@@ -45,6 +46,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params,
|
|||||||
tags = params.tagname.split(' ')
|
tags = params.tagname.split(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultLanguage = config.editor.snippetDefaultLanguage === 'Auto Detect' ? null : config.editor.snippetDefaultLanguage
|
||||||
|
|
||||||
return dataApi
|
return dataApi
|
||||||
.createNote(storage, {
|
.createNote(storage, {
|
||||||
type: 'SNIPPET_NOTE',
|
type: 'SNIPPET_NOTE',
|
||||||
@@ -55,8 +58,9 @@ export function createSnippetNote (storage, folder, dispatch, location, params,
|
|||||||
snippets: [
|
snippets: [
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
mode: config.editor.snippetDefaultLanguage || 'text',
|
mode: defaultLanguage,
|
||||||
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(/\-+$/, '')
|
||||||
|
}
|
||||||
@@ -5,15 +5,17 @@ import styles from './FullscreenButton.styl'
|
|||||||
import i18n from 'browser/lib/i18n'
|
import i18n from 'browser/lib/i18n'
|
||||||
|
|
||||||
const OSX = global.process.platform === 'darwin'
|
const OSX = global.process.platform === 'darwin'
|
||||||
const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B'
|
|
||||||
const FullscreenButton = ({
|
const FullscreenButton = ({
|
||||||
onClick
|
onClick
|
||||||
}) => (
|
}) => {
|
||||||
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')} onMouseDown={(e) => onClick(e)}>
|
const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B'
|
||||||
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
|
return (
|
||||||
<span styleName='tooltip'>{i18n.__('Fullscreen')}({hotkey})</span>
|
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')} onMouseDown={(e) => onClick(e)}>
|
||||||
</button>
|
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
|
||||||
)
|
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Fullscreen')}({hotkey})</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
FullscreenButton.propTypes = {
|
FullscreenButton.propTypes = {
|
||||||
onClick: PropTypes.func.isRequired
|
onClick: PropTypes.func.isRequired
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
|
||||||
|
.tooltip:lang(ja)
|
||||||
|
@extend .tooltip
|
||||||
|
right 35px
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.control-fullScreenButton
|
.control-fullScreenButton
|
||||||
topBarButtonDark()
|
topBarButtonDark()
|
||||||
@@ -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,15 @@ 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: props.config.editor.type !== 'SPLIT',
|
||||||
isLocked: false,
|
isLocked: false,
|
||||||
editorType: props.config.editor.type
|
editorType: props.config.editor.type,
|
||||||
|
switchPreview: props.config.editor.switchPreview
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchTimer = null
|
this.dispatchTimer = null
|
||||||
|
|
||||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||||
@@ -63,6 +66,9 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
})
|
})
|
||||||
ee.on('hotkey:deletenote', this.handleDeleteNote.bind(this))
|
ee.on('hotkey:deletenote', this.handleDeleteNote.bind(this))
|
||||||
ee.on('code:generate-toc', this.generateToc)
|
ee.on('code:generate-toc', this.generateToc)
|
||||||
|
|
||||||
|
// Focus content if using blur or double click
|
||||||
|
if (this.state.switchPreview === 'BLUR' || this.state.switchPreview === 'DBL_CLICK') this.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps (nextProps) {
|
componentWillReceiveProps (nextProps) {
|
||||||
@@ -71,7 +77,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()
|
||||||
@@ -292,7 +298,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
|
|
||||||
handleToggleLockButton (event, noteStatus) {
|
handleToggleLockButton (event, noteStatus) {
|
||||||
// first argument event is not used
|
// first argument event is not used
|
||||||
if (this.props.config.editor.switchPreview === 'BLUR' && noteStatus === 'CODE') {
|
if (noteStatus === 'CODE') {
|
||||||
this.setState({isLockButtonShown: true})
|
this.setState({isLockButtonShown: true})
|
||||||
} else {
|
} else {
|
||||||
this.setState({isLockButtonShown: false})
|
this.setState({isLockButtonShown: false})
|
||||||
@@ -318,7 +324,8 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleSwitchMode (type) {
|
handleSwitchMode (type) {
|
||||||
this.setState({ editorType: type }, () => {
|
// If in split mode, hide the lock button
|
||||||
|
this.setState({ editorType: type, isLockButtonShown: !(type === 'SPLIT') }, () => {
|
||||||
this.focus()
|
this.focus()
|
||||||
const newConfig = Object.assign({}, this.props.config)
|
const newConfig = Object.assign({}, this.props.config)
|
||||||
newConfig.editor.type = type
|
newConfig.editor.type = type
|
||||||
@@ -361,7 +368,9 @@ 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)}
|
||||||
|
isLocked={this.state.isLocked}
|
||||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||||
/>
|
/>
|
||||||
} else {
|
} else {
|
||||||
@@ -371,6 +380,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}
|
||||||
/>
|
/>
|
||||||
@@ -433,6 +443,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={this.handleUpdateTag.bind(this)}
|
onChange={this.handleUpdateTag.bind(this)}
|
||||||
|
coloredTags={config.coloredTags}
|
||||||
/>
|
/>
|
||||||
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
|
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import _ from 'lodash'
|
|||||||
import {findNoteTitle} from 'browser/lib/findNoteTitle'
|
import {findNoteTitle} from 'browser/lib/findNoteTitle'
|
||||||
import convertModeName from 'browser/lib/convertModeName'
|
import convertModeName from 'browser/lib/convertModeName'
|
||||||
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||||
|
import FullscreenButton from './FullscreenButton'
|
||||||
import TrashButton from './TrashButton'
|
import TrashButton from './TrashButton'
|
||||||
import RestoreButton from './RestoreButton'
|
import RestoreButton from './RestoreButton'
|
||||||
import PermanentDeleteButton from './PermanentDeleteButton'
|
import PermanentDeleteButton from './PermanentDeleteButton'
|
||||||
@@ -48,7 +49,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 +77,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 +412,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
|
||||||
@@ -596,13 +600,16 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addSnippet () {
|
addSnippet () {
|
||||||
const { config } = this.props
|
const { config: { editor: { snippetDefaultLanguage } } } = this.props
|
||||||
const { note } = this.state
|
const { note } = this.state
|
||||||
|
|
||||||
|
const defaultLanguage = snippetDefaultLanguage === 'Auto Detect' ? null : snippetDefaultLanguage
|
||||||
|
|
||||||
note.snippets = note.snippets.concat([{
|
note.snippets = note.snippets.concat([{
|
||||||
name: '',
|
name: '',
|
||||||
mode: config.editor.snippetDefaultLanguage || 'text',
|
mode: defaultLanguage,
|
||||||
content: ''
|
content: '',
|
||||||
|
linesHighlighted: []
|
||||||
}])
|
}])
|
||||||
const snippetIndex = note.snippets.length - 1
|
const snippetIndex = note.snippets.length - 1
|
||||||
|
|
||||||
@@ -645,11 +652,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')]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -661,6 +675,8 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
const storageKey = note.storage
|
const storageKey = note.storage
|
||||||
const folderKey = note.folder
|
const folderKey = note.folder
|
||||||
|
|
||||||
|
const autoDetect = config.editor.snippetDefaultLanguage === 'Auto Detect'
|
||||||
|
|
||||||
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
|
||||||
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
||||||
@@ -685,10 +701,6 @@ 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))
|
|
||||||
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,26 +709,34 @@ 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}
|
||||||
storageKey={storageKey}
|
storageKey={storageKey}
|
||||||
/>
|
/>
|
||||||
: <CodeEditor styleName='tabView-content'
|
: <CodeEditor styleName='tabView-content'
|
||||||
mode={snippet.mode}
|
mode={snippet.mode || (autoDetect ? null : config.editor.snippetDefaultLanguage)}
|
||||||
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}
|
||||||
indentType={config.editor.indentType}
|
indentType={config.editor.indentType}
|
||||||
indentSize={editorIndentSize}
|
indentSize={editorIndentSize}
|
||||||
displayLineNumbers={config.editor.displayLineNumbers}
|
displayLineNumbers={config.editor.displayLineNumbers}
|
||||||
|
matchingPairs={config.editor.matchingPairs}
|
||||||
|
matchingTriples={config.editor.matchingTriples}
|
||||||
|
explodingPairs={config.editor.explodingPairs}
|
||||||
keyMap={config.editor.keyMap}
|
keyMap={config.editor.keyMap}
|
||||||
scrollPastEnd={config.editor.scrollPastEnd}
|
scrollPastEnd={config.editor.scrollPastEnd}
|
||||||
fetchUrlTitle={config.editor.fetchUrlTitle}
|
fetchUrlTitle={config.editor.fetchUrlTitle}
|
||||||
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}
|
||||||
|
autoDetect={autoDetect}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -772,6 +792,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={(e) => this.handleChange(e)}
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
coloredTags={config.coloredTags}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div styleName='info-right'>
|
<div styleName='info-right'>
|
||||||
@@ -780,11 +801,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
isActive={note.isStarred}
|
isActive={note.isStarred}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')}
|
<FullscreenButton onClick={(e) => this.handleFullScreenButton(e)} />
|
||||||
onMouseDown={(e) => this.handleFullScreenButton(e)}>
|
|
||||||
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
|
|
||||||
<span styleName='tooltip'>{i18n.__('Fullscreen')}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||||
|
|
||||||
@@ -800,7 +817,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
|
||||||
@@ -54,7 +54,7 @@ class StarButton extends React.Component {
|
|||||||
: '../resources/icon/icon-star.svg'
|
: '../resources/icon/icon-star.svg'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<span styleName='tooltip'>{i18n.__('Star')}</span>
|
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Star')}</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,11 @@
|
|||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
|
||||||
|
.tooltip:lang(ja)
|
||||||
|
@extend .tooltip
|
||||||
|
right 103px
|
||||||
|
width 70px
|
||||||
|
|
||||||
.root--active
|
.root--active
|
||||||
@extend .root
|
@extend .root
|
||||||
transition 0.15s
|
transition 0.15s
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import invertColor from 'invert-color'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './TagSelect.styl'
|
import styles from './TagSelect.styl'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
@@ -45,8 +46,14 @@ class TagSelect extends React.Component {
|
|||||||
value = _.isArray(value)
|
value = _.isArray(value)
|
||||||
? value.slice()
|
? value.slice()
|
||||||
: []
|
: []
|
||||||
value.push(newTag)
|
|
||||||
value = _.uniq(value)
|
if (!_.includes(value, newTag)) {
|
||||||
|
value.push(newTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.saveTagsAlphabetically) {
|
||||||
|
value = _.sortBy(value)
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
newTag: ''
|
newTag: ''
|
||||||
@@ -179,19 +186,34 @@ class TagSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { value, className, showTagsAlphabetically } = this.props
|
const { value, className, showTagsAlphabetically, coloredTags } = this.props
|
||||||
|
|
||||||
const tagList = _.isArray(value)
|
const tagList = _.isArray(value)
|
||||||
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
|
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
|
||||||
|
const wrapperStyle = {}
|
||||||
|
const textStyle = {}
|
||||||
|
const BLACK = '#333333'
|
||||||
|
const WHITE = '#f1f1f1'
|
||||||
|
const color = coloredTags[tag]
|
||||||
|
const invertedColor = color && invertColor(color, { black: BLACK, white: WHITE })
|
||||||
|
let iconRemove = '../resources/icon/icon-x.svg'
|
||||||
|
if (color) {
|
||||||
|
wrapperStyle.backgroundColor = color
|
||||||
|
textStyle.color = invertedColor
|
||||||
|
}
|
||||||
|
if (invertedColor === WHITE) {
|
||||||
|
iconRemove = '../resources/icon/icon-x-light.svg'
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<span styleName='tag'
|
<span styleName='tag'
|
||||||
key={tag}
|
key={tag}
|
||||||
|
style={wrapperStyle}
|
||||||
>
|
>
|
||||||
<span styleName='tag-label' onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
|
<span styleName='tag-label' style={textStyle} onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
|
||||||
<button styleName='tag-removeButton'
|
<button styleName='tag-removeButton'
|
||||||
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
|
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
|
||||||
>
|
>
|
||||||
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
|
<img className='tag-removeButton-icon' src={iconRemove} width='8px' />
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
@@ -240,7 +262,8 @@ TagSelect.contextTypes = {
|
|||||||
TagSelect.propTypes = {
|
TagSelect.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
value: PropTypes.arrayOf(PropTypes.string),
|
value: PropTypes.arrayOf(PropTypes.string),
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func,
|
||||||
|
coloredTags: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CSSModules(TagSelect, styles)
|
export default CSSModules(TagSelect, styles)
|
||||||
|
|||||||
@@ -3,19 +3,18 @@
|
|||||||
align-items center
|
align-items center
|
||||||
user-select none
|
user-select none
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
width 100%
|
width 96%
|
||||||
overflow-x scroll
|
overflow-x auto
|
||||||
white-space nowrap
|
white-space nowrap
|
||||||
margin-top 31px
|
top 50px
|
||||||
position absolute
|
position absolute
|
||||||
|
&::-webkit-scrollbar
|
||||||
.root::-webkit-scrollbar
|
height 8px
|
||||||
display none
|
|
||||||
|
|
||||||
.tag
|
.tag
|
||||||
display flex
|
display flex
|
||||||
align-items center
|
align-items center
|
||||||
margin 0px 2px
|
margin 0px 2px 2px
|
||||||
padding 2px 4px
|
padding 2px 4px
|
||||||
background-color alpha($ui-tag-backgroundColor, 3%)
|
background-color alpha($ui-tag-backgroundColor, 3%)
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const ToggleModeButton = ({
|
|||||||
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
|
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
|
||||||
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
|
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
|
||||||
</div>
|
</div>
|
||||||
<span styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
|
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,11 @@
|
|||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
|
||||||
|
.tooltip:lang(ja)
|
||||||
|
@extend .tooltip
|
||||||
|
left -8px
|
||||||
|
width 70px
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.control-fullScreenButton
|
.control-fullScreenButton
|
||||||
topBarButtonDark()
|
topBarButtonDark()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const TrashButton = ({
|
|||||||
onClick={(e) => onClick(e)}
|
onClick={(e) => onClick(e)}
|
||||||
>
|
>
|
||||||
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
|
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
|
||||||
<span styleName='tooltip'>{i18n.__('Trash')}</span>
|
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Trash')}</span>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
opacity 0
|
opacity 0
|
||||||
transition 0.1s
|
transition 0.1s
|
||||||
|
|
||||||
|
.tooltip:lang(ja)
|
||||||
|
@extend .tooltip
|
||||||
|
right 46px
|
||||||
|
|
||||||
.control-trashButton--in-trash
|
.control-trashButton--in-trash
|
||||||
top 60px
|
top 60px
|
||||||
topBarButtonRight()
|
topBarButtonRight()
|
||||||
|
|||||||
@@ -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: []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
@@ -170,10 +172,21 @@ class Main extends React.Component {
|
|||||||
delete CodeMirror.keyMap.emacs['Ctrl-V']
|
delete CodeMirror.keyMap.emacs['Ctrl-V']
|
||||||
|
|
||||||
eventEmitter.on('editor:fullscreen', this.toggleFullScreen)
|
eventEmitter.on('editor:fullscreen', this.toggleFullScreen)
|
||||||
|
eventEmitter.on('menubar:togglemenubar', this.toggleMenuBarVisible.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
eventEmitter.off('editor:fullscreen', this.toggleFullScreen)
|
eventEmitter.off('editor:fullscreen', this.toggleFullScreen)
|
||||||
|
eventEmitter.off('menubar:togglemenubar', this.toggleMenuBarVisible.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMenuBarVisible () {
|
||||||
|
const { config } = this.props
|
||||||
|
const { ui } = config
|
||||||
|
|
||||||
|
const newUI = Object.assign(ui, {showMenuBar: !ui.showMenuBar})
|
||||||
|
const newConfig = Object.assign(config, newUI)
|
||||||
|
ConfigManager.set(newConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeftSlideMouseDown (e) {
|
handleLeftSlideMouseDown (e) {
|
||||||
@@ -234,8 +247,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')]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,14 +656,18 @@ class NoteList extends React.Component {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
data.forEach((item) => {
|
const dispatchHandler = () => {
|
||||||
dispatch({
|
data.forEach((item) => {
|
||||||
type: 'DELETE_NOTE',
|
dispatch({
|
||||||
storageKey: item.storageKey,
|
type: 'DELETE_NOTE',
|
||||||
noteKey: item.noteKey
|
storageKey: item.storageKey,
|
||||||
|
noteKey: item.noteKey
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
ee.once('list:next', dispatchHandler)
|
||||||
})
|
})
|
||||||
|
.then(() => ee.emit('list:next'))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('Cannot Delete note: ' + err)
|
console.error('Cannot Delete note: ' + err)
|
||||||
})
|
})
|
||||||
@@ -683,6 +691,7 @@ class NoteList extends React.Component {
|
|||||||
})
|
})
|
||||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
|
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
|
||||||
})
|
})
|
||||||
|
.then(() => ee.emit('list:next'))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error('Notes could not go to trash: ' + err)
|
console.error('Notes could not go to trash: ' + err)
|
||||||
})
|
})
|
||||||
@@ -706,7 +715,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)
|
||||||
@@ -1042,6 +1052,7 @@ class NoteList extends React.Component {
|
|||||||
storageName={this.getNoteStorage(note).name}
|
storageName={this.getNoteStorage(note).name}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||||
|
coloredTags={config.coloredTags}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1124,4 +1135,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)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import i18n from 'browser/lib/i18n'
|
|||||||
import context from 'browser/lib/context'
|
import context from 'browser/lib/context'
|
||||||
import { remote } from 'electron'
|
import { remote } from 'electron'
|
||||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||||
|
import ColorPicker from 'browser/components/ColorPicker'
|
||||||
|
|
||||||
function matchActiveTags (tags, activeTags) {
|
function matchActiveTags (tags, activeTags) {
|
||||||
return _.every(activeTags, v => tags.indexOf(v) >= 0)
|
return _.every(activeTags, v => tags.indexOf(v) >= 0)
|
||||||
@@ -27,6 +28,22 @@ function matchActiveTags (tags, activeTags) {
|
|||||||
|
|
||||||
class SideNav extends React.Component {
|
class SideNav extends React.Component {
|
||||||
// TODO: should not use electron stuff v0.7
|
// TODO: should not use electron stuff v0.7
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
colorPicker: {
|
||||||
|
show: false,
|
||||||
|
color: null,
|
||||||
|
tagName: null,
|
||||||
|
targetRect: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dismissColorPicker = this.dismissColorPicker.bind(this)
|
||||||
|
this.handleColorPickerConfirm = this.handleColorPickerConfirm.bind(this)
|
||||||
|
this.handleColorPickerReset = this.handleColorPickerReset.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
|
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
|
||||||
@@ -104,9 +121,64 @@ class SideNav extends React.Component {
|
|||||||
click: this.deleteTag.bind(this, tag)
|
click: this.deleteTag.bind(this, tag)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
menu.push({
|
||||||
|
label: i18n.__('Customize Color'),
|
||||||
|
click: this.displayColorPicker.bind(this, tag, e.target.getBoundingClientRect())
|
||||||
|
})
|
||||||
|
|
||||||
context.popup(menu)
|
context.popup(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dismissColorPicker () {
|
||||||
|
this.setState({
|
||||||
|
colorPicker: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
displayColorPicker (tagName, rect) {
|
||||||
|
const { config } = this.props
|
||||||
|
this.setState({
|
||||||
|
colorPicker: {
|
||||||
|
show: true,
|
||||||
|
color: config.coloredTags[tagName],
|
||||||
|
tagName,
|
||||||
|
targetRect: rect
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleColorPickerConfirm (color) {
|
||||||
|
const { dispatch, config: {coloredTags} } = this.props
|
||||||
|
const { colorPicker: { tagName } } = this.state
|
||||||
|
const newColoredTags = Object.assign({}, coloredTags, {[tagName]: color.hex})
|
||||||
|
|
||||||
|
const config = { coloredTags: newColoredTags }
|
||||||
|
ConfigManager.set(config)
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_CONFIG',
|
||||||
|
config
|
||||||
|
})
|
||||||
|
this.dismissColorPicker()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleColorPickerReset () {
|
||||||
|
const { dispatch, config: {coloredTags} } = this.props
|
||||||
|
const { colorPicker: { tagName } } = this.state
|
||||||
|
const newColoredTags = Object.assign({}, coloredTags)
|
||||||
|
|
||||||
|
delete newColoredTags[tagName]
|
||||||
|
|
||||||
|
const config = { coloredTags: newColoredTags }
|
||||||
|
ConfigManager.set(config)
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_CONFIG',
|
||||||
|
config
|
||||||
|
})
|
||||||
|
this.dismissColorPicker()
|
||||||
|
}
|
||||||
|
|
||||||
handleToggleButtonClick (e) {
|
handleToggleButtonClick (e) {
|
||||||
const { dispatch, config } = this.props
|
const { dispatch, config } = this.props
|
||||||
|
|
||||||
@@ -207,6 +279,7 @@ class SideNav extends React.Component {
|
|||||||
|
|
||||||
tagListComponent () {
|
tagListComponent () {
|
||||||
const { data, location, config } = this.props
|
const { data, location, config } = this.props
|
||||||
|
const { colorPicker } = this.state
|
||||||
const activeTags = this.getActiveTags(location.pathname)
|
const activeTags = this.getActiveTags(location.pathname)
|
||||||
const relatedTags = this.getRelatedTags(activeTags, data.noteMap)
|
const relatedTags = this.getRelatedTags(activeTags, data.noteMap)
|
||||||
let tagList = _.sortBy(data.tagNoteMap.map(
|
let tagList = _.sortBy(data.tagNoteMap.map(
|
||||||
@@ -237,10 +310,11 @@ class SideNav extends React.Component {
|
|||||||
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
||||||
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
||||||
handleContextMenu={this.handleTagContextMenu.bind(this)}
|
handleContextMenu={this.handleTagContextMenu.bind(this)}
|
||||||
isActive={this.getTagActive(location.pathname, tag.name)}
|
isActive={this.getTagActive(location.pathname, tag.name) || (colorPicker.tagName === tag.name)}
|
||||||
isRelated={tag.related}
|
isRelated={tag.related}
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
count={tag.size}
|
count={tag.size}
|
||||||
|
color={config.coloredTags[tag.name]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -333,6 +407,7 @@ class SideNav extends React.Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { data, location, config, dispatch } = this.props
|
const { data, location, config, dispatch } = this.props
|
||||||
|
const { colorPicker: colorPickerState } = this.state
|
||||||
|
|
||||||
const isFolded = config.isSideNavFolded
|
const isFolded = config.isSideNavFolded
|
||||||
|
|
||||||
@@ -349,6 +424,20 @@ class SideNav extends React.Component {
|
|||||||
useDragHandle
|
useDragHandle
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let colorPicker
|
||||||
|
if (colorPickerState.show) {
|
||||||
|
colorPicker = (
|
||||||
|
<ColorPicker
|
||||||
|
color={colorPickerState.color}
|
||||||
|
targetRect={colorPickerState.targetRect}
|
||||||
|
onConfirm={this.handleColorPickerConfirm}
|
||||||
|
onCancel={this.dismissColorPicker}
|
||||||
|
onReset={this.handleColorPickerReset}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const style = {}
|
const style = {}
|
||||||
if (!isFolded) style.width = this.props.width
|
if (!isFolded) style.width = this.props.width
|
||||||
const isTagActive = location.pathname.match(/tag/)
|
const isTagActive = location.pathname.match(/tag/)
|
||||||
@@ -368,6 +457,7 @@ class SideNav extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.SideNavComponent(isFolded, storageList)}
|
{this.SideNavComponent(isFolded, storageList)}
|
||||||
|
{colorPicker}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ modalBackColor = white
|
|||||||
|
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
|
background-color $ui-dark-backgroundColor
|
||||||
::-webkit-scrollbar-thumb
|
::-webkit-scrollbar-thumb
|
||||||
background-color rgba(0, 0, 0, 0.3)
|
background-color rgba(0, 0, 0, 0.3)
|
||||||
.ModalBase
|
.ModalBase
|
||||||
@@ -148,6 +149,7 @@ body[data-theme="dark"]
|
|||||||
z-index modalZIndex + 5
|
z-index modalZIndex + 5
|
||||||
|
|
||||||
body[data-theme="solarized-dark"]
|
body[data-theme="solarized-dark"]
|
||||||
|
background-color $ui-solarized-dark-backgroundColor
|
||||||
::-webkit-scrollbar-thumb
|
::-webkit-scrollbar-thumb
|
||||||
background-color rgba(0, 0, 0, 0.3)
|
background-color rgba(0, 0, 0, 0.3)
|
||||||
.ModalBase
|
.ModalBase
|
||||||
@@ -157,6 +159,7 @@ body[data-theme="solarized-dark"]
|
|||||||
color: $ui-solarized-dark-text-color
|
color: $ui-solarized-dark-text-color
|
||||||
|
|
||||||
body[data-theme="monokai"]
|
body[data-theme="monokai"]
|
||||||
|
background-color $ui-monokai-backgroundColor
|
||||||
::-webkit-scrollbar-thumb
|
::-webkit-scrollbar-thumb
|
||||||
background-color rgba(0, 0, 0, 0.3)
|
background-color rgba(0, 0, 0, 0.3)
|
||||||
.ModalBase
|
.ModalBase
|
||||||
@@ -166,6 +169,7 @@ body[data-theme="monokai"]
|
|||||||
color: $ui-monokai-text-color
|
color: $ui-monokai-text-color
|
||||||
|
|
||||||
body[data-theme="dracula"]
|
body[data-theme="dracula"]
|
||||||
|
background-color $ui-dracula-backgroundColor
|
||||||
::-webkit-scrollbar-thumb
|
::-webkit-scrollbar-thumb
|
||||||
background-color rgba(0, 0, 0, 0.3)
|
background-color rgba(0, 0, 0, 0.3)
|
||||||
.ModalBase
|
.ModalBase
|
||||||
|
|||||||
@@ -25,25 +25,31 @@ 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',
|
||||||
|
toggleMenuBar: 'Alt'
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
language: 'en',
|
language: 'en',
|
||||||
theme: 'default',
|
theme: 'default',
|
||||||
showCopyNotification: true,
|
showCopyNotification: true,
|
||||||
disableDirectWrite: false,
|
disableDirectWrite: false,
|
||||||
defaultNote: 'ALWAYS_ASK' // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE'
|
defaultNote: 'ALWAYS_ASK', // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE'
|
||||||
|
showMenuBar: false
|
||||||
},
|
},
|
||||||
editor: {
|
editor: {
|
||||||
theme: 'base16-light',
|
theme: 'base16-light',
|
||||||
keyMap: 'sublime',
|
keyMap: 'sublime',
|
||||||
fontSize: '14',
|
fontSize: '14',
|
||||||
fontFamily: win ? 'Segoe UI' : 'Monaco, Consolas',
|
fontFamily: win ? 'Consolas' : 'Monaco',
|
||||||
indentType: 'space',
|
indentType: 'space',
|
||||||
indentSize: '2',
|
indentSize: '2',
|
||||||
enableRulers: false,
|
enableRulers: false,
|
||||||
rulers: [80, 120],
|
rulers: [80, 120],
|
||||||
displayLineNumbers: true,
|
displayLineNumbers: true,
|
||||||
|
matchingPairs: '()[]{}\'\'""$$**``',
|
||||||
|
matchingTriples: '```"""\'\'\'',
|
||||||
|
explodingPairs: '[]{}``$$',
|
||||||
switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK'
|
switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK'
|
||||||
delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE'
|
delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE'
|
||||||
scrollPastEnd: false,
|
scrollPastEnd: false,
|
||||||
@@ -52,7 +58,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',
|
||||||
@@ -81,7 +88,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
token: '',
|
token: '',
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: ''
|
||||||
}
|
},
|
||||||
|
coloredTags: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validate (config) {
|
function validate (config) {
|
||||||
@@ -203,7 +211,7 @@ function assignConfigValues (originalConfig, rcConfig) {
|
|||||||
function rewriteHotkey (config) {
|
function rewriteHotkey (config) {
|
||||||
const keys = [...Object.keys(config.hotkey)]
|
const keys = [...Object.keys(config.hotkey)]
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
config.hotkey[key] = config.hotkey[key].replace(/Cmd/g, 'Command')
|
config.hotkey[key] = config.hotkey[key].replace(/Cmd\s/g, 'Command ')
|
||||||
config.hotkey[key] = config.hotkey[key].replace(/Opt\s/g, 'Option ')
|
config.hotkey[key] = config.hotkey[key].replace(/Opt\s/g, 'Option ')
|
||||||
})
|
})
|
||||||
return config
|
return config
|
||||||
|
|||||||
@@ -241,7 +241,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))
|
||||||
})
|
})
|
||||||
@@ -270,7 +278,7 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) {
|
|||||||
let promise
|
let promise
|
||||||
if (dropEvent.dataTransfer.files.length > 0) {
|
if (dropEvent.dataTransfer.files.length > 0) {
|
||||||
promise = Promise.all(Array.from(dropEvent.dataTransfer.files).map(file => {
|
promise = Promise.all(Array.from(dropEvent.dataTransfer.files).map(file => {
|
||||||
if (file['type'].startsWith('image')) {
|
if (file['type'].startsWith('image') && !file['type'].endsWith('gif')) {
|
||||||
return fixRotate(file)
|
return fixRotate(file)
|
||||||
.then(data => copyAttachment({type: 'base64', data: data, sourceFilePath: file.path}, storageKey, noteKey)
|
.then(data => copyAttachment({type: 'base64', data: data, sourceFilePath: file.path}, storageKey, noteKey)
|
||||||
.then(fileName => ({
|
.then(fileName => ({
|
||||||
@@ -330,7 +338,7 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) {
|
|||||||
* @param {String} noteKey Key of the current note
|
* @param {String} noteKey Key of the current note
|
||||||
* @param {DataTransferItem} dataTransferItem Part of the past-event
|
* @param {DataTransferItem} dataTransferItem Part of the past-event
|
||||||
*/
|
*/
|
||||||
function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) {
|
function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) {
|
||||||
if (!codeEditor) {
|
if (!codeEditor) {
|
||||||
throw new Error('codeEditor has to be given')
|
throw new Error('codeEditor has to be given')
|
||||||
}
|
}
|
||||||
@@ -367,6 +375,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 handlePasteNativeImage (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
|
||||||
@@ -434,7 +480,14 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) {
|
|||||||
* @returns {String} Input without the references
|
* @returns {String} Input without the references
|
||||||
*/
|
*/
|
||||||
function removeStorageAndNoteReferences (input, noteKey) {
|
function removeStorageAndNoteReferences (input, noteKey) {
|
||||||
return input.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER)
|
return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) {
|
||||||
|
const temp = match
|
||||||
|
.replace(new RegExp(mdurl.encode(path.win32.sep), 'g'), path.sep)
|
||||||
|
.replace(new RegExp(mdurl.encode(path.posix.sep), 'g'), path.sep)
|
||||||
|
.replace(new RegExp(escapeStringRegexp(path.win32.sep), 'g'), path.sep)
|
||||||
|
.replace(new RegExp(escapeStringRegexp(path.posix.sep), 'g'), path.sep)
|
||||||
|
return temp.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -589,7 +642,8 @@ module.exports = {
|
|||||||
fixLocalURLS,
|
fixLocalURLS,
|
||||||
generateAttachmentMarkdown,
|
generateAttachmentMarkdown,
|
||||||
handleAttachmentDrop,
|
handleAttachmentDrop,
|
||||||
handlePastImageEvent,
|
handlePasteImageEvent,
|
||||||
|
handlePasteNativeImage,
|
||||||
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)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const resolveStorageData = require('./resolveStorageData')
|
|||||||
const resolveStorageNotes = require('./resolveStorageNotes')
|
const resolveStorageNotes = require('./resolveStorageNotes')
|
||||||
const consts = require('browser/lib/consts')
|
const consts = require('browser/lib/consts')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const fs = require('fs')
|
||||||
const CSON = require('@rokt33r/season')
|
const CSON = require('@rokt33r/season')
|
||||||
/**
|
/**
|
||||||
* @return {Object} all storages and notes
|
* @return {Object} all storages and notes
|
||||||
@@ -19,11 +20,14 @@ const CSON = require('@rokt33r/season')
|
|||||||
* 2. legacy
|
* 2. legacy
|
||||||
* 3. empty directory
|
* 3. empty directory
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function init () {
|
function init () {
|
||||||
const fetchStorages = function () {
|
const fetchStorages = function () {
|
||||||
let rawStorages
|
let rawStorages
|
||||||
try {
|
try {
|
||||||
rawStorages = JSON.parse(window.localStorage.getItem('storages'))
|
rawStorages = JSON.parse(window.localStorage.getItem('storages'))
|
||||||
|
// Remove storages who's location is inaccesible.
|
||||||
|
rawStorages = rawStorages.filter(storage => fs.existsSync(storage.path))
|
||||||
if (!_.isArray(rawStorages)) throw new Error('Cached data is not valid.')
|
if (!_.isArray(rawStorages)) throw new Error('Cached data is not valid.')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Failed to parse cached data from localStorage', e)
|
console.warn('Failed to parse cached data from localStorage', e)
|
||||||
@@ -36,6 +40,7 @@ function init () {
|
|||||||
|
|
||||||
const fetchNotes = function (storages) {
|
const fetchNotes = function (storages) {
|
||||||
const findNotesFromEachStorage = storages
|
const findNotesFromEachStorage = storages
|
||||||
|
.filter(storage => fs.existsSync(storage.path))
|
||||||
.map((storage) => {
|
.map((storage) => {
|
||||||
return resolveStorageNotes(storage)
|
return resolveStorageNotes(storage)
|
||||||
.then((notes) => {
|
.then((notes) => {
|
||||||
@@ -51,7 +56,11 @@ function init () {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (unknownCount > 0) {
|
if (unknownCount > 0) {
|
||||||
CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version']))
|
try {
|
||||||
|
CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version']))
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Error writting boostnote.json: ' + e + ' from init.js')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return notes
|
return notes
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -6,5 +6,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
'deleteNote': () => {
|
'deleteNote': () => {
|
||||||
ee.emit('hotkey:deletenote')
|
ee.emit('hotkey:deletenote')
|
||||||
|
},
|
||||||
|
'toggleMenuBar': () => {
|
||||||
|
ee.emit('menubar:togglemenubar')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class Crowdfunding extends React.Component {
|
|||||||
<p>{i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}</p>
|
<p>{i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}</p>
|
||||||
<br />
|
<br />
|
||||||
<p>{i18n.__('### We believe Meritocracy')}</p>
|
<p>{i18n.__('### We believe Meritocracy')}</p>
|
||||||
<p>{i18n.__('We think developers who has skill and did great things must be rewarded properly.')}</p>
|
<p>{i18n.__('We think developers who have skills and do great things must be rewarded properly.')}</p>
|
||||||
<p>{i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}</p>
|
<p>{i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}</p>
|
||||||
<p>{i18n.__('It sometimes looks like exploitation.')}</p>
|
<p>{i18n.__('It sometimes looks like exploitation.')}</p>
|
||||||
<p>{i18n.__('We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.')}</p>
|
<p>{i18n.__('We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.')}</p>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
.folderItem-right-button
|
.folderItem-right-button
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
height 25px
|
height 25px
|
||||||
margin-top 2.5px
|
margin-top 2px
|
||||||
colorDefaultButton()
|
colorDefaultButton()
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
border $ui-border
|
border $ui-border
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ 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,
|
||||||
|
toggleMenuBar: this.refs.toggleMenuBar.value
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
config
|
config
|
||||||
@@ -127,6 +129,17 @@ class HotkeyTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>{i18n.__('Show/Hide Menu Bar')}</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
onChange={(e) => this.handleHotkeyChange(e)}
|
||||||
|
ref='toggleMenuBar'
|
||||||
|
value={config.hotkey.toggleMenuBar}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div styleName='group-section'>
|
<div styleName='group-section'>
|
||||||
<div styleName='group-section-label'>{i18n.__('Toggle Editor Mode')}</div>
|
<div styleName='group-section-label'>{i18n.__('Toggle Editor Mode')}</div>
|
||||||
<div styleName='group-section-control'>
|
<div styleName='group-section-control'>
|
||||||
@@ -149,6 +162,17 @@ class HotkeyTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>{i18n.__('Paste HTML')}</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)}
|
||||||
@@ -129,7 +134,7 @@ class InfoTab extends React.Component {
|
|||||||
>{i18n.__('Development')}</a>{i18n.__(' : Development configurations for Boostnote.')}
|
>{i18n.__('Development')}</a>{i18n.__(' : Development configurations for Boostnote.')}
|
||||||
</li>
|
</li>
|
||||||
<li styleName='cc'>
|
<li styleName='cc'>
|
||||||
{i18n.__('Copyright (C) 2017 - 2018 BoostIO')}
|
{i18n.__('Copyright (C) 2017 - 2019 BoostIO')}
|
||||||
</li>
|
</li>
|
||||||
<li styleName='cc'>
|
<li styleName='cc'>
|
||||||
{i18n.__('License: GPL v3')}
|
{i18n.__('License: GPL v3')}
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ class SnippetEditor extends React.Component {
|
|||||||
foldGutter: true,
|
foldGutter: true,
|
||||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||||
autoCloseBrackets: {
|
autoCloseBrackets: {
|
||||||
pairs: '()[]{}\'\'""$$**``',
|
pairs: this.props.matchingPairs,
|
||||||
triples: '```"""\'\'\'',
|
triples: this.props.matchingTriples,
|
||||||
explode: '[]{}``$$',
|
explode: this.props.explodingPairs,
|
||||||
override: true
|
override: true
|
||||||
},
|
},
|
||||||
mode: 'null'
|
mode: 'null'
|
||||||
|
|||||||
@@ -136,6 +136,9 @@ class SnippetTab extends React.Component {
|
|||||||
enableRulers={config.editor.enableRulers}
|
enableRulers={config.editor.enableRulers}
|
||||||
rulers={config.editor.rulers}
|
rulers={config.editor.rulers}
|
||||||
displayLineNumbers={config.editor.displayLineNumbers}
|
displayLineNumbers={config.editor.displayLineNumbers}
|
||||||
|
matchingPairs={config.editor.matchingPairs}
|
||||||
|
matchingTriples={config.editor.matchingTriples}
|
||||||
|
explodingPairs={config.editor.explodingPairs}
|
||||||
scrollPastEnd={config.editor.scrollPastEnd}
|
scrollPastEnd={config.editor.scrollPastEnd}
|
||||||
onRef={ref => { this.snippetEditor = ref }} />
|
onRef={ref => { this.snippetEditor = ref }} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class UiTab extends React.Component {
|
|||||||
showTagsAlphabetically: this.refs.showTagsAlphabetically.checked,
|
showTagsAlphabetically: this.refs.showTagsAlphabetically.checked,
|
||||||
saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked,
|
saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked,
|
||||||
enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked,
|
enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked,
|
||||||
|
showMenuBar: this.refs.showMenuBar.checked,
|
||||||
disableDirectWrite: this.refs.uiD2w != null
|
disableDirectWrite: this.refs.uiD2w != null
|
||||||
? this.refs.uiD2w.checked
|
? this.refs.uiD2w.checked
|
||||||
: false
|
: false
|
||||||
@@ -96,7 +97,11 @@ 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
|
matchingPairs: this.refs.matchingPairs.value,
|
||||||
|
matchingTriples: this.refs.matchingTriples.value,
|
||||||
|
explodingPairs: this.refs.explodingPairs.value,
|
||||||
|
spellcheck: this.refs.spellcheck.checked,
|
||||||
|
enableSmartPaste: this.refs.enableSmartPaste.checked
|
||||||
},
|
},
|
||||||
preview: {
|
preview: {
|
||||||
fontSize: this.refs.previewFontSize.value,
|
fontSize: this.refs.previewFontSize.value,
|
||||||
@@ -234,6 +239,16 @@ class UiTab extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div styleName='group-checkBoxSection'>
|
||||||
|
<label>
|
||||||
|
<input onChange={(e) => this.handleUIChange(e)}
|
||||||
|
checked={this.state.config.ui.showMenuBar}
|
||||||
|
ref='showMenuBar'
|
||||||
|
type='checkbox'
|
||||||
|
/>
|
||||||
|
{i18n.__('Show menu bar')}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div styleName='group-checkBoxSection'>
|
<div styleName='group-checkBoxSection'>
|
||||||
<label>
|
<label>
|
||||||
<input onChange={(e) => this.handleUIChange(e)}
|
<input onChange={(e) => this.handleUIChange(e)}
|
||||||
@@ -477,6 +492,7 @@ class UiTab extends React.Component {
|
|||||||
ref='editorSnippetDefaultLanguage'
|
ref='editorSnippetDefaultLanguage'
|
||||||
onChange={(e) => this.handleUIChange(e)}
|
onChange={(e) => this.handleUIChange(e)}
|
||||||
>
|
>
|
||||||
|
<option key='Auto Detect' value='Auto Detect'>Auto Detect</option>
|
||||||
{
|
{
|
||||||
_.sortBy(CodeMirror.modeInfo.map(mode => mode.name)).map(name => (<option key={name} value={name}>{name}</option>))
|
_.sortBy(CodeMirror.modeInfo.map(mode => mode.name)).map(name => (<option key={name} value={name}>{name}</option>))
|
||||||
}
|
}
|
||||||
@@ -552,6 +568,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 HTML 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)}
|
||||||
@@ -563,6 +591,48 @@ class UiTab extends React.Component {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
{i18n.__('Matching character pairs')}
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
value={this.state.config.editor.matchingPairs}
|
||||||
|
ref='matchingPairs'
|
||||||
|
onChange={(e) => this.handleUIChange(e)}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
{i18n.__('Matching character triples')}
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
value={this.state.config.editor.matchingTriples}
|
||||||
|
ref='matchingTriples'
|
||||||
|
onChange={(e) => this.handleUIChange(e)}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div styleName='group-section'>
|
||||||
|
<div styleName='group-section-label'>
|
||||||
|
{i18n.__('Exploding character pairs')}
|
||||||
|
</div>
|
||||||
|
<div styleName='group-section-control'>
|
||||||
|
<input styleName='group-section-control-input'
|
||||||
|
value={this.state.config.editor.explodingPairs}
|
||||||
|
ref='explodingPairs'
|
||||||
|
onChange={(e) => this.handleUIChange(e)}
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div styleName='group-header2'>{i18n.__('Preview')}</div>
|
<div styleName='group-header2'>{i18n.__('Preview')}</div>
|
||||||
<div styleName='group-section'>
|
<div styleName='group-section'>
|
||||||
<div styleName='group-section-label'>
|
<div styleName='group-section-label'>
|
||||||
@@ -590,6 +660,7 @@ class UiTab extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div styleName='group-section'>
|
<div styleName='group-section'>
|
||||||
<div styleName='group-section-label'>{i18n.__('Code Block Theme')}</div>
|
<div styleName='group-section-label'>{i18n.__('Code Block Theme')}</div>
|
||||||
<div styleName='group-section-control'>
|
<div styleName='group-section-control'>
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
border none
|
border none
|
||||||
background-color transparent
|
background-color transparent
|
||||||
outline none
|
outline none
|
||||||
padding 0 4px
|
padding 2px 4px
|
||||||
|
margin 0px 2px 2px
|
||||||
font-size 13px
|
font-size 13px
|
||||||
|
height 23px
|
||||||
|
|
||||||
ul
|
ul
|
||||||
position fixed
|
position fixed
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# Build
|
# Build
|
||||||
This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
|
||||||
|
This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [Portuguese](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
||||||
|
|
||||||
## Environments
|
## Environments
|
||||||
|
|
||||||
* npm: 6.x
|
- npm: 6.x
|
||||||
* node: 8.x
|
- node: 8.x
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -24,10 +25,40 @@ $ yarn run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
> ### Notice
|
> ### Notice
|
||||||
|
>
|
||||||
> There are some cases where you have to refresh the app manually.
|
> There are some cases where you have to refresh the app manually.
|
||||||
|
>
|
||||||
> 1. When editing a constructor method of a component
|
> 1. When editing a constructor method of a component
|
||||||
> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.)
|
> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.)
|
||||||
|
|
||||||
|
## Accessing code used in Pull Requests
|
||||||
|
Visit the page for the pull request and look at the end of the url for the PR number
|
||||||
|
<pre>
|
||||||
|
https://github.com/BoostIO/Boostnote/pull/2794
|
||||||
|
</pre>
|
||||||
|
In the following, replace \<PR> with that number (no brackets).
|
||||||
|
For the above url, you would replace \<PR> with 2794
|
||||||
|
|
||||||
|
_If you do not have a local copy of the master branch yet_
|
||||||
|
```
|
||||||
|
git clone https://github.com/BoostIO/Boostnote.git
|
||||||
|
cd Boostnote
|
||||||
|
git fetch origin pull/<PR>/head:<PR>
|
||||||
|
git checkout <PR>
|
||||||
|
```
|
||||||
|
|
||||||
|
_If you already have the master branch_
|
||||||
|
```
|
||||||
|
git fetch origin pull/<PR>/head:<PR>
|
||||||
|
git checkout <PR>
|
||||||
|
```
|
||||||
|
|
||||||
|
_To compile and run the code_
|
||||||
|
```
|
||||||
|
yarn
|
||||||
|
yarn run dev
|
||||||
|
```
|
||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
We use Grunt to automate deployment.
|
We use Grunt to automate deployment.
|
||||||
@@ -51,7 +82,6 @@ Distribution packages are created by exec `grunt build` on Linux platform (e.g.
|
|||||||
|
|
||||||
After installing the supported version of `node` and `npm`, install build dependency packages.
|
After installing the supported version of `node` and `npm`, install build dependency packages.
|
||||||
|
|
||||||
|
|
||||||
Ubuntu/Debian:
|
Ubuntu/Debian:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# Build
|
# Build
|
||||||
Diese Seite ist auch verfügbar in [Japanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Koreanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Vereinfachtem Chinesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [Französisch](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) und [Deutsch](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
|
||||||
|
Diese Seite ist auch verfügbar in [Japanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Koreanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Vereinfachtem Chinesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [Französisch](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [Portugiesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) und [Deutsch](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
||||||
|
|
||||||
## Umgebungen
|
## Umgebungen
|
||||||
|
|
||||||
* npm: 6.x
|
- npm: 6.x
|
||||||
* node: 8.x
|
- node: 8.x
|
||||||
|
|
||||||
## Entwicklung
|
## Entwicklung
|
||||||
|
|
||||||
@@ -24,7 +25,9 @@ $ yarn run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
> ### Notiz
|
> ### Notiz
|
||||||
|
>
|
||||||
> Es gibt einige Fälle bei denen die App manuell zu refreshen ist.
|
> Es gibt einige Fälle bei denen die App manuell zu refreshen ist.
|
||||||
|
>
|
||||||
> 1. Wenn eine "constructor method" einer Komponente manuell editiert wird.
|
> 1. Wenn eine "constructor method" einer Komponente manuell editiert wird.
|
||||||
> 2. Wenn eine neue CSS Klasse ergänzt wird (ähnlich wie 1: die CSS Klasse wird von jeder Komponenete neu geschrieben. Dieser Prozess passiert in der "Constructor method".)
|
> 2. Wenn eine neue CSS Klasse ergänzt wird (ähnlich wie 1: die CSS Klasse wird von jeder Komponenete neu geschrieben. Dieser Prozess passiert in der "Constructor method".)
|
||||||
|
|
||||||
@@ -51,7 +54,6 @@ Distributions Pakete können mittels `grunt build` auf Linux Plattformen (e.g. U
|
|||||||
|
|
||||||
Nach der Installation der supporteten Version von `node` and `npm`, installiere auch build dependency packages.
|
Nach der Installation der supporteten Version von `node` and `npm`, installiere auch build dependency packages.
|
||||||
|
|
||||||
|
|
||||||
Ubuntu/Debian:
|
Ubuntu/Debian:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# How to debug Boostnote (Electron app)
|
# How to debug Boostnote (Electron app)
|
||||||
|
|
||||||
Diese Seite ist auch verfügbar in [Japanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/debug.md), [Koreanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/debug.md), [Russisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/debug.md), [Vereinfachtem Chinesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/debug.md), [Französisch](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/debug.md) und [Deutsch](https://github.com/BoostIO/Boostnote/blob/master/docs/de/debug.md).
|
Diese Seite ist auch verfügbar in [Japanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/debug.md), [Koreanisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/debug.md), [Russisch](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/debug.md), [Vereinfachtem Chinesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/debug.md), [Französisch](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/debug.md), [Portugiesisch](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/debug.md) und [Deutsch](https://github.com/BoostIO/Boostnote/blob/master/docs/de/debug.md).
|
||||||
|
|
||||||
|
Boostnote ist eine Electron App und basiert auf Chromium.
|
||||||
Boostnote ist eine Electron App und basiert auf Chromium.
|
|
||||||
|
|
||||||
Zum Entwicklen verwendest du am Besten die `Developer Tools` von Google Chrome verwenden. Diese kannst du ganz einfach im unter dem Menüpunkt `View` mit `Toggle Developer Tools` aktivieren:
|
Zum Entwicklen verwendest du am Besten die `Developer Tools` von Google Chrome verwenden. Diese kannst du ganz einfach im unter dem Menüpunkt `View` mit `Toggle Developer Tools` aktivieren:
|
||||||
|
|
||||||
@@ -13,10 +12,9 @@ Die Anzeige der `Developer Tools` sieht in etwa so aus:
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
Fehlermeldungen werden in der Regel in der `console` ausgegeben, die du über den gleichnamigen Reiter der `Developer Tools` anzeigen lassen kannst. Zum Debuggen kannst du beispielsweise über den `debugger` Haltepunkte im Code setzen.
|
Fehlermeldungen werden in der Regel in der `console` ausgegeben, die du über den gleichnamigen Reiter der `Developer Tools` anzeigen lassen kannst. Zum Debuggen kannst du beispielsweise über den `debugger` Haltepunkte im Code setzen.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -24,8 +22,8 @@ Du kannst aber natürlich auch die Art von Debugging verwenden mit der du am bes
|
|||||||
|
|
||||||
## Referenz
|
## Referenz
|
||||||
|
|
||||||
* [Official document of Google Chrome about debugging](https://developer.chrome.com/devtools)
|
- [Official document of Google Chrome about debugging](https://developer.chrome.com/devtools)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Special thanks: Translated by [gino909](https://github.com/gino909), [mdeuerlein](https://github.com/mdeuerlein)
|
Special thanks: Translated by [gino909](https://github.com/gino909), [mdeuerlein](https://github.com/mdeuerlein)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
# How to debug Boostnote (Electron app)
|
# How to debug Boostnote (Electron app)
|
||||||
|
|
||||||
This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/debug.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/debug.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/debug.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/debug.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/debug.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/debug.md).
|
This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/debug.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/debug.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/debug.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/debug.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/debug.md), [Portuguese](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/debug.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/debug.md).
|
||||||
|
|
||||||
## Debug with Google Chrome developer Tools
|
## Debug with Google Chrome developer Tools
|
||||||
|
|
||||||
Boostnote is an Electron app so it's based on Chromium; developers can use `Developer Tools` just like Google Chrome.
|
Boostnote is an Electron app so it's based on Chromium; developers can use `Developer Tools` just like Google Chrome.
|
||||||
|
|
||||||
You can toggle the `Developer Tools` like this:
|
You can toggle the `Developer Tools` like this:
|
||||||
@@ -14,6 +15,7 @@ The `Developer Tools` will look like this:
|
|||||||
When errors occur, the error messages are displayed at the `console`.
|
When errors occur, the error messages are displayed at the `console`.
|
||||||
|
|
||||||
### Debugging
|
### Debugging
|
||||||
|
|
||||||
For example, you can use the `debugger` to set a breakpoint in the code like this:
|
For example, you can use the `debugger` to set a breakpoint in the code like this:
|
||||||
|
|
||||||

|

|
||||||
@@ -21,16 +23,18 @@ For example, you can use the `debugger` to set a breakpoint in the code like thi
|
|||||||
This is just an illustrative example, you should find a way to debug which fits your style.
|
This is just an illustrative example, you should find a way to debug which fits your style.
|
||||||
|
|
||||||
### References
|
### References
|
||||||
* [Official document of Google Chrome about debugging](https://developer.chrome.com/devtools)
|
|
||||||
|
- [Official document of Google Chrome about debugging](https://developer.chrome.com/devtools)
|
||||||
|
|
||||||
## Debug with Visual Studio Code
|
## Debug with Visual Studio Code
|
||||||
|
|
||||||
1. Install **[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome "Install Debugger for Chrome")** plugin for Visual Studio Code. Then restart it.
|
1. Install **[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome 'Install Debugger for Chrome')** plugin for Visual Studio Code. Then restart it.
|
||||||
2. Pressing **Shift+Command+B** or running **Run Build Task** from the global **Terminal** menu, then pick the task named **Build Boostnote**. Or run `yarn run watch` from the terminal.
|
2. Pressing **Shift+Command+B** or running **Run Build Task** from the global **Terminal** menu, then pick the task named **Build Boostnote**. Or run `yarn run watch` from the terminal.
|
||||||
3. When above task is running, open **Debug view** in **Activity Bar** on the side of VS Code or use shortcut **Shift+Command+D**.
|
3. When above task is running, open **Debug view** in **Activity Bar** on the side of VS Code or use shortcut **Shift+Command+D**.
|
||||||
4. Select the configuration named **Boostnote All** from the **Debug configuration**, then click the green arrow button or press **F5** to start debugging.
|
4. Select the configuration named **Boostnote All** from the **Debug configuration**, then click the green arrow button or press **F5** to start debugging.
|
||||||
5. Now you should find **Boostnote** is running. You will see two processes running, one named **Boostnote Main** and the other named **Boostnote Renderer**. Now you can set **debug breakpoints** in vscode. If you find your **breakpoints** is unverified, you need to switch to the appropriate process between **Boostnote Renderer** and **Boostnote Main**.
|
5. Now you should find **Boostnote** is running. You will see two processes running, one named **Boostnote Main** and the other named **Boostnote Renderer**. Now you can set **debug breakpoints** in vscode. If you find your **breakpoints** is unverified, you need to switch to the appropriate process between **Boostnote Renderer** and **Boostnote Main**.
|
||||||
|
|
||||||
### References
|
### References
|
||||||
* [Electron application debugging](https://electronjs.org/docs/tutorial/application-debugging)
|
|
||||||
* [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
|
- [Electron application debugging](https://electronjs.org/docs/tutorial/application-debugging)
|
||||||
|
- [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
# Build
|
# Build
|
||||||
Cette page est également disponible en [Anglais](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md), [Japonais](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Coréen](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russe](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Chinois Simplifié](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md) et en [Allemand](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md)
|
|
||||||
|
Cette page est également disponible en [Anglais](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md), [Japonais](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Coréen](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russe](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Chinois Simplifié](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [Portugais](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) et en [Allemand](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md)
|
||||||
|
|
||||||
## Environnements
|
## Environnements
|
||||||
|
|
||||||
* npm: 6.x
|
- npm: 6.x
|
||||||
* node: 8.x
|
- node: 8.x
|
||||||
|
|
||||||
## Développement
|
## Développement
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ Installez les paquets requis à l'aide de `yarn`.
|
|||||||
```
|
```
|
||||||
$ yarn
|
$ yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
Build et start
|
Build et start
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -23,7 +25,9 @@ $ yarn run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
> ### Notice
|
> ### Notice
|
||||||
|
>
|
||||||
> Il y a certains cas où vous voudrez relancer l'application manuellement.
|
> Il y a certains cas où vous voudrez relancer l'application manuellement.
|
||||||
|
>
|
||||||
> 1. Quand vous éditez la méthode constructeur dans un composant
|
> 1. Quand vous éditez la méthode constructeur dans un composant
|
||||||
> 2. Quand vous ajoutez une nouvelle classe css. (Comme pour 1: la classe est réécrite pour chaque composant. Le process intervient dans la méthode constructeur)
|
> 2. Quand vous ajoutez une nouvelle classe css. (Comme pour 1: la classe est réécrite pour chaque composant. Le process intervient dans la méthode constructeur)
|
||||||
|
|
||||||
@@ -37,6 +41,7 @@ Nous avons donc préparé un script séparé qui va rendre un fichier exécutabl
|
|||||||
```
|
```
|
||||||
grunt pre-build
|
grunt pre-build
|
||||||
```
|
```
|
||||||
|
|
||||||
Vous trouverez l'exécutable dans le dossier `dist`.
|
Vous trouverez l'exécutable dans le dossier `dist`.
|
||||||
Note : l'auto updater ne marchera pas car l'application n'est pas signée.
|
Note : l'auto updater ne marchera pas car l'application n'est pas signée.
|
||||||
|
|
||||||
@@ -50,7 +55,6 @@ Les paquets sont créés en exécutant `grunt build` sur une plateforme Linux (e
|
|||||||
|
|
||||||
Après avoir installé la version supportée de `node` et de `npm`, installer les paquets de builds.
|
Après avoir installé la version supportée de `node` et de `npm`, installer les paquets de builds.
|
||||||
|
|
||||||
|
|
||||||
Ubuntu/Debian:
|
Ubuntu/Debian:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
75
docs/pt_BR/build.md
Normal file
75
docs/pt_BR/build.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Build
|
||||||
|
|
||||||
|
Esta página também está disponível em [Japônes](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Coreano](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russo](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Chinês simplificado](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [Francês](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) e [Alemão](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
||||||
|
|
||||||
|
## Ambiente
|
||||||
|
|
||||||
|
- npm: 6.x
|
||||||
|
- node: 8.x
|
||||||
|
|
||||||
|
## Desenvolvimento
|
||||||
|
|
||||||
|
Nós usamos o Webpack HMR para desenvolver o Boostnote.
|
||||||
|
Ao executar os seguintes comandos no diretório raiz do projeto, o Boostnote será iniciado com as configurações padrão.
|
||||||
|
|
||||||
|
Instala os pacotes necessários usando o yarn.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
Gerar e iniciar.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ yarn run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
> ### Notice
|
||||||
|
>
|
||||||
|
> Existe alguns casos onde você precisa atualizar o app manualmente.
|
||||||
|
>
|
||||||
|
> 1. Quando editar um método construtor de um componente
|
||||||
|
> 2. Quando adicionar uma nova classe de css (similiar ao 1: a classe do css é reescrita por cada componente. Esse processo ocorre através do método construtor)
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
Nós usamos o Grunt para automatizar o desenvolvimento.
|
||||||
|
Você pode gerar o programa usando `grunt`. Contudo, nós não recomendamos isso porque a tarefa padrão inclui _codedesign_ e _authenticode_.
|
||||||
|
|
||||||
|
Então nós preparamos um _script_ separado, o qual somente cria um executável.
|
||||||
|
|
||||||
|
```
|
||||||
|
grunt pre-build
|
||||||
|
```
|
||||||
|
|
||||||
|
Você irá encontrar o executável na pasta `dist`. Nota: o atualizador automático não funciona porque o app não está certificado.
|
||||||
|
|
||||||
|
Se você achar isto necessário, você pode usar o _codesign_ ou o _authenticode_ com esse executável.
|
||||||
|
|
||||||
|
## Faça seus próprios pacotes de distribuição (deb, rpm)
|
||||||
|
|
||||||
|
Pacotes de distribuição são gerados através do comando `grunt build` em plataforma Linux (e.g. Ubuntu, Fedora).
|
||||||
|
|
||||||
|
> Nota: você pode criar `.deb` e `.rpm` em um mesmo ambiente.
|
||||||
|
|
||||||
|
Depois de instalar uma versão suportada do `node` e do `npm`, deve-se instalar as dependências para gerar os pacotes.
|
||||||
|
|
||||||
|
Ubuntu/Debian:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo apt-get install -y rpm fakeroot
|
||||||
|
```
|
||||||
|
|
||||||
|
Fedora:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot
|
||||||
|
```
|
||||||
|
|
||||||
|
Então execute `grunt build`.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ grunt build
|
||||||
|
```
|
||||||
|
|
||||||
|
Você vai encontrar o `.deb` e o `.rpm` na pasta`dist`.
|
||||||
40
docs/pt_BR/debug.md
Normal file
40
docs/pt_BR/debug.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Como debugar Boostnote (app Electron)
|
||||||
|
|
||||||
|
Esta página também está disponível em [Japônes](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/debug.md), [Coreano](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/debug.md), [Russo](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/debug.md), [Chinês simplificado](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/debug.md), [Francês](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/debug.md) e [Alemão](https://github.com/BoostIO/Boostnote/blob/master/docs/de/debug.md).
|
||||||
|
|
||||||
|
## Debugar com o Google Chrome developer Tools
|
||||||
|
|
||||||
|
Boostnote é um app Electron, por isso ele é baseado no Chromium; desenvolvedores podem usar o `Developer Tools` igual no Google Chrome.
|
||||||
|
|
||||||
|
Você pode habilitar e desabilitar o `Developer Tools` assim:
|
||||||
|

|
||||||
|
|
||||||
|
O `Developer Tools` deve parecer assim:
|
||||||
|

|
||||||
|
|
||||||
|
Quando erros acontecem, eles são apresentados na aba `console`.
|
||||||
|
|
||||||
|
### Debugando
|
||||||
|
|
||||||
|
Por exemplo, você pode usar o `debugger` para adicionar um _breakpoint_ (ponto de parada) no código dessa forma:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Isso é só um exemplo ilustrativo, mas você deve encontrar um jeito de debugar que encaixe no seu estilo.
|
||||||
|
|
||||||
|
### Referências
|
||||||
|
|
||||||
|
- [Documentação do Google Chrome sobre como debugar](https://developer.chrome.com/devtools)
|
||||||
|
|
||||||
|
## Debugar com o Visual Studio Code (VS Code)
|
||||||
|
|
||||||
|
1. Instale o plugin **[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome 'Instale o pacote Debugger for Chrome')** para Visual Studio Code. Então reinicie-o.
|
||||||
|
2. Pressione **Shift+Command+B** ou execute **Run Build Task** do menu global **Terminal**, então seleciona a task **Build Boostnote**. Ou execute `yarn run watch` no terminal.
|
||||||
|
3. Quando a task acima estiver rodando, abra o **Debug view** na **Activity Bar** no lado do seu VS Code ou use o atalho **Shift+Command+D**.
|
||||||
|
4. Selecione a configuração **Boostnote All** no **Debug configuration**, então clique na seta verde ou aperte **F5** para começar a debugar.
|
||||||
|
5. Agora você deve encontrar seu **Boostnote** rodando. Você vai ver dois processos rodando, um com nome de **Boostnote Main** e outro com nome de **Boostnote Renderer**. Agora você pode adicionar os **debug breakpoints** no vscode. Se os seus **breakpoints** não forem alertados você pode precisar altenrar entre os processos **Boostnote Renderer** e **Boostnote Main**.
|
||||||
|
|
||||||
|
### Referências
|
||||||
|
|
||||||
|
- [Debugando uma aplicação Electron](https://electronjs.org/docs/tutorial/application-debugging)
|
||||||
|
- [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
# 編譯
|
# 編譯
|
||||||
此文件還提供下列的語言 [日文](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [韓文](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [俄文](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [簡體中文](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [法文](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) and [德文](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
|
||||||
|
此文件還提供下列的語言 [日文](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [韓文](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [俄文](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [簡體中文](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [法文](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [葡萄牙](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) and [德文](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
|
||||||
|
|
||||||
## 環境
|
## 環境
|
||||||
|
|
||||||
* npm: 6.x
|
- npm: 6.x
|
||||||
* node: 8.x
|
- node: 8.x
|
||||||
|
|
||||||
## 開發
|
## 開發
|
||||||
|
|
||||||
@@ -25,7 +26,9 @@ $ yarn run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
> ### Notice
|
> ### Notice
|
||||||
|
>
|
||||||
> There are some cases where you have to refresh the app manually.
|
> There are some cases where you have to refresh the app manually.
|
||||||
|
>
|
||||||
> 1. When editing a constructor method of a component
|
> 1. When editing a constructor method of a component
|
||||||
> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.)
|
> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.)
|
||||||
|
|
||||||
@@ -52,7 +55,6 @@ Distribution packages are created by exec `grunt build` on Linux platform (e.g.
|
|||||||
|
|
||||||
After installing the supported version of `node` and `npm`, install build dependency packages.
|
After installing the supported version of `node` and `npm`, install build dependency packages.
|
||||||
|
|
||||||
|
|
||||||
Ubuntu/Debian:
|
Ubuntu/Debian:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -44,9 +44,46 @@
|
|||||||
? match[2].replace('x', ' ')
|
? match[2].replace('x', ' ')
|
||||||
: (parseInt(match[3], 10) + 1) + match[4]
|
: (parseInt(match[3], 10) + 1) + match[4]
|
||||||
replacements[i] = '\n' + indent + bullet + after
|
replacements[i] = '\n' + indent + bullet + after
|
||||||
|
|
||||||
|
if (bullet) incrementRemainingMarkdownListNumbers(cm, pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cm.replaceSelections(replacements)
|
cm.replaceSelections(replacements)
|
||||||
}
|
}
|
||||||
|
// Auto-updating Markdown list numbers when a new item is added to the
|
||||||
|
// middle of a list
|
||||||
|
function incrementRemainingMarkdownListNumbers(cm, pos) {
|
||||||
|
var startLine = pos.line, lookAhead = 0, skipCount = 0
|
||||||
|
var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1]
|
||||||
|
|
||||||
|
do {
|
||||||
|
lookAhead += 1
|
||||||
|
var nextLineNumber = startLine + lookAhead
|
||||||
|
var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine)
|
||||||
|
|
||||||
|
if (nextItem) {
|
||||||
|
var nextIndent = nextItem[1]
|
||||||
|
var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount)
|
||||||
|
var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber
|
||||||
|
|
||||||
|
if (startIndent === nextIndent && !isNaN(nextNumber)) {
|
||||||
|
if (newNumber === nextNumber) itemNumber = nextNumber + 1
|
||||||
|
if (newNumber > nextNumber) itemNumber = newNumber + 1
|
||||||
|
cm.replaceRange(
|
||||||
|
nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
|
||||||
|
{
|
||||||
|
line: nextLineNumber, ch: 0
|
||||||
|
}, {
|
||||||
|
line: nextLineNumber, ch: nextLine.length
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if (startIndent.length > nextIndent.length) return
|
||||||
|
// This doesn't run if the next line immediatley indents, as it is
|
||||||
|
// not clear of the users intention (new indented item or same level)
|
||||||
|
if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return
|
||||||
|
skipCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (nextItem)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ ipcMain.on('config-renew', (e, payload) => {
|
|||||||
globalShortcut.unregisterAll()
|
globalShortcut.unregisterAll()
|
||||||
var { config } = payload
|
var { config } = payload
|
||||||
|
|
||||||
|
mainWindow.setMenuBarVisibility(config.ui.showMenuBar)
|
||||||
var errors = []
|
var errors = []
|
||||||
try {
|
try {
|
||||||
globalShortcut.register(config.hotkey.toggleMain, toggleMainWindow)
|
globalShortcut.register(config.hotkey.toggleMain, toggleMainWindow)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const app = electron.app
|
|||||||
const Menu = electron.Menu
|
const Menu = electron.Menu
|
||||||
const ipc = electron.ipcMain
|
const ipc = electron.ipcMain
|
||||||
const GhReleases = require('electron-gh-releases')
|
const GhReleases = require('electron-gh-releases')
|
||||||
|
const isDev = process.env.NODE_ENV !== 'production'
|
||||||
// electron.crashReporter.start()
|
// electron.crashReporter.start()
|
||||||
var ipcServer = null
|
var ipcServer = null
|
||||||
|
|
||||||
@@ -35,6 +36,10 @@ const updater = new GhReleases(ghReleasesOpts)
|
|||||||
// Check for updates
|
// Check for updates
|
||||||
// `status` returns true if there is a new update available
|
// `status` returns true if there is a new update available
|
||||||
function checkUpdate () {
|
function checkUpdate () {
|
||||||
|
if (isDev) { // Prevents app from attempting to update when in dev mode.
|
||||||
|
console.log('Updates are disabled in Development mode, see main-app.js')
|
||||||
|
return true
|
||||||
|
}
|
||||||
if (process.platform === 'linux' || isUpdateReady) {
|
if (process.platform === 'linux' || isUpdateReady) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -94,12 +99,12 @@ app.on('ready', function () {
|
|||||||
|
|
||||||
// Check update every day
|
// Check update every day
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
checkUpdate()
|
if (!isDev) checkUpdate()
|
||||||
}, 1000 * 60 * 60 * 24)
|
}, 1000 * 60 * 60 * 24)
|
||||||
|
|
||||||
// Check update after 10 secs to prevent file locking of Windows
|
// Check update after 10 secs to prevent file locking of Windows
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
checkUpdate()
|
if (!isDev) checkUpdate()
|
||||||
|
|
||||||
ipc.on('update-check', function (event, msg) {
|
ipc.on('update-check', function (event, msg) {
|
||||||
if (isUpdateReady) {
|
if (isUpdateReady) {
|
||||||
|
|||||||
108
lib/main-menu.js
108
lib/main-menu.js
@@ -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',
|
||||||
@@ -388,6 +383,27 @@ const help = {
|
|||||||
{
|
{
|
||||||
label: 'Changelog',
|
label: 'Changelog',
|
||||||
click () { shell.openExternal('https://github.com/BoostIO/boost-releases') }
|
click () { shell.openExternal('https://github.com/BoostIO/boost-releases') }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Cheatsheets',
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'Markdown',
|
||||||
|
click () { shell.openExternal('https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet') }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Latex',
|
||||||
|
click () { shell.openExternal('https://katex.org/docs/supported.html') }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'HTML',
|
||||||
|
click () { shell.openExternal('https://htmlcheatsheet.com/') }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Boostnote',
|
||||||
|
click () { shell.openExternal('https://github.com/TobseF/boostnote-markdown-cheatsheet/blob/master/BOOSTNOTE_MARKDOWN_CHEAT_SHEET.md') }
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ const Config = require('electron-config')
|
|||||||
const config = new Config()
|
const config = new Config()
|
||||||
const _ = require('lodash')
|
const _ = require('lodash')
|
||||||
|
|
||||||
var showMenu = process.platform !== 'win32'
|
|
||||||
const windowSize = config.get('windowsize') || {
|
const windowSize = config.get('windowsize') || {
|
||||||
x: null,
|
x: null,
|
||||||
y: null,
|
y: null,
|
||||||
@@ -22,7 +21,6 @@ const mainWindow = new BrowserWindow({
|
|||||||
useContentSize: true,
|
useContentSize: true,
|
||||||
minWidth: 500,
|
minWidth: 500,
|
||||||
minHeight: 320,
|
minHeight: 320,
|
||||||
autoHideMenuBar: showMenu,
|
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
zoomFactor: 1.0,
|
zoomFactor: 1.0,
|
||||||
enableBlinkFeatures: 'OverlayScrollbars'
|
enableBlinkFeatures: 'OverlayScrollbars'
|
||||||
@@ -33,6 +31,7 @@ const mainWindow = new BrowserWindow({
|
|||||||
const url = path.resolve(__dirname, './main.html')
|
const url = path.resolve(__dirname, './main.html')
|
||||||
|
|
||||||
mainWindow.loadURL('file://' + url)
|
mainWindow.loadURL('file://' + url)
|
||||||
|
mainWindow.setMenuBarVisibility(false)
|
||||||
|
|
||||||
mainWindow.webContents.on('new-window', function (e) {
|
mainWindow.webContents.on('new-window', function (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|||||||
@@ -8,7 +8,10 @@
|
|||||||
"to create a new note": "ノートを新規に作成",
|
"to create a new note": "ノートを新規に作成",
|
||||||
"Toggle Mode": "モード切替",
|
"Toggle Mode": "モード切替",
|
||||||
"Add tag...": "タグを追加...",
|
"Add tag...": "タグを追加...",
|
||||||
|
"Star": "お気に入り",
|
||||||
|
"Fullscreen": "全画面",
|
||||||
"Trash": "ゴミ箱",
|
"Trash": "ゴミ箱",
|
||||||
|
"Info": "情報",
|
||||||
"MODIFICATION DATE": "修正日",
|
"MODIFICATION DATE": "修正日",
|
||||||
"Words": "ワード",
|
"Words": "ワード",
|
||||||
"Letters": "文字",
|
"Letters": "文字",
|
||||||
@@ -26,6 +29,9 @@
|
|||||||
"Storage Locations": "ストレージ",
|
"Storage Locations": "ストレージ",
|
||||||
"Add Storage Location": "ストレージロケーションを追加",
|
"Add Storage Location": "ストレージロケーションを追加",
|
||||||
"Add Folder": "フォルダを追加",
|
"Add Folder": "フォルダを追加",
|
||||||
|
"Create new folder": "新規フォルダ作成",
|
||||||
|
"Folder name": "フォルダ名",
|
||||||
|
"Create": "作成",
|
||||||
"Select Folder": "フォルダを選択",
|
"Select Folder": "フォルダを選択",
|
||||||
"Open Storage folder": "ストレージフォルダを開く",
|
"Open Storage folder": "ストレージフォルダを開く",
|
||||||
"Unlink": "リンク解除",
|
"Unlink": "リンク解除",
|
||||||
@@ -37,9 +43,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 +67,32 @@
|
|||||||
"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": "スマートテーブルエディタを有効にする",
|
||||||
|
"Enable HTML paste": "HTML貼り付けを有効にする",
|
||||||
|
"Matching character pairs": "自動補完する括弧ペアの列記",
|
||||||
|
"Matching character triples": "自動補完する3文字括弧の列記",
|
||||||
|
"Exploding character pairs": "改行時に空行を挿入する括弧ペアの列記",
|
||||||
"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",
|
||||||
@@ -105,7 +128,19 @@
|
|||||||
"French": "フランス語",
|
"French": "フランス語",
|
||||||
"Show \"Saved to Clipboard\" notification when copying": "クリップボードコピー時に \"クリップボードに保存\" 通知を表示する",
|
"Show \"Saved to Clipboard\" notification when copying": "クリップボードコピー時に \"クリップボードに保存\" 通知を表示する",
|
||||||
"All Notes": "すべてのノート",
|
"All Notes": "すべてのノート",
|
||||||
|
"Pin to Top": "一番上にピン留め",
|
||||||
|
"Remove pin": "ピン削除",
|
||||||
|
"Clone Note": "ノート複製",
|
||||||
|
"Copy Note Link": "ノートのリンクをコピー",
|
||||||
|
"Publish Blog": "ブログを公開",
|
||||||
"Starred": "スター付き",
|
"Starred": "スター付き",
|
||||||
|
"Empty Trash": "ゴミ箱を空にする",
|
||||||
|
"Restore Note": "ノート復元",
|
||||||
|
"Rename Folder": "フォルダの名称変更",
|
||||||
|
"Export Folder": "フォルダの書き出し",
|
||||||
|
"Export as txt": ".txtで書き出す",
|
||||||
|
"Export as md": ".mdで書き出す",
|
||||||
|
"Delete Folder": "フォルダ削除",
|
||||||
"Are you sure to ": "本当に ",
|
"Are you sure to ": "本当に ",
|
||||||
" delete": "このフォルダを",
|
" delete": "このフォルダを",
|
||||||
"this folder?": "削除しますか?",
|
"this folder?": "削除しますか?",
|
||||||
@@ -135,6 +170,8 @@
|
|||||||
"Hotkeys": "ホットキー",
|
"Hotkeys": "ホットキー",
|
||||||
"Show/Hide Boostnote": "Boostnote の表示/非表示",
|
"Show/Hide Boostnote": "Boostnote の表示/非表示",
|
||||||
"Toggle Editor Mode": "エディタモードの切替",
|
"Toggle Editor Mode": "エディタモードの切替",
|
||||||
|
"Delete Note": "ノート削除",
|
||||||
|
"Paste HTML": "HTMLで貼り付け",
|
||||||
"Restore": "リストア",
|
"Restore": "リストア",
|
||||||
"Permanent Delete": "永久に削除",
|
"Permanent Delete": "永久に削除",
|
||||||
"Confirm note deletion": "ノート削除確認",
|
"Confirm note deletion": "ノート削除確認",
|
||||||
@@ -165,6 +202,7 @@
|
|||||||
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
|
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
|
||||||
"Location": "ロケーション",
|
"Location": "ロケーション",
|
||||||
"Add": "追加",
|
"Add": "追加",
|
||||||
|
"Export Storage": "ストレージの書き出し",
|
||||||
"Unlink Storage": "ストレージのリンクを解除",
|
"Unlink Storage": "ストレージのリンクを解除",
|
||||||
"Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "リンクの解除ではBoostnoteからリンクされたストレージを削除しますが、データは削除されません。データを削除する場合はご自身でハードドライブからフォルダを削除してください。",
|
"Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "リンクの解除ではBoostnoteからリンクされたストレージを削除しますが、データは削除されません。データを削除する場合はご自身でハードドライブからフォルダを削除してください。",
|
||||||
"Editor Rulers": "罫線",
|
"Editor Rulers": "罫線",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"White": "Branco",
|
"White": "Branco",
|
||||||
"Solarized Dark": "Escuro Solarizado",
|
"Solarized Dark": "Escuro Solarizado",
|
||||||
"Dark": "Escuro",
|
"Dark": "Escuro",
|
||||||
"Show a confirmation dialog when deleting notes": "Mostrar um diálogo de confirmação ao escluir notas",
|
"Show a confirmation dialog when deleting notes": "Mostrar um diálogo de confirmação ao excluir notas",
|
||||||
"Editor Theme": "Tema do Editor",
|
"Editor Theme": "Tema do Editor",
|
||||||
"Editor Font Size": "Tamanho da Fonte do Editor",
|
"Editor Font Size": "Tamanho da Fonte do Editor",
|
||||||
"Editor Font Family": "Família da Fonte do Editor",
|
"Editor Font Family": "Família da Fonte do Editor",
|
||||||
|
|||||||
23
package.json
23
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "boost",
|
"name": "boost",
|
||||||
"productName": "Boostnote",
|
"productName": "Boostnote",
|
||||||
"version": "0.11.11",
|
"version": "0.11.13",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"description": "Boostnote",
|
"description": "Boostnote",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"storage",
|
"storage",
|
||||||
"electron"
|
"electron"
|
||||||
],
|
],
|
||||||
"author": "Dick Choi <fluke8259@gmail.com> (https://github.com/Rokt33r)",
|
"author": "Junyoung Choi <fluke8259@gmail.com> (https://github.com/Rokt33r)",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Kazu Yokomizo (https://github.com/kazup01)",
|
"Kazu Yokomizo (https://github.com/kazup01)",
|
||||||
"dojineko (https://github.com/dojineko)",
|
"dojineko (https://github.com/dojineko)",
|
||||||
@@ -41,7 +41,8 @@
|
|||||||
"Yoshihisa Mochihara (https://github.com/yosmoc)",
|
"Yoshihisa Mochihara (https://github.com/yosmoc)",
|
||||||
"Mike Resoli (https://github.com/mikeres0)",
|
"Mike Resoli (https://github.com/mikeres0)",
|
||||||
"tjado (https://github.com/tejado)",
|
"tjado (https://github.com/tejado)",
|
||||||
"Sota Sugiura (https://github.com/sota1235)"
|
"Sota Sugiura (https://github.com/sota1235)",
|
||||||
|
"Milo Todt (https://github.com/MiloTodt)"
|
||||||
],
|
],
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/BoostIO/Boostnote/issues"
|
"url": "https://github.com/BoostIO/Boostnote/issues"
|
||||||
@@ -62,13 +63,15 @@
|
|||||||
"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",
|
||||||
|
"highlight.js": "^9.13.1",
|
||||||
"i18n-2": "^0.7.2",
|
"i18n-2": "^0.7.2",
|
||||||
"iconv-lite": "^0.4.19",
|
"iconv-lite": "^0.4.19",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
|
"invert-color": "^2.0.0",
|
||||||
"js-sequence-diagrams": "^1000000.0.6",
|
"js-sequence-diagrams": "^1000000.0.6",
|
||||||
"js-yaml": "^3.12.0",
|
"js-yaml": "^3.12.0",
|
||||||
"katex": "^0.9.0",
|
"katex": "^0.9.0",
|
||||||
@@ -90,17 +93,20 @@
|
|||||||
"mdurl": "^1.0.1",
|
"mdurl": "^1.0.1",
|
||||||
"mermaid": "^8.0.0-rc.8",
|
"mermaid": "^8.0.0-rc.8",
|
||||||
"moment": "^2.10.3",
|
"moment": "^2.10.3",
|
||||||
"mousetrap": "^1.6.1",
|
"mousetrap": "^1.6.2",
|
||||||
"mousetrap-global-bind": "^1.1.0",
|
"mousetrap-global-bind": "^1.1.0",
|
||||||
"node-ipc": "^8.1.0",
|
"node-ipc": "^8.1.0",
|
||||||
"raphael": "^2.2.7",
|
"raphael": "^2.2.7",
|
||||||
"react": "^15.5.4",
|
"react": "^15.5.4",
|
||||||
"react-autosuggest": "^9.4.0",
|
"react-autosuggest": "^9.4.0",
|
||||||
"react-codemirror": "^0.3.0",
|
"react-codemirror": "^0.3.0",
|
||||||
|
"react-color": "^2.2.2",
|
||||||
"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 +148,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",
|
||||||
@@ -150,7 +157,6 @@
|
|||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"mock-require": "^3.0.1",
|
"mock-require": "^3.0.1",
|
||||||
"nib": "^1.1.0",
|
"nib": "^1.1.0",
|
||||||
"react-color": "^2.2.2",
|
|
||||||
"react-css-modules": "^3.7.6",
|
"react-css-modules": "^3.7.6",
|
||||||
"react-input-autosize": "^1.1.0",
|
"react-input-autosize": "^1.1.0",
|
||||||
"react-router": "^2.4.0",
|
"react-router": "^2.4.0",
|
||||||
@@ -189,5 +195,10 @@
|
|||||||
"<rootDir>/tests/jest.js",
|
"<rootDir>/tests/jest.js",
|
||||||
"jest-localstorage-mock"
|
"jest-localstorage-mock"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "npm run lint"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
readme.md
14
readme.md
@@ -5,10 +5,14 @@
|
|||||||
<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)
|
||||||
- [Sosuke](https://github.com/sosukesuzuki)
|
- [Sosuke](https://github.com/sosukesuzuki)
|
||||||
- [Kazz](https://github.com/kazup01)
|
- [Kazz](https://github.com/kazup01)
|
||||||
@@ -20,11 +24,11 @@ Thank you to all the people who have contributed to Boostnote!
|
|||||||
<a href="https://github.com/BoostIO/Boostnote/graphs/contributors"><img src="https://opencollective.com/boostnoteio/contributors.svg?width=890" /></a>
|
<a href="https://github.com/BoostIO/Boostnote/graphs/contributors"><img src="https://opencollective.com/boostnoteio/contributors.svg?width=890" /></a>
|
||||||
|
|
||||||
## Supporting Boostnote
|
## Supporting Boostnote
|
||||||
Boostnote is an open source project. It's an independent project with its ongoing development made possible thanks to the support by our amazing backers.
|
Boostnote is an open source project. It's an independent project with its ongoing development made possible thanks to the support by our amazing backers.
|
||||||
|
|
||||||
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/)
|
||||||
@@ -38,7 +42,7 @@ Issues on Boostnote can be funded by anyone and the money will be distributed to
|
|||||||
* Website: https://boostnote.io
|
* Website: https://boostnote.io
|
||||||
* Newsletters: https://boostnote.io/#subscribe
|
* Newsletters: https://boostnote.io/#subscribe
|
||||||
* [Development](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md): Development configurations for Boostnote.
|
* [Development](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md): Development configurations for Boostnote.
|
||||||
* Copyright (C) 2016 - 2018 BoostIO, Inc.
|
* Copyright (C) 2016 - 2019 BoostIO, Inc.
|
||||||
|
|
||||||
|
|
||||||
#### License
|
#### License
|
||||||
|
|||||||
17
resources/icon/icon-x-light.svg
Normal file
17
resources/icon/icon-x-light.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>x</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<g id="Feather" transform="translate(-1910.000000, -644.000000)" stroke="#c1c1c1" stroke-width="2">
|
||||||
|
<g id="Group" transform="translate(175.000000, 332.000000)">
|
||||||
|
<g id="x" transform="translate(1736.000000, 313.000000)">
|
||||||
|
<path d="M12,0 L0,12" id="Shape"></path>
|
||||||
|
<path d="M0,0 L12,12" id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 917 B |
@@ -3,7 +3,7 @@ import renderer from 'react-test-renderer'
|
|||||||
import TagListItem from 'browser/components/TagListItem'
|
import TagListItem from 'browser/components/TagListItem'
|
||||||
|
|
||||||
it('TagListItem renders correctly', () => {
|
it('TagListItem renders correctly', () => {
|
||||||
const tagListItem = renderer.create(<TagListItem name='Test' handleClickTagListItem={jest.fn()} />)
|
const tagListItem = renderer.create(<TagListItem name='Test' handleClickTagListItem={jest.fn()} color='#000' />)
|
||||||
|
|
||||||
expect(tagListItem.toJSON()).toMatchSnapshot()
|
expect(tagListItem.toJSON()).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ exports[`TagListItem renders correctly 1`] = `
|
|||||||
className="tagList-item"
|
className="tagList-item"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
className="tagList-item-color"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"backgroundColor": "#000",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
<span
|
<span
|
||||||
className="tagList-item-name"
|
className="tagList-item-name"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ it('should test that copyAttachment with url (without extension, with query)', f
|
|||||||
|
|
||||||
it('should replace the all ":storage" path with the actual storage path', function () {
|
it('should replace the all ":storage" path with the actual storage path', function () {
|
||||||
const storageFolder = systemUnderTest.DESTINATION_FOLDER
|
const storageFolder = systemUnderTest.DESTINATION_FOLDER
|
||||||
|
const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c'
|
||||||
const testInput =
|
const testInput =
|
||||||
'<html>\n' +
|
'<html>\n' +
|
||||||
' <head>\n' +
|
' <head>\n' +
|
||||||
@@ -276,14 +277,18 @@ it('should replace the all ":storage" path with the actual storage path', functi
|
|||||||
' <body data-theme="default">\n' +
|
' <body data-theme="default">\n' +
|
||||||
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
||||||
' <p data-line="2">\n' +
|
' <p data-line="2">\n' +
|
||||||
' <img src=":storage' + mdurl.encode(path.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
' <img src=":storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="4">\n' +
|
' <p data-line="4">\n' +
|
||||||
' <a href=":storage' + mdurl.encode(path.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
' <a href=":storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="6">\n' +
|
' <p data-line="6">\n' +
|
||||||
' <img src=":storage' + mdurl.encode(path.sep) + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
' <img src=":storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
|
' <pre class="fence" data-line="8">\n' +
|
||||||
|
' <span class="filename"></span>\n' +
|
||||||
|
' <div class="gallery" data-autoplay="undefined" data-height="undefined">:storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + 'f939b2c3.jpg</div>\n' +
|
||||||
|
' </pre>\n' +
|
||||||
' </body>\n' +
|
' </body>\n' +
|
||||||
'</html>'
|
'</html>'
|
||||||
const storagePath = '<<dummyStoragePath>>'
|
const storagePath = '<<dummyStoragePath>>'
|
||||||
@@ -295,14 +300,18 @@ it('should replace the all ":storage" path with the actual storage path', functi
|
|||||||
' <body data-theme="default">\n' +
|
' <body data-theme="default">\n' +
|
||||||
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
||||||
' <p data-line="2">\n' +
|
' <p data-line="2">\n' +
|
||||||
' <img src="file:///' + storagePath + path.sep + storageFolder + path.sep + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
' <img src="file:///' + storagePath + path.sep + storageFolder + path.sep + noteKey + path.sep + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="4">\n' +
|
' <p data-line="4">\n' +
|
||||||
' <a href="file:///' + storagePath + path.sep + storageFolder + path.sep + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
' <a href="file:///' + storagePath + path.sep + storageFolder + path.sep + noteKey + path.sep + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="6">\n' +
|
' <p data-line="6">\n' +
|
||||||
' <img src="file:///' + storagePath + path.sep + storageFolder + path.sep + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
' <img src="file:///' + storagePath + path.sep + storageFolder + path.sep + noteKey + path.sep + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
|
' <pre class="fence" data-line="8">\n' +
|
||||||
|
' <span class="filename"></span>\n' +
|
||||||
|
' <div class="gallery" data-autoplay="undefined" data-height="undefined">file:///' + storagePath + path.sep + storageFolder + path.sep + noteKey + path.sep + 'f939b2c3.jpg</div>\n' +
|
||||||
|
' </pre>\n' +
|
||||||
' </body>\n' +
|
' </body>\n' +
|
||||||
'</html>'
|
'</html>'
|
||||||
const actual = systemUnderTest.fixLocalURLS(testInput, storagePath)
|
const actual = systemUnderTest.fixLocalURLS(testInput, storagePath)
|
||||||
@@ -311,6 +320,7 @@ it('should replace the all ":storage" path with the actual storage path', functi
|
|||||||
|
|
||||||
it('should replace the ":storage" path with the actual storage path when they have different path separators', function () {
|
it('should replace the ":storage" path with the actual storage path when they have different path separators', function () {
|
||||||
const storageFolder = systemUnderTest.DESTINATION_FOLDER
|
const storageFolder = systemUnderTest.DESTINATION_FOLDER
|
||||||
|
const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c'
|
||||||
const testInput =
|
const testInput =
|
||||||
'<html>\n' +
|
'<html>\n' +
|
||||||
' <head>\n' +
|
' <head>\n' +
|
||||||
@@ -319,10 +329,10 @@ it('should replace the ":storage" path with the actual storage path when they ha
|
|||||||
' <body data-theme="default">\n' +
|
' <body data-theme="default">\n' +
|
||||||
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
||||||
' <p data-line="2">\n' +
|
' <p data-line="2">\n' +
|
||||||
' <img src=":storage' + mdurl.encode(path.win32.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
' <img src=":storage' + mdurl.encode(path.win32.sep) + noteKey + mdurl.encode(path.win32.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="4">\n' +
|
' <p data-line="4">\n' +
|
||||||
' <a href=":storage' + mdurl.encode(path.posix.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
' <a href=":storage' + mdurl.encode(path.posix.sep) + noteKey + mdurl.encode(path.posix.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' </body>\n' +
|
' </body>\n' +
|
||||||
'</html>'
|
'</html>'
|
||||||
@@ -335,10 +345,10 @@ it('should replace the ":storage" path with the actual storage path when they ha
|
|||||||
' <body data-theme="default">\n' +
|
' <body data-theme="default">\n' +
|
||||||
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
||||||
' <p data-line="2">\n' +
|
' <p data-line="2">\n' +
|
||||||
' <img src="file:///' + storagePath + path.sep + storageFolder + path.sep + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
' <img src="file:///' + storagePath + path.sep + storageFolder + path.sep + noteKey + path.sep + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="4">\n' +
|
' <p data-line="4">\n' +
|
||||||
' <a href="file:///' + storagePath + path.sep + storageFolder + path.sep + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
' <a href="file:///' + storagePath + path.sep + storageFolder + path.sep + noteKey + path.sep + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' </body>\n' +
|
' </body>\n' +
|
||||||
'</html>'
|
'</html>'
|
||||||
@@ -391,28 +401,17 @@ it('should test that getAttachmentsInMarkdownContent finds all attachments when
|
|||||||
|
|
||||||
it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function () {
|
it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function () {
|
||||||
const dummyStoragePath = 'dummyStoragePath'
|
const dummyStoragePath = 'dummyStoragePath'
|
||||||
const testInput =
|
const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c'
|
||||||
'<html>\n' +
|
const testInput = '"# Test\n' +
|
||||||
' <head>\n' +
|
'\n' +
|
||||||
' //header\n' +
|
'\n' +
|
||||||
' </head>\n' +
|
'\n' +
|
||||||
' <body data-theme="default">\n' +
|
'"'
|
||||||
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
|
||||||
' <p data-line="2">\n' +
|
|
||||||
' <img src=":storage' + mdurl.encode(path.sep) + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + mdurl.encode(path.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
|
||||||
' </p>\n' +
|
|
||||||
' <p data-line="4">\n' +
|
|
||||||
' <a href=":storage' + mdurl.encode(path.sep) + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + mdurl.encode(path.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
|
||||||
' </p>\n' +
|
|
||||||
' <p data-line="6">\n' +
|
|
||||||
' <img src=":storage' + mdurl.encode(path.sep) + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + mdurl.encode(path.sep) + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
|
||||||
' </p>\n' +
|
|
||||||
' </body>\n' +
|
|
||||||
'</html>'
|
|
||||||
const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent(testInput, dummyStoragePath)
|
const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent(testInput, dummyStoragePath)
|
||||||
const expected = [dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '0.6r4zdgc22xp.png',
|
const expected = [dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.6r4zdgc22xp.png',
|
||||||
dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '0.q2i4iw0fyx.pdf',
|
dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.q2i4iw0fyx.pdf',
|
||||||
dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + 'd6c5ee92.jpg']
|
dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + 'd6c5ee92.jpg']
|
||||||
expect(actual).toEqual(expect.arrayContaining(expected))
|
expect(actual).toEqual(expect.arrayContaining(expected))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -427,13 +426,13 @@ it('should remove the all ":storage" and noteKey references', function () {
|
|||||||
' <body data-theme="default">\n' +
|
' <body data-theme="default">\n' +
|
||||||
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
' <h2 data-line="0" id="Headline">Headline</h2>\n' +
|
||||||
' <p data-line="2">\n' +
|
' <p data-line="2">\n' +
|
||||||
' <img src=":storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
' <img src=":storage' + mdurl.encode(path.win32.sep) + noteKey + mdurl.encode(path.win32.sep) + '0.6r4zdgc22xp.png" alt="dummyImage.png" >\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="4">\n' +
|
' <p data-line="4">\n' +
|
||||||
' <a href=":storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
' <a href=":storage' + mdurl.encode(path.posix.sep) + noteKey + mdurl.encode(path.posix.sep) + '0.q2i4iw0fyx.pdf">dummyPDF.pdf</a>\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' <p data-line="6">\n' +
|
' <p data-line="6">\n' +
|
||||||
' <img src=":storage' + mdurl.encode(path.sep) + noteKey + mdurl.encode(path.sep) + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
' <img src=":storage' + mdurl.encode(path.win32.sep) + noteKey + mdurl.encode(path.posix.sep) + 'd6c5ee92.jpg" alt="dummyImage2.jpg">\n' +
|
||||||
' </p>\n' +
|
' </p>\n' +
|
||||||
' </body>\n' +
|
' </body>\n' +
|
||||||
'</html>'
|
'</html>'
|
||||||
@@ -463,8 +462,8 @@ it('should make sure that "removeStorageAndNoteReferences" works with markdown c
|
|||||||
const noteKey = 'noteKey'
|
const noteKey = 'noteKey'
|
||||||
const testInput =
|
const testInput =
|
||||||
'Test input' +
|
'Test input' +
|
||||||
' \n' +
|
' \n' +
|
||||||
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + noteKey + path.sep + 'pdf.pdf](pdf})'
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + noteKey + path.posix.sep + 'pdf.pdf](pdf})'
|
||||||
|
|
||||||
const expectedOutput =
|
const expectedOutput =
|
||||||
'Test input' +
|
'Test input' +
|
||||||
@@ -616,8 +615,8 @@ it('should test that moveAttachments returns a correct modified content version'
|
|||||||
const newNoteKey = 'newNoteKey'
|
const newNoteKey = 'newNoteKey'
|
||||||
const testInput =
|
const testInput =
|
||||||
'Test input' +
|
'Test input' +
|
||||||
' \n' +
|
' \n' +
|
||||||
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNoteKey + path.sep + 'pdf.pdf](pdf})'
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNoteKey + path.posix.sep + 'pdf.pdf](pdf})'
|
||||||
const expectedOutput =
|
const expectedOutput =
|
||||||
'Test input' +
|
'Test input' +
|
||||||
' \n' +
|
' \n' +
|
||||||
@@ -632,8 +631,8 @@ it('should test that cloneAttachments modifies the content of the new note corre
|
|||||||
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'}
|
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'}
|
||||||
const testInput =
|
const testInput =
|
||||||
'Test input' +
|
'Test input' +
|
||||||
' \n' +
|
' \n' +
|
||||||
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})'
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})'
|
||||||
newNote.content = testInput
|
newNote.content = testInput
|
||||||
findStorage.findStorage = jest.fn()
|
findStorage.findStorage = jest.fn()
|
||||||
findStorage.findStorage.mockReturnValue({path: 'dummyStoragePath'})
|
findStorage.findStorage.mockReturnValue({path: 'dummyStoragePath'})
|
||||||
@@ -656,8 +655,8 @@ it('should test that cloneAttachments finds all attachments and copies them to t
|
|||||||
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'MARKDOWN_NOTE'}
|
const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'MARKDOWN_NOTE'}
|
||||||
const testInput =
|
const testInput =
|
||||||
'Test input' +
|
'Test input' +
|
||||||
' \n' +
|
' \n' +
|
||||||
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})'
|
'[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})'
|
||||||
oldNote.content = testInput
|
oldNote.content = testInput
|
||||||
newNote.content = testInput
|
newNote.content = testInput
|
||||||
|
|
||||||
@@ -706,14 +705,22 @@ it('should test that isAttachmentLink works correctly', function () {
|
|||||||
expect(systemUnderTest.isAttachmentLink('text')).toBe(false)
|
expect(systemUnderTest.isAttachmentLink('text')).toBe(false)
|
||||||
expect(systemUnderTest.isAttachmentLink('text [linkText](link)')).toBe(false)
|
expect(systemUnderTest.isAttachmentLink('text [linkText](link)')).toBe(false)
|
||||||
expect(systemUnderTest.isAttachmentLink('text ')).toBe(false)
|
expect(systemUnderTest.isAttachmentLink('text ')).toBe(false)
|
||||||
expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf)')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink('')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf)')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink('text ')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('text ')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf) test')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink(' test')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink(' test')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf) test')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true)
|
||||||
expect(systemUnderTest.isAttachmentLink('text  test')).toBe(true)
|
expect(systemUnderTest.isAttachmentLink('text  test')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('text ')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink(' test')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true)
|
||||||
|
expect(systemUnderTest.isAttachmentLink('text  test')).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function () {
|
it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function () {
|
||||||
@@ -721,7 +728,7 @@ it('should test that handleAttachmentLinkPaste copies the attachments to the new
|
|||||||
findStorage.findStorage = jest.fn(() => dummyStorage)
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const pasteText = 'text '
|
const pasteText = 'text '
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
||||||
|
|
||||||
@@ -736,12 +743,53 @@ it('should test that handleAttachmentLinkPaste copies the attachments to the new
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist', function () {
|
it('should test that handleAttachmentLinkPaste copies the attachments to the new location - win32 path', function () {
|
||||||
const dummyStorage = {path: 'dummyStoragePath'}
|
const dummyStorage = {path: 'dummyStoragePath'}
|
||||||
findStorage.findStorage = jest.fn(() => dummyStorage)
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const pasteText = 'text '
|
const pasteText = 'text '
|
||||||
|
const storageKey = 'storageKey'
|
||||||
|
const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
||||||
|
|
||||||
|
sander.exists = jest.fn(() => Promise.resolve(true))
|
||||||
|
systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName'))
|
||||||
|
|
||||||
|
return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText)
|
||||||
|
.then(() => {
|
||||||
|
expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey)
|
||||||
|
expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath)
|
||||||
|
expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePath, storageKey, newNoteKey)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist - win32 path', function () {
|
||||||
|
const dummyStorage = {path: 'dummyStoragePath'}
|
||||||
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
|
const pasteText = 'text '
|
||||||
|
const storageKey = 'storageKey'
|
||||||
|
const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
||||||
|
|
||||||
|
sander.exists = jest.fn(() => Promise.resolve(false))
|
||||||
|
systemUnderTest.copyAttachment = jest.fn()
|
||||||
|
systemUnderTest.generateFileNotFoundMarkdown = jest.fn()
|
||||||
|
|
||||||
|
return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText)
|
||||||
|
.then(() => {
|
||||||
|
expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey)
|
||||||
|
expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath)
|
||||||
|
expect(systemUnderTest.copyAttachment).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist -- posix', function () {
|
||||||
|
const dummyStorage = {path: 'dummyStoragePath'}
|
||||||
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
|
const pasteText = 'text '
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
||||||
|
|
||||||
@@ -762,8 +810,8 @@ it('should test that handleAttachmentLinkPaste copies multiple attachments if mu
|
|||||||
findStorage.findStorage = jest.fn(() => dummyStorage)
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const pasteText = 'text  ..' +
|
const pasteText = 'text  ..' +
|
||||||
''
|
''
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
||||||
const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg')
|
const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg')
|
||||||
@@ -787,7 +835,7 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past
|
|||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const dummyNewFileName = 'dummyNewFileName'
|
const dummyNewFileName = 'dummyNewFileName'
|
||||||
const pasteText = 'text '
|
const pasteText = 'text '
|
||||||
const expectedText = 'text '
|
const expectedText = 'text '
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
|
|
||||||
@@ -807,8 +855,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past
|
|||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const dummyNewFileNameOne = 'dummyNewFileName'
|
const dummyNewFileNameOne = 'dummyNewFileName'
|
||||||
const dummyNewFileNameTwo = 'dummyNewFileNameTwo'
|
const dummyNewFileNameTwo = 'dummyNewFileNameTwo'
|
||||||
const pasteText = 'text  ' +
|
const pasteText = 'text  ' +
|
||||||
''
|
''
|
||||||
const expectedText = 'text  ' +
|
const expectedText = 'text  ' +
|
||||||
''
|
''
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
@@ -829,8 +877,8 @@ it('should test that handleAttachmentLinkPaste calls the copy method correct if
|
|||||||
findStorage.findStorage = jest.fn(() => dummyStorage)
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const pasteText = 'text  ..' +
|
const pasteText = 'text  ..' +
|
||||||
''
|
''
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf')
|
||||||
const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg')
|
const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg')
|
||||||
@@ -856,7 +904,7 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past
|
|||||||
findStorage.findStorage = jest.fn(() => dummyStorage)
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const pasteText = 'text '
|
const pasteText = 'text '
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const fileNotFoundMD = 'file not found'
|
const fileNotFoundMD = 'file not found'
|
||||||
const expectedPastText = 'text ' + fileNotFoundMD
|
const expectedPastText = 'text ' + fileNotFoundMD
|
||||||
@@ -875,8 +923,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past
|
|||||||
findStorage.findStorage = jest.fn(() => dummyStorage)
|
findStorage.findStorage = jest.fn(() => dummyStorage)
|
||||||
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723'
|
||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const pasteText = 'text  ' +
|
const pasteText = 'text  ' +
|
||||||
''
|
''
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const fileNotFoundMD = 'file not found'
|
const fileNotFoundMD = 'file not found'
|
||||||
const expectedPastText = 'text ' + fileNotFoundMD + ' ' + fileNotFoundMD
|
const expectedPastText = 'text ' + fileNotFoundMD + ' ' + fileNotFoundMD
|
||||||
@@ -897,8 +945,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past
|
|||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const dummyFoundFileName = 'dummyFileName'
|
const dummyFoundFileName = 'dummyFileName'
|
||||||
const fileNotFoundMD = 'file not found'
|
const fileNotFoundMD = 'file not found'
|
||||||
const pasteText = 'text  .. ' +
|
const pasteText = 'text  .. ' +
|
||||||
''
|
''
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const expectedPastText = 'text ' + fileNotFoundMD + ' .. '
|
const expectedPastText = 'text ' + fileNotFoundMD + ' .. '
|
||||||
|
|
||||||
@@ -921,8 +969,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past
|
|||||||
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723'
|
||||||
const dummyFoundFileName = 'dummyFileName'
|
const dummyFoundFileName = 'dummyFileName'
|
||||||
const fileNotFoundMD = 'file not found'
|
const fileNotFoundMD = 'file not found'
|
||||||
const pasteText = 'text  .. ' +
|
const pasteText = 'text  .. ' +
|
||||||
''
|
''
|
||||||
const storageKey = 'storageKey'
|
const storageKey = 'storageKey'
|
||||||
const expectedPastText = 'text  .. ' + fileNotFoundMD
|
const expectedPastText = 'text  .. ' + fileNotFoundMD
|
||||||
|
|
||||||
|
|||||||
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
178
yarn.lock
178
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"
|
||||||
@@ -4300,6 +4337,11 @@ he@^1.1.1:
|
|||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
resolved "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||||
|
|
||||||
|
highlight.js@^9.13.1:
|
||||||
|
version "9.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e"
|
||||||
|
integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==
|
||||||
|
|
||||||
highlight.js@^9.3.0:
|
highlight.js@^9.3.0:
|
||||||
version "9.12.0"
|
version "9.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
|
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
|
||||||
@@ -4452,6 +4494,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"
|
||||||
@@ -4600,6 +4657,11 @@ invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.0.0"
|
loose-envify "^1.0.0"
|
||||||
|
|
||||||
|
invert-color@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/invert-color/-/invert-color-2.0.0.tgz#894ab1f7494a6e45f5e74c2f99ec042cd67dd23e"
|
||||||
|
integrity sha512-9s6IATlhOAr0/0MPUpLdMpk81ixIu8IqwPwORssXBauFT/4ff/iyEOcojd0UYuPwkDbJvL1+blIZGhqVIaAm5Q==
|
||||||
|
|
||||||
invert-kv@^1.0.0:
|
invert-kv@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
|
||||||
@@ -4662,6 +4724,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 +4762,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 +5396,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 +5407,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 +5764,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 +5888,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"
|
||||||
@@ -6237,9 +6326,10 @@ mousetrap-global-bind@^1.1.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mousetrap-global-bind/-/mousetrap-global-bind-1.1.0.tgz#cd7de9222bd0646fa2e010d54c84a74c26a88edd"
|
resolved "https://registry.yarnpkg.com/mousetrap-global-bind/-/mousetrap-global-bind-1.1.0.tgz#cd7de9222bd0646fa2e010d54c84a74c26a88edd"
|
||||||
|
|
||||||
mousetrap@^1.6.1:
|
mousetrap@^1.6.2:
|
||||||
version "1.6.1"
|
version "1.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.1.tgz#2a085f5c751294c75e7e81f6ec2545b29cbf42d9"
|
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.2.tgz#caadd9cf886db0986fb2fee59a82f6bd37527587"
|
||||||
|
integrity sha512-jDjhi7wlHwdO6q6DS7YRmSHcuI+RVxadBkLt3KHrhd3C2b+w5pKefg3oj5beTcHZyVFA9Aksf+yEE1y5jxUjVA==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@@ -6681,16 +6771,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 +6992,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 +7359,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 +7545,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 +7556,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 +7625,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 +7687,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 +8045,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 +8148,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 +8311,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