Skip to content

Commit 78ae7b8

Browse files
authored
Merge branch 'master' into bug-2581
2 parents 849104f + c7bae93 commit 78ae7b8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+220138
-86
lines changed

.vscode/launch.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
8+
{
9+
"type": "node",
10+
"request": "launch",
11+
"name": "BoostNote Main",
12+
"protocol": "inspector",
13+
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
14+
"runtimeArgs": [
15+
"--remote-debugging-port=9223",
16+
"--hot",
17+
"${workspaceFolder}/index.js"
18+
],
19+
"windows": {
20+
"runtimeExecutable": "${workspaceFolder}/node_modeules/.bin/electron.cmd"
21+
}
22+
},
23+
{
24+
"type": "chrome",
25+
"request": "attach",
26+
"name": "BoostNote Renderer",
27+
"port": 9223,
28+
"webRoot": "${workspaceFolder}",
29+
"sourceMapPathOverrides": {
30+
"webpack:///./~/*": "${webRoot}/node_modules/*",
31+
"webpack:///*": "${webRoot}/*"
32+
}
33+
}
34+
],
35+
"compounds": [
36+
{
37+
"name": "BostNote All",
38+
"configurations": ["BoostNote Main", "BoostNote Renderer"]
39+
}
40+
]
41+
}

.vscode/tasks.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
// See https://go.microsoft.com/fwlink/?LinkId=733558
3+
// for the documentation about the tasks.json format
4+
"version": "2.0.0",
5+
"tasks": [
6+
{
7+
"label": "Build Boostnote",
8+
"group": "build",
9+
"type": "npm",
10+
"script": "watch",
11+
"isBackground": true,
12+
"presentation": {
13+
"reveal": "always",
14+
},
15+
"problemMatcher": {
16+
"pattern":[
17+
{
18+
"regexp": "^([^\\\\s].*)\\\\((\\\\d+,\\\\d+)\\\\):\\\\s*(.*)$",
19+
"file": 1,
20+
"location": 2,
21+
"message": 3
22+
}
23+
]
24+
}
25+
}
26+
]
27+
}

PULL_REQUEST_TEMPLATE.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!--
2+
Before submitting this PR, please make sure that:
3+
- You have read and understand the contributing.md
4+
- You have checked docs/code_style.md for information on code style
5+
-->
6+
## Description
7+
<!--
8+
Tell us what your PR does.
9+
Please attach a screenshot/ video/gif image describing your PR if possible.
10+
-->
11+
12+
## Issue fixed
13+
<!--
14+
Please list out all issue fixed with this PR here.
15+
-->
16+
17+
<!--
18+
Please make sure you fill in these checkboxes,
19+
your PR will be reviewed faster if we know exactly what it does.
20+
21+
Change :white_circle: to :radio_button: in all the options that apply
22+
-->
23+
## Type of changes
24+
25+
- :white_circle: Bug fix (Change that fixed an issue)
26+
- :white_circle: Breaking change (Change that can cause existing functionality to change)
27+
- :white_circle: Improvement (Change that improves the code. Maybe performance or development improvement)
28+
- :white_circle: Feature (Change that adds new functionality)
29+
- :white_circle: Documentation change (Change that modifies documentation. Maybe typo fixes)
30+
31+
## Checklist:
32+
33+
- :white_circle: My code follows [the project code style](docs/code_style.md)
34+
- :white_circle: I have written test for my code and it has been tested
35+
- :white_circle: All existing tests have been passed
36+
- :white_circle: I have attached a screenshot/video to visualize my change if possible

browser/components/CodeEditor.js

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ import eventEmitter from 'browser/main/lib/eventEmitter'
1111
import iconv from 'iconv-lite'
1212
import crypto from 'crypto'
1313
import consts from 'browser/lib/consts'
14+
import styles from '../components/CodeEditor.styl'
1415
import fs from 'fs'
15-
const { ipcRenderer } = require('electron')
16+
const { ipcRenderer, remote } = require('electron')
1617
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
18+
const spellcheck = require('browser/lib/spellcheck')
19+
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
1720
import TurndownService from 'turndown'
1821
import { gfm } from 'turndown-plugin-gfm'
1922

@@ -30,7 +33,7 @@ export default class CodeEditor extends React.Component {
3033
leading: false,
3134
trailing: true
3235
})
33-
this.changeHandler = e => this.handleChange(e)
36+
this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject)
3437
this.focusHandler = () => {
3538
ipcRenderer.send('editor:focused', true)
3639
}
@@ -62,6 +65,12 @@ export default class CodeEditor extends React.Component {
6265
this.scrollToLineHandeler = this.scrollToLine.bind(this)
6366

6467
this.formatTable = () => this.handleFormatTable()
68+
this.contextMenuHandler = function (editor, event) {
69+
const menu = buildEditorContextMenu(editor, event)
70+
if (menu != null) {
71+
setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
72+
}
73+
}
6574
this.editorActivityHandler = () => this.handleEditorActivity()
6675

6776
this.turndownService = new TurndownService()
@@ -232,6 +241,7 @@ export default class CodeEditor extends React.Component {
232241
this.editor.on('blur', this.blurHandler)
233242
this.editor.on('change', this.changeHandler)
234243
this.editor.on('paste', this.pasteHandler)
244+
this.editor.on('contextmenu', this.contextMenuHandler)
235245
eventEmitter.on('top:search', this.searchHandler)
236246

237247
eventEmitter.emit('code:init')
@@ -248,6 +258,10 @@ export default class CodeEditor extends React.Component {
248258

249259
this.textEditorInterface = new TextEditorInterface(this.editor)
250260
this.tableEditor = new TableEditor(this.textEditorInterface)
261+
if (this.props.spellCheck) {
262+
this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'})
263+
}
264+
251265
eventEmitter.on('code:format-table', this.formatTable)
252266

253267
this.tableEditorOptions = options({
@@ -317,22 +331,28 @@ export default class CodeEditor extends React.Component {
317331
const snippetLines = snippets[i].content.split('\n')
318332
let cursorLineNumber = 0
319333
let cursorLinePosition = 0
334+
335+
let cursorIndex
320336
for (let j = 0; j < snippetLines.length; j++) {
321-
const cursorIndex = snippetLines[j].indexOf(templateCursorString)
337+
cursorIndex = snippetLines[j].indexOf(templateCursorString)
338+
322339
if (cursorIndex !== -1) {
323340
cursorLineNumber = j
324341
cursorLinePosition = cursorIndex
325-
cm.replaceRange(
326-
snippets[i].content.replace(templateCursorString, ''),
327-
wordBeforeCursor.range.from,
328-
wordBeforeCursor.range.to
329-
)
330-
cm.setCursor({
331-
line: cursor.line + cursorLineNumber,
332-
ch: cursorLinePosition
333-
})
342+
343+
break
334344
}
335345
}
346+
347+
cm.replaceRange(
348+
snippets[i].content.replace(templateCursorString, ''),
349+
wordBeforeCursor.range.from,
350+
wordBeforeCursor.range.to
351+
)
352+
cm.setCursor({
353+
line: cursor.line + cursorLineNumber,
354+
ch: cursorLinePosition + cursor.ch - wordBeforeCursor.text.length
355+
})
336356
} else {
337357
cm.replaceRange(
338358
snippets[i].content,
@@ -389,9 +409,11 @@ export default class CodeEditor extends React.Component {
389409
this.editor.off('paste', this.pasteHandler)
390410
eventEmitter.off('top:search', this.searchHandler)
391411
this.editor.off('scroll', this.scrollHandler)
412+
this.editor.off('contextmenu', this.contextMenuHandler)
392413
const editorTheme = document.getElementById('editorTheme')
393414
editorTheme.removeEventListener('load', this.loadStyleHandler)
394415

416+
spellcheck.setLanguage(null, spellcheck.SPELLCHECK_DISABLED)
395417
eventEmitter.off('code:format-table', this.formatTable)
396418
}
397419

@@ -459,6 +481,16 @@ export default class CodeEditor extends React.Component {
459481
needRefresh = true
460482
}
461483

484+
if (prevProps.spellCheck !== this.props.spellCheck) {
485+
if (this.props.spellCheck === false) {
486+
spellcheck.setLanguage(this.editor, spellcheck.SPELLCHECK_DISABLED)
487+
let elem = document.getElementById('editor-bottom-panel')
488+
elem.parentNode.removeChild(elem)
489+
} else {
490+
this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'})
491+
}
492+
}
493+
462494
if (needRefresh) {
463495
this.editor.refresh()
464496
}
@@ -472,10 +504,11 @@ export default class CodeEditor extends React.Component {
472504
CodeMirror.autoLoadMode(this.editor, syntax.mode)
473505
}
474506

475-
handleChange (e) {
476-
this.value = this.editor.getValue()
507+
handleChange (editor, changeObject) {
508+
spellcheck.handleChange(editor, changeObject)
509+
this.value = editor.getValue()
477510
if (this.props.onChange) {
478-
this.props.onChange(e)
511+
this.props.onChange(editor)
479512
}
480513
}
481514

@@ -712,6 +745,25 @@ export default class CodeEditor extends React.Component {
712745
/>
713746
)
714747
}
748+
749+
createSpellCheckPanel () {
750+
const panel = document.createElement('div')
751+
panel.className = 'panel bottom'
752+
panel.id = 'editor-bottom-panel'
753+
const dropdown = document.createElement('select')
754+
dropdown.title = 'Spellcheck'
755+
dropdown.className = styles['spellcheck-select']
756+
dropdown.addEventListener('change', (e) => spellcheck.setLanguage(this.editor, dropdown.value))
757+
const options = spellcheck.getAvailableDictionaries()
758+
for (const op of options) {
759+
const option = document.createElement('option')
760+
option.value = op.value
761+
option.innerHTML = op.label
762+
dropdown.appendChild(option)
763+
}
764+
panel.appendChild(dropdown)
765+
return panel
766+
}
715767
}
716768

717769
CodeEditor.propTypes = {
@@ -722,7 +774,8 @@ CodeEditor.propTypes = {
722774
className: PropTypes.string,
723775
onBlur: PropTypes.func,
724776
onChange: PropTypes.func,
725-
readOnly: PropTypes.bool
777+
readOnly: PropTypes.bool,
778+
spellCheck: PropTypes.bool
726779
}
727780

728781
CodeEditor.defaultProps = {
@@ -732,5 +785,6 @@ CodeEditor.defaultProps = {
732785
fontSize: 14,
733786
fontFamily: 'Monaco, Consolas',
734787
indentSize: 4,
735-
indentType: 'space'
788+
indentType: 'space',
789+
spellCheck: false
736790
}

browser/components/CodeEditor.styl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.codeEditor-typo
2+
text-decoration underline wavy red
3+
4+
.spellcheck-select
5+
border: none
6+
text-decoration underline wavy red

browser/components/MarkdownEditor.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,10 @@ class MarkdownEditor extends React.Component {
147147
e.preventDefault()
148148
e.stopPropagation()
149149
const idMatch = /checkbox-([0-9]+)/
150-
const checkedMatch = /\[x\]/i
151-
const uncheckedMatch = /\[ \]/
150+
const checkedMatch = /^\s*[\+\-\*] \[x\]/i
151+
const uncheckedMatch = /^\s*[\+\-\*] \[ \]/
152+
const checkReplace = /\[x\]/i
153+
const uncheckReplace = /\[ \]/
152154
if (idMatch.test(e.target.getAttribute('id'))) {
153155
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
154156
const lines = this.refs.code.value
@@ -157,10 +159,10 @@ class MarkdownEditor extends React.Component {
157159
const targetLine = lines[lineIndex]
158160

159161
if (targetLine.match(checkedMatch)) {
160-
lines[lineIndex] = targetLine.replace(checkedMatch, '[ ]')
162+
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
161163
}
162164
if (targetLine.match(uncheckedMatch)) {
163-
lines[lineIndex] = targetLine.replace(uncheckedMatch, '[x]')
165+
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
164166
}
165167
this.refs.code.setValue(lines.join('\n'))
166168
}
@@ -275,6 +277,7 @@ class MarkdownEditor extends React.Component {
275277
enableTableEditor={config.editor.enableTableEditor}
276278
onChange={(e) => this.handleChange(e)}
277279
onBlur={(e) => this.handleBlur(e)}
280+
spellCheck={config.editor.spellcheck}
278281
/>
279282
<MarkdownPreview styleName={this.state.status === 'PREVIEW'
280283
? 'preview'

browser/components/MarkdownPreview.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import yaml from 'js-yaml'
2121
import context from 'browser/lib/context'
2222
import i18n from 'browser/lib/i18n'
2323
import fs from 'fs'
24+
import ConfigManager from '../main/lib/ConfigManager'
2425

2526
const { remote, shell } = require('electron')
2627
const attachmentManagement = require('../main/lib/dataApi/attachmentManagement')
@@ -263,6 +264,10 @@ export default class MarkdownPreview extends React.Component {
263264
}
264265

265266
handleMouseDown (e) {
267+
const config = ConfigManager.get()
268+
if (config.editor.switchPreview === 'RIGHTCLICK' && e.buttons === 2 && config.editor.type === 'SPLIT') {
269+
eventEmitter.emit('topbar:togglemodebutton', 'CODE')
270+
}
266271
if (e.target != null) {
267272
switch (e.target.tagName) {
268273
case 'A':

browser/components/MarkdownSplitEditor.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ class MarkdownSplitEditor extends React.Component {
7878
e.preventDefault()
7979
e.stopPropagation()
8080
const idMatch = /checkbox-([0-9]+)/
81-
const checkedMatch = /\[x\]/i
82-
const uncheckedMatch = /\[ \]/
81+
const checkedMatch = /^\s*[\+\-\*] \[x\]/i
82+
const uncheckedMatch = /^\s*[\+\-\*] \[ \]/
83+
const checkReplace = /\[x\]/i
84+
const uncheckReplace = /\[ \]/
8385
if (idMatch.test(e.target.getAttribute('id'))) {
8486
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
8587
const lines = this.refs.code.value
@@ -88,10 +90,10 @@ class MarkdownSplitEditor extends React.Component {
8890
const targetLine = lines[lineIndex]
8991

9092
if (targetLine.match(checkedMatch)) {
91-
lines[lineIndex] = targetLine.replace(checkedMatch, '[ ]')
93+
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
9294
}
9395
if (targetLine.match(uncheckedMatch)) {
94-
lines[lineIndex] = targetLine.replace(uncheckedMatch, '[x]')
96+
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
9597
}
9698
this.refs.code.setValue(lines.join('\n'))
9799
}
@@ -169,6 +171,7 @@ class MarkdownSplitEditor extends React.Component {
169171
noteKey={noteKey}
170172
onChange={this.handleOnChange.bind(this)}
171173
onScroll={this.handleScroll.bind(this)}
174+
spellCheck={config.editor.spellcheck}
172175
/>
173176
<div styleName='slider' style={{left: this.state.codeEditorWidthInPercent + '%'}} onMouseDown={e => this.handleMouseDown(e)} >
174177
<div styleName='slider-hitbox' />

browser/components/render/MermaidRender.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function render (element, content, theme) {
2525
if (height && height.value !== 'undefined') {
2626
element.style.height = height.value + 'vh'
2727
}
28-
let isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula'
28+
const isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula'
2929
mermaidAPI.initialize({
3030
theme: isDarkTheme ? 'dark' : 'default',
3131
themeCSS: isDarkTheme ? darkThemeStyling : '',

0 commit comments

Comments
 (0)