diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index d17987ce..a20807c0 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -55,19 +55,64 @@ export default class CodeEditor extends React.Component { indentWithTabs: this.props.indentType !== 'space', keyMap: this.props.keyMap, inputStyle: 'textarea', + dragDrop: false, extraKeys: { Tab: function (cm) { + const cursor = cm.getCursor() + const line = cm.getLine(cursor.line) if (cm.somethingSelected()) cm.indentSelection('add') else { - if (cm.getOption('indentWithTabs')) { - cm.execCommand('insertTab') + const tabs = cm.getOption('indentWithTabs') + if (line.trimLeft() === '- ' || line.trimLeft() === '* ' || line.trimLeft() === '+ ') { + cm.execCommand('goLineStart') + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + cm.execCommand('goLineEnd') } else { - cm.execCommand('insertSoftTab') + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } } } }, 'Cmd-T': function (cm) { // Do nothing + }, + Enter: (cm) => { + const cursor = cm.getCursor() + const line = cm.getLine(cursor.line) + let bulletType; + if (line.trim().startsWith('- ')) { + bulletType = 1 // dash + } else if (line.trim().startsWith('* ')) { + bulletType = 2 // star + } else if (line.trim().startsWith('+ ')) { + bulletType = 3 // plus + } else { + bulletType = 0 // not a bullet + } + const numberedListRegex = /^(\d+)\. .+/ + const match = line.trim().match(numberedListRegex) + if (bulletType !== 0 || match) { + cm.execCommand('newlineAndIndent') + const range = {line: cursor.line + 1, ch: cm.getLine(cursor.line + 1).length} + if (match) { + cm.replaceRange((parseInt(match[1]) + 1) + '. ', range) + } else if (bulletType === 1) { + cm.replaceRange('- ', range) + } else if (bulletType === 2) { + cm.replaceRange('* ', range) + } else if (bulletType === 3) { + cm.replaceRange('+ ', range) + } + } else { + cm.execCommand('newlineAndIndent') + } } } }) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index d809524c..9bb50a87 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -11,6 +11,10 @@ class MarkdownEditor extends React.Component { this.escapeFromEditor = ['Control', 'w'] + this.supportMdBold = ['Control', 'b'] + + this.supportMdWordBold = ['Control', ':'] + this.state = { status: 'PREVIEW', renderValue: props.value, @@ -78,7 +82,7 @@ class MarkdownEditor extends React.Component { this.refs.code.blur() this.refs.preview.focus() } - eventEmitter.emit('topbar:showlockbutton') + eventEmitter.emit('topbar:togglelockbutton', this.state.status) }) } } @@ -95,7 +99,7 @@ class MarkdownEditor extends React.Component { this.refs.preview.focus() this.refs.preview.scrollTo(cursorPosition.line) }) - eventEmitter.emit('topbar:showlockbutton') + eventEmitter.emit('topbar:togglelockbutton', this.state.status) } } @@ -111,7 +115,7 @@ class MarkdownEditor extends React.Component { }, () => { this.refs.code.focus() }) - eventEmitter.emit('topbar:showlockbutton') + eventEmitter.emit('topbar:togglelockbutton', this.state.status) } } @@ -148,7 +152,7 @@ class MarkdownEditor extends React.Component { } else { this.refs.code.focus() } - eventEmitter.emit('topbar:showlockbutton') + eventEmitter.emit('topbar:togglelockbutton', this.state.status) } reload () { @@ -157,7 +161,8 @@ class MarkdownEditor extends React.Component { this.renderPreview(this.props.value) } - handleKeyDown (e) { + handleKeyDown(e) { + if (this.state.status !== 'CODE') return false const keyPressed = Object.assign(this.state.keyPressed, { [e.key]: true }) @@ -166,6 +171,27 @@ class MarkdownEditor extends React.Component { if (!this.state.isLocked && this.state.status === 'CODE' && this.escapeFromEditor.every(isNoteHandlerKey)) { document.activeElement.blur() } + if (this.supportMdBold.every(isNoteHandlerKey)) { + this.addMdAndMoveCaretToCenter('****') + } + if (this.supportMdWordBold.every(isNoteHandlerKey)) { + this.addMdBetweenWord('**') + } + } + + addMdAndMoveCaretToCenter (mdElement) { + const currentCaret = this.refs.code.editor.getCursor() + const cmDoc = this.refs.code.editor.getDoc() + cmDoc.replaceRange(mdElement, currentCaret) + this.refs.code.editor.setCursor({line: currentCaret.line, ch: currentCaret.ch + mdElement.length/2}) + } + + addMdBetweenWord (mdElement) { + const currentCaret = this.refs.code.editor.getCursor() + const word = this.refs.code.editor.findWordAt(currentCaret) + const cmDoc = this.refs.code.editor.getDoc() + cmDoc.replaceRange(mdElement, word.anchor) + cmDoc.replaceRange(mdElement, { line: word.head.line, ch: word.head.ch + mdElement.length }) } handleKeyUp (e) { diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 00a0523c..460920d8 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -243,6 +243,10 @@ export default class MarkdownPreview extends React.Component { this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) + _.forEach(this.refs.root.contentWindow.document.querySelectorAll('.taskListItem'), (el) => { + el.parentNode.parentNode.style.listStyleType = 'none' + }) + _.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => { el.addEventListener('click', this.anchorClickHandler) }) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index 9dd769d2..3fbc893f 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -182,8 +182,6 @@ ul list-style-type circle &>li>ul list-style-type square -ul.markdownIt-TOC, ul.markdownIt-TOC ul - list-style-type none ol list-style-type decimal padding-left 2em diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 8159884c..af68b0e3 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -1,7 +1,6 @@ import markdownit from 'markdown-it' import emoji from 'markdown-it-emoji' import math from '@rokt33r/markdown-it-math' -import tocAndAnchor from 'markdown-it-toc-and-anchor' import _ from 'lodash' const katex = window.katex @@ -37,9 +36,6 @@ var md = markdownit({ md.use(emoji, { shortcuts: {} }) -md.use(tocAndAnchor, { - anchorLink: false -}) md.use(math, { inlineRenderer: function (str) { let output = '' diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index a3391a30..641582a6 100644 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -26,12 +26,12 @@ class MarkdownNoteDetail extends React.Component { title: '', content: '' }, props.note), - editorStatus: false, + isLockButtonShown: false, isLocked: false } this.dispatchTimer = null - this.showLockButton = this.handleShowLockButton.bind(this) + this.toggleLockButton = this.handleToggleLockButton.bind(this) } focus () { @@ -39,7 +39,7 @@ class MarkdownNoteDetail extends React.Component { } componentDidMount () { - ee.on('topbar:showlockbutton', this.showLockButton) + ee.on('topbar:togglelockbutton', this.toggleLockButton) } componentWillReceiveProps (nextProps) { @@ -59,16 +59,19 @@ class MarkdownNoteDetail extends React.Component { } componentDidUnmount () { - ee.off('topbar:lock', this.showLockButton) + ee.off('topbar:togglelockbutton', this.toggleLockButton) } findTitle (value) { let splitted = value.split('\n') let title = null + let isMarkdownInCode = false for (let i = 0; i < splitted.length; i++) { let trimmedLine = splitted[i].trim() - if (trimmedLine.match(/^# .+/)) { + if (trimmedLine.match('```')) { + isMarkdownInCode = !isMarkdownInCode + } else if (isMarkdownInCode === false && trimmedLine.match(/^# +/)) { title = trimmedLine.substring(1, trimmedLine.length).trim() break } @@ -216,6 +219,7 @@ class MarkdownNoteDetail extends React.Component { e.preventDefault() ee.emit('editor:lock') this.setState({ isLocked: !this.state.isLocked }) + if (this.state.isLocked) this.focus() } getToggleLockButton () { @@ -226,8 +230,13 @@ class MarkdownNoteDetail extends React.Component { if (e.keyCode === 27) this.handleDeleteCancelButtonClick(e) } - handleShowLockButton () { - this.setState({editorStatus: this.refs.content.state.status}) + handleToggleLockButton (event, noteStatus) { + // first argument event is not used + if (this.props.config.editor.switchPreview === 'BLUR' && noteStatus === 'CODE') { + this.setState({isLockButtonShown: true}) + } else { + this.setState({isLockButtonShown: false}) + } } handleFocus (e) { @@ -268,20 +277,31 @@ class MarkdownNoteDetail extends React.Component { {(() => { const faClassName = `fa ${this.getToggleLockButton()}` const lockButtonComponent = - return ( - this.state.editorStatus === 'CODE' ? lockButtonComponent : '' + this.state.isLockButtonShown ? lockButtonComponent : '' ) })()} - diff --git a/browser/main/Detail/MarkdownNoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl index e23296a4..53a6a4e9 100644 --- a/browser/main/Detail/MarkdownNoteDetail.styl +++ b/browser/main/Detail/MarkdownNoteDetail.styl @@ -9,6 +9,28 @@ background-color $ui-noteDetail-backgroundColor box-shadow $note-detail-box-shadow +.lock-button + padding-bottom 3px + +.control-lockButton + topBarButtonLight() + +.control-lockButton-tooltip + tooltip() + position fixed + pointer-events none + top 50px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + +.control-trashButton + float right + topBarButtonLight() + .body absolute left right left $note-detail-left-margin @@ -24,3 +46,12 @@ body[data-theme="dark"] border-color $ui-dark-borderColor background-color $ui-dark-noteDetail-backgroundColor box-shadow none + + .control-lockButton + topBarButtonDark() + + .control-lockButton-tooltip + darkTooltip() + + .control-trashButton + topBarButtonDark() diff --git a/browser/main/Detail/NoteDetailInfo.styl b/browser/main/Detail/NoteDetailInfo.styl index 73062d8a..97adfe5c 100644 --- a/browser/main/Detail/NoteDetailInfo.styl +++ b/browser/main/Detail/NoteDetailInfo.styl @@ -38,7 +38,7 @@ $info-margin-under-border = 27px margin 13px 2px padding 0 border-radius 17px - &:hover .info-right-button-tooltip + &:hover .info-left-button-tooltip opacity 1 &:focus border-color $ui-favorite-star-button-color @@ -54,21 +54,6 @@ $info-margin-under-border = 27px bottom 1px padding-left 30px -.info-right-button - width 34px - height 34px - border-radius 17px - font-size 14px - margin 13px 7px - padding 0 - border none - color $ui-button-color - background-color transparent - &:hover - opacity 1 - background-color $ui-button--hover-backgroundColor - - body[data-theme="dark"] .info border-color $ui-dark-borderColor @@ -88,11 +73,3 @@ body[data-theme="dark"] .info-right background-color $ui-dark-noteDetail-backgroundColor - - .info-right-button - navDarkButtonColor() - border-color $ui-dark-borderColor - &:active - border-color $ui-dark-button--focus-borderColor - &:focus - border-color $ui-button--focus-borderColor diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 30760589..953c2e11 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -259,6 +259,8 @@ class SnippetNoteDetail extends React.Component { this.setState({ note: this.state.note, snippetIndex + }, () => { + this.save() }) } @@ -545,10 +547,18 @@ class SnippetNoteDetail extends React.Component { />
-
diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index ab86130b..f9e7ef7a 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -68,6 +68,10 @@ &:active .update-icon color white +.control-trashButton + float right + topBarButtonLight() + body[data-theme="dark"] .root border-color $ui-dark-borderColor @@ -93,3 +97,6 @@ body[data-theme="dark"] .override button border-color $ui-dark-borderColor + + .control-trashButton + topBarButtonDark() diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index b8c9700d..c36b73ca 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -38,10 +38,14 @@ class NoteList extends React.Component { this.focusHandler = () => { this.refs.list.focus() } - this.alertIfSnippetHnalder = () => { + this.alertIfSnippetHandler = () => { this.alertIfSnippet() } + this.jumpToTopHandler = () => { + this.jumpToTop() + } + this.state = { } } @@ -51,7 +55,9 @@ class NoteList extends React.Component { ee.on('list:next', this.selectNextNoteHandler) ee.on('list:prior', this.selectPriorNoteHandler) ee.on('list:focus', this.focusHandler) - ee.on('list:isMarkdownNote', this.alertIfSnippetHnalder) + ee.on('list:isMarkdownNote', this.alertIfSnippetHandler) + ee.on('list:top', this.jumpToTopHandler) + ee.on('list:jumpToTop', this.jumpToTopHandler) } componentWillReceiveProps (nextProps) { @@ -70,7 +76,9 @@ class NoteList extends React.Component { ee.off('list:next', this.selectNextNoteHandler) ee.off('list:prior', this.selectPriorNoteHandler) ee.off('list:focus', this.focusHandler) - ee.off('list:isMarkdownNote', this.alertIfSnippetHnalder) + ee.off('list:isMarkdownNote', this.alertIfSnippetHandler) + ee.off('list:top', this.jumpToTopHandler) + ee.off('list:jumpToTop', this.jumpToTopHandler) } componentDidUpdate (prevProps) { @@ -324,6 +332,23 @@ class NoteList extends React.Component { } } + jumpToTop() { + if (this.notes === null || this.notes.length === 0) { + return + } + let { router } = this.context + let { location } = this.props + + const targetIndex = 0 + + router.push({ + pathname: location.pathname, + query: { + key: this.notes[targetIndex].storage + '-' + this.notes[targetIndex].key + } + }) + } + render () { let { location, notes, config } = this.props let sortFunc = config.sortBy === 'CREATED_AT' diff --git a/browser/styles/index.styl b/browser/styles/index.styl index bfd6e865..26a00f1b 100644 --- a/browser/styles/index.styl +++ b/browser/styles/index.styl @@ -139,6 +139,24 @@ modal() border-radius $modal-border-radius box-shadow 2px 2px 10px gray +topBarButtonLight() + width 34px + height 34px + border-radius 17px + font-size 14px + margin 13px 7px + padding-top 7px + border none + color $ui-button-color + fill $ui-button-color + background-color transparent + &:active + border-color $ui-button--active-backgroundColor + &:hover + background-color $ui-button--hover-backgroundColor + .control-lockButton-tooltip + opacity 1 + // Dark theme $ui-dark-borderColor = lighten(#21252B, 20%) $ui-dark-backgroundColor = #1D1D1D @@ -151,6 +169,7 @@ $ui-dark-button--active-color = white $ui-dark-button--active-backgroundColor = #6AA5E9 $ui-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-dark-button--focus-borderColor = lighten(#369DCD, 25%) +$ui-dark-topbar-button-color = #939395 $dark-default-button-background = $ui-dark-backgroundColor $dark-default-button-background--hover = $ui-dark-button--hover-backgroundColor @@ -190,6 +209,18 @@ navDarkButtonColor() background-color $ui-dark-button--active-backgroundColor color $ui-dark-button--active-color +topBarButtonDark() + border-color $ui-dark-borderColor + color $ui-dark-topbar-button-color + &:hover + background-color $dark-default-button-background--hover + &:active + border-color $ui-dark-button--focus-borderColor + &:active:hover + background-color $ui-dark-button--active-backgroundColor + &:focus + border-color $ui-button--focus-borderColor + $ui-dark-tooltip-text-color = white $ui-dark-tooltip-backgroundColor = alpha(#444, 70%) $ui-dark-tooltip-button-backgroundColor = #D1D1D1 diff --git a/lib/main-menu.js b/lib/main-menu.js index b3a7acc7..2dfe92ea 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -183,6 +183,13 @@ var view = { mainWindow.webContents.send('list:prior') } }, + { + label: 'Jump to Top', + accelerator: 'Control + G', + click () { + mainWindow.webContents.send('list:jumpToTop') + } + }, { type: 'separator' }, diff --git a/package.json b/package.json index 10cedc2c..667ffb8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "boost", - "version": "0.8.6", + "version": "0.8.7", "description": "Boostnote", "main": "index.js", "license": "GPL-3.0", @@ -61,7 +61,6 @@ "markdown-it-checkbox": "^1.1.0", "markdown-it-emoji": "^1.1.1", "markdown-it-footnote": "^3.0.0", - "markdown-it-toc-and-anchor": "^4.1.1", "md5": "^2.0.0", "mixpanel": "^0.4.1", "moment": "^2.10.3", diff --git a/resources/tray-icon-dark.png b/resources/tray-icon-dark.png old mode 100755 new mode 100644 index 2b76ee32..4aa26bfd Binary files a/resources/tray-icon-dark.png and b/resources/tray-icon-dark.png differ diff --git a/resources/tray-icon-dark@2x.png b/resources/tray-icon-dark@2x.png old mode 100755 new mode 100644 index fea7a99b..5e58d5ef Binary files a/resources/tray-icon-dark@2x.png and b/resources/tray-icon-dark@2x.png differ diff --git a/resources/tray-icon-default.png b/resources/tray-icon-default.png old mode 100755 new mode 100644 index 2b76ee32..08725fb7 Binary files a/resources/tray-icon-default.png and b/resources/tray-icon-default.png differ diff --git a/resources/tray-icon-default@2x.png b/resources/tray-icon-default@2x.png old mode 100755 new mode 100644 index fea7a99b..c2a7e17e Binary files a/resources/tray-icon-default@2x.png and b/resources/tray-icon-default@2x.png differ diff --git a/resources/tray-icon.png b/resources/tray-icon.png old mode 100755 new mode 100644 index 2b76ee32..08725fb7 Binary files a/resources/tray-icon.png and b/resources/tray-icon.png differ diff --git a/resources/tray-icon@2x.png b/resources/tray-icon@2x.png old mode 100755 new mode 100644 index fea7a99b..c2a7e17e Binary files a/resources/tray-icon@2x.png and b/resources/tray-icon@2x.png differ