mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 10:16:26 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27265e210f | ||
|
|
c392c5d178 | ||
|
|
11fe420fac | ||
|
|
9b17a8fb5b | ||
|
|
27f3fd0032 | ||
|
|
1672d9fa5f | ||
|
|
59c9e11879 | ||
|
|
4523743150 | ||
|
|
2fdbe9de96 | ||
|
|
a617976c78 | ||
|
|
7201a98d78 | ||
|
|
472560e2bf | ||
|
|
96753fe0a0 | ||
|
|
83c2fdd161 | ||
|
|
911ce7572f | ||
|
|
0a24d7d4a7 | ||
|
|
c542062d4d | ||
|
|
eb7a195cce | ||
|
|
23a356164e | ||
|
|
de19c51061 | ||
|
|
221b6a2938 | ||
|
|
cda9d53c8e | ||
|
|
f043b0ffb3 | ||
|
|
28e0590327 | ||
|
|
ec6de1b91b | ||
|
|
2b0bdbf1c8 | ||
|
|
f48864a2e7 | ||
|
|
94c6578675 | ||
|
|
2af2399971 | ||
|
|
ebea01cecf | ||
|
|
5d1db1de31 | ||
|
|
6c528625d8 | ||
|
|
2a60ba95e0 | ||
|
|
cdb079dc81 | ||
|
|
2ac0d93caf | ||
|
|
41977e8726 | ||
|
|
b9e6a56a83 | ||
|
|
2468c8311f | ||
|
|
e8e05b20cd | ||
|
|
5bd0a446f1 |
@@ -1,6 +1,7 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import _ from 'lodash'
|
||||
import CodeMirror from 'codemirror'
|
||||
import path from 'path'
|
||||
|
||||
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
||||
|
||||
@@ -163,6 +164,19 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.setCursor(cursor)
|
||||
}
|
||||
|
||||
handleDropImage (e) {
|
||||
e.preventDefault()
|
||||
let imagePath = e.dataTransfer.files[0].path
|
||||
let filename = path.basename(imagePath)
|
||||
let imageMd = ``
|
||||
this.insertImage(imageMd)
|
||||
}
|
||||
|
||||
insertImage (imageMd) {
|
||||
const textarea = this.editor.getInputField()
|
||||
textarea.value = textarea.value.substr(0, textarea.selectionStart) + imageMd + textarea.value.substr(textarea.selectionEnd)
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className, fontFamily, fontSize } = this.props
|
||||
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
|
||||
@@ -180,6 +194,7 @@ export default class CodeEditor extends React.Component {
|
||||
fontFamily: fontFamily.join(', '),
|
||||
fontSize: fontSize
|
||||
}}
|
||||
onDrop={(e) => this.handleDropImage(e)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@ class MarkdownEditor extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.hotkey = props.config.hotkey
|
||||
|
||||
this.state = {
|
||||
status: 'PREVIEW',
|
||||
renderValue: props.value
|
||||
renderValue: props.value,
|
||||
keyPressed: {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +145,24 @@ class MarkdownEditor extends React.Component {
|
||||
this.renderPreview(this.props.value)
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
const keyPressed = Object.assign(this.state.keyPressed, {
|
||||
[e.key]: true
|
||||
})
|
||||
this.setState({ keyPressed })
|
||||
let isNoteHandlerKey = (el) => { return this.state.keyPressed[el] }
|
||||
if (this.state.status === 'CODE' && this.hotkey.noteHandlerKey.escapeFromEditor.every(isNoteHandlerKey)) {
|
||||
document.activeElement.blur()
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyUp (e) {
|
||||
const keyPressed = Object.assign(this.state.keyPressed, {
|
||||
[e.key]: false
|
||||
})
|
||||
this.setState({ keyPressed })
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className, value, config } = this.props
|
||||
|
||||
@@ -160,6 +181,8 @@ class MarkdownEditor extends React.Component {
|
||||
}
|
||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||
tabIndex='-1'
|
||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||
onKeyUp={(e) => this.handleKeyUp(e)}
|
||||
>
|
||||
<CodeEditor styleName='codeEditor'
|
||||
ref='code'
|
||||
|
||||
@@ -6,6 +6,8 @@ import consts from 'browser/lib/consts'
|
||||
import Raphael from 'raphael'
|
||||
import flowchart from 'flowchart'
|
||||
import SequenceDiagram from 'js-sequence-diagrams'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import fs from 'fs'
|
||||
|
||||
function decodeHTMLEntities (text) {
|
||||
var entities = [
|
||||
@@ -25,6 +27,7 @@ function decodeHTMLEntities (text) {
|
||||
const { remote } = require('electron')
|
||||
const { app } = remote
|
||||
const path = require('path')
|
||||
const dialog = remote.dialog
|
||||
|
||||
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
|
||||
const appPath = 'file://' + (process.env.NODE_ENV === 'production'
|
||||
@@ -49,6 +52,8 @@ body {
|
||||
}
|
||||
code {
|
||||
font-family: ${codeBlockFontFamily.join(', ')};
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
color: #CC305F;
|
||||
}
|
||||
.lineNumber {
|
||||
${lineNumber && 'display: block !important;'}
|
||||
@@ -90,6 +95,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
this.mouseUpHandler = (e) => this.handleMouseUp(e)
|
||||
this.anchorClickHandler = (e) => this.handlePreviewAnchorClick(e)
|
||||
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
|
||||
this.saveAsTextHandler = () => this.handleSaveAsText()
|
||||
this.saveAsMdHandler = () => this.handleSaveAsMd()
|
||||
}
|
||||
|
||||
handlePreviewAnchorClick (e) {
|
||||
@@ -134,6 +141,31 @@ export default class MarkdownPreview extends React.Component {
|
||||
if (this.props.onMouseUp != null) this.props.onMouseUp(e)
|
||||
}
|
||||
|
||||
handleSaveAsText () {
|
||||
this.exportAsDocument('txt')
|
||||
}
|
||||
|
||||
handleSaveAsMd () {
|
||||
this.exportAsDocument('md')
|
||||
}
|
||||
|
||||
exportAsDocument (fileType) {
|
||||
const options = {
|
||||
filters: [
|
||||
{ name: 'Documents', extensions: [fileType]}
|
||||
],
|
||||
properties: ['openFile', 'createDirectory']
|
||||
}
|
||||
dialog.showSaveDialog(remote.getCurrentWindow(), options,
|
||||
(filename) => {
|
||||
if (filename) {
|
||||
fs.writeFile(filename, this.props.value, (err) => {
|
||||
if (err) throw err
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
||||
this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler)
|
||||
@@ -149,12 +181,16 @@ export default class MarkdownPreview extends React.Component {
|
||||
|
||||
this.refs.root.contentWindow.document.addEventListener('mousedown', this.mouseDownHandler)
|
||||
this.refs.root.contentWindow.document.addEventListener('mouseup', this.mouseUpHandler)
|
||||
eventEmitter.on('export:save-text', this.saveAsTextHandler)
|
||||
eventEmitter.on('export:save-md', this.saveAsMdHandler)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.refs.root.contentWindow.document.body.removeEventListener('contextmenu', this.contextMenuHandler)
|
||||
this.refs.root.contentWindow.document.removeEventListener('mousedown', this.mouseDownHandler)
|
||||
this.refs.root.contentWindow.document.removeEventListener('mouseup', this.mouseUpHandler)
|
||||
eventEmitter.off('export:save-text', this.saveAsTextHandler)
|
||||
eventEmitter.off('export:save-md', this.saveAsMdHandler)
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
|
||||
@@ -25,6 +25,7 @@ $list-width = 250px
|
||||
.result
|
||||
absolute left right bottom
|
||||
top $search-height
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
|
||||
.result-nav
|
||||
user-select none
|
||||
@@ -89,6 +90,9 @@ body[data-theme="dark"]
|
||||
.search-input
|
||||
color $ui-dark-text-color
|
||||
|
||||
.result
|
||||
background-color $ui-dark-noteList-backgroundColor
|
||||
|
||||
.result-nav
|
||||
background-color $ui-dark-backgroundColor
|
||||
label
|
||||
|
||||
@@ -67,12 +67,8 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleWindowBlur (e) {
|
||||
let { filter } = this.state
|
||||
filter.type = 'ALL'
|
||||
this.setState({
|
||||
search: '',
|
||||
filter,
|
||||
index: 0
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ class NoteList extends React.Component {
|
||||
this.focusHandler = () => {
|
||||
this.refs.list.focus()
|
||||
}
|
||||
this.alertIfSnippetHnalder = () => {
|
||||
this.alertIfSnippet()
|
||||
}
|
||||
|
||||
this.state = {
|
||||
}
|
||||
@@ -48,6 +51,7 @@ 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)
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
@@ -66,6 +70,7 @@ 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)
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
@@ -305,6 +310,20 @@ class NoteList extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
alertIfSnippet() {
|
||||
let { location } = this.props
|
||||
const targetIndex = _.findIndex(this.notes, (note) => {
|
||||
return `${note.storage}-${note.key}` === location.query.key
|
||||
})
|
||||
if (this.notes[targetIndex].type === 'SNIPPET_NOTE') {
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: 'Sorry!',
|
||||
detail: 'md/text import is available only a markdown note.'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let { location, notes, config } = this.props
|
||||
let sortFunc = config.sortBy === 'CREATED_AT'
|
||||
|
||||
@@ -8,7 +8,7 @@ const electron = require('electron')
|
||||
const { remote, ipcRenderer } = electron
|
||||
const { Menu, MenuItem, dialog } = remote
|
||||
|
||||
const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3]
|
||||
const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
|
||||
|
||||
class StatusBar extends React.Component {
|
||||
updateApp () {
|
||||
|
||||
@@ -24,14 +24,20 @@ class TopBar extends React.Component {
|
||||
this.newNoteHandler = () => {
|
||||
this.handleNewPostButtonClick()
|
||||
}
|
||||
|
||||
this.focusSearchHandler = () => {
|
||||
this.handleOnSearchFocus()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
ee.on('top:new-note', this.newNoteHandler)
|
||||
ee.on('top:focus-search', this.focusSearchHandler)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
ee.off('top:new-note', this.newNoteHandler)
|
||||
ee.off('top:focus-search', this.focusSearchHandler)
|
||||
}
|
||||
|
||||
handleNewPostButtonClick (e) {
|
||||
@@ -244,6 +250,14 @@ class TopBar extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleOnSearchFocus () {
|
||||
if (this.state.searchPopupOpen) {
|
||||
this.refs.search.childNodes[0].blur()
|
||||
} else {
|
||||
this.refs.search.childNodes[0].focus()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
let { config, style, data } = this.props
|
||||
let searchOptionList = this.getOptions()
|
||||
|
||||
@@ -16,7 +16,10 @@ export const DEFAULT_CONFIG = {
|
||||
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
|
||||
hotkey: {
|
||||
toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
|
||||
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
|
||||
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E',
|
||||
noteHandlerKey: {
|
||||
escapeFromEditor: ['Control', 'e']
|
||||
}
|
||||
},
|
||||
ui: {
|
||||
theme: 'default',
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
right 10px
|
||||
height 30px
|
||||
padding 0 25px
|
||||
border $ui-border
|
||||
border-radius 2px
|
||||
color $ui-text-color
|
||||
colorDefaultButton()
|
||||
|
||||
|
||||
@@ -102,9 +102,10 @@ class NewNoteModal extends React.Component {
|
||||
<div styleName='header'>
|
||||
<div styleName='title'>Make a Note</div>
|
||||
</div>
|
||||
<button styleName='closeButton'
|
||||
onClick={(e) => this.handleCloseButtonClick(e)}
|
||||
>Close</button>
|
||||
<button styleName='closeButton' onClick={(e) => this.handleCloseButtonClick(e)}>
|
||||
<div styleName='close-mark'>X</div>
|
||||
<div styleName='close-text'>esc</div>
|
||||
</button>
|
||||
|
||||
<div styleName='control'>
|
||||
<button styleName='control-button'
|
||||
|
||||
@@ -14,20 +14,24 @@
|
||||
color $ui-text-color
|
||||
|
||||
.closeButton
|
||||
height 50px
|
||||
position absolute
|
||||
top 10px
|
||||
background-color transparent
|
||||
color $ui-inactive-text-color
|
||||
border none
|
||||
top 1px
|
||||
right 10px
|
||||
height 30px
|
||||
width 0 25px
|
||||
border $ui-border
|
||||
border-radius 2px
|
||||
color $ui-text-color
|
||||
colorDefaultButton()
|
||||
text-align center
|
||||
width top-bar--height
|
||||
height top-bar--height
|
||||
|
||||
.control
|
||||
padding 25px 15px 15px
|
||||
text-align center
|
||||
|
||||
.close-mark
|
||||
font-size 15px
|
||||
|
||||
.control-button
|
||||
width 220px
|
||||
height 220px
|
||||
@@ -64,11 +68,6 @@ body[data-theme="dark"]
|
||||
border-color $ui-dark-borderColor
|
||||
color $ui-dark-text-color
|
||||
|
||||
.closeButton
|
||||
border-color $ui-dark-borderColor
|
||||
color $ui-dark-text-color
|
||||
colorDarkDefaultButton()
|
||||
|
||||
.control-button
|
||||
border-color $ui-dark-borderColor
|
||||
color $ui-dark-text-color
|
||||
|
||||
@@ -138,6 +138,7 @@ class HotkeyTab extends React.Component {
|
||||
<li><code>Escape</code> (or <code>Esc</code> for short)</li>
|
||||
<li><code>VolumeUp</code>, <code>VolumeDown</code> and <code>VolumeMute</code></li>
|
||||
<li><code>MediaNextTrack</code>, <code>MediaPreviousTrack</code>, <code>MediaStop</code> and <code>MediaPlayPause</code></li>
|
||||
<li><code>Control</code> (or <code>Ctrl</code> for short)</li>
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -59,6 +59,35 @@ var file = {
|
||||
mainWindow.webContents.send('top:new-note')
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Focus Note',
|
||||
accelerator: 'Control + E',
|
||||
click () {
|
||||
mainWindow.webContents.send('detail:focus')
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Export as',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Plain Text (.txt)',
|
||||
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')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
@@ -136,6 +165,33 @@ var view = {
|
||||
click: function () {
|
||||
BrowserWindow.getFocusedWindow().toggleDevTools()
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Next Note',
|
||||
accelerator: 'Control + J',
|
||||
click () {
|
||||
mainWindow.webContents.send('list:next')
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Previous Note',
|
||||
accelerator: 'Control + U',
|
||||
click () {
|
||||
mainWindow.webContents.send('list:prior')
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Focus Search',
|
||||
accelerator: 'Control + S',
|
||||
click () {
|
||||
mainWindow.webContents.send('top:focus-search')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -49,7 +49,12 @@ if (process.platform !== 'linux' || process.env.DESKTOP_SESSION === 'cinnamon')
|
||||
})
|
||||
|
||||
app.on('before-quit', function (e) {
|
||||
config.set('windowsize', mainWindow.getBounds())
|
||||
try {
|
||||
config.set('windowsize', mainWindow.getBounds())
|
||||
} catch (e) {
|
||||
// ignore any errors because an error occurs only on update
|
||||
// refs: https://github.com/BoostIO/Boostnote/issues/243
|
||||
}
|
||||
mainWindow.removeAllListeners()
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "boost",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
"description": "Boostnote",
|
||||
"main": "index.js",
|
||||
"license": "GPL-3.0",
|
||||
|
||||
Reference in New Issue
Block a user