1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 18:26:26 +00:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Dick Choi
3758ea2cf4 bump up version 2016-01-18 01:02:05 +09:00
Rokt33r
e62fc11328 switch folder properly after moving an article to other folder 2016-01-17 08:43:01 +09:00
Dick Choi
3cbfae83c1 fix basefonts again 2016-01-17 07:27:38 +09:00
Dick Choi
57667654ef add Base fonts for windows 2016-01-17 00:19:20 +09:00
Rokt33r
eadd66fa91 decode entities to parse properly by katex 2016-01-16 19:40:03 +09:00
Rokt33r
75cd94a39a refactor MarkdownPreview & protocol must be defined in 'href' to open in browser 2016-01-16 05:28:27 +09:00
Rokt33r
7872bfe19d Merge branch 'markdown' into v0.5.2
* markdown:
  markdown strike bug fixed, no emoji shortcut, checkbox syntax added
2016-01-16 04:54:02 +09:00
Rokt33r
af008e69c2 modify default hotkey(toggle-main-window) 2016-01-16 04:53:49 +09:00
Rokt33r
a549abc20f Refactor event handlers in ArticleEditor 2016-01-16 04:53:06 +09:00
Rokt33r
116344737a right click to quick preview(edit on focus) 2016-01-16 04:24:09 +09:00
Rokt33r
93c03f4e88 add right click to switch edit/preview option to app settings 2016-01-16 02:40:31 +09:00
Rokt33r
445332c27c Find command of ace won't fire blur 2016-01-16 00:58:49 +09:00
Rokt33r
c42e1892d0 fix a little 2016-01-15 23:35:41 +09:00
Rokt33r
b6b526dd57 activity record bug fix 2016-01-15 15:41:37 +09:00
Rokt33r
3ef7f19ffc devtools will automatically removed in production 2016-01-15 15:41:18 +09:00
Rokt33r
9d0d851c2e markdown strike bug fixed, no emoji shortcut, checkbox syntax added 2016-01-13 13:10:46 +09:00
18 changed files with 257 additions and 108 deletions

View File

@@ -21,6 +21,29 @@ export default class CodeEditor extends React.Component {
this.configApplyHandler = (e, config) => this.handleConfigApply(e, config)
this.changeHandler = e => this.handleChange(e)
this.blurHandler = (e) => {
if (e.relatedTarget === null) {
return
}
let isFocusingToSearch = e.relatedTarget.className && e.relatedTarget.className.split(' ').some(clss => {
return clss === 'ace_search_field' || clss === 'ace_searchbtn' || clss === 'ace_replacebtn' || clss === 'ace_searchbtn_close' || clss === 'ace_text-input'
})
if (isFocusingToSearch) {
return
}
if (this.props.onBlur) this.props.onBlur(e)
}
this.afterExecHandler = (e) => {
switch (e.command.name) {
case 'find':
Array.prototype.forEach.call(ReactDOM.findDOMNode(this).querySelectorAll('.ace_search_field, .ace_searchbtn, .ace_replacebtn, .ace_searchbtn_close'), el => {
el.removeEventListener('blur', this.blurHandler)
el.addEventListener('blur', this.blurHandler)
})
}
}
this.state = {
fontSize: config['editor-font-size'],
@@ -49,6 +72,8 @@ export default class CodeEditor extends React.Component {
editor.setReadOnly(!!this.props.readOnly)
editor.setFontSize(this.state.fontSize)
editor.on('blur', this.blurHandler)
editor.commands.addCommand({
name: 'Emacs cursor up',
bindKey: {mac: 'Ctrl-P'},
@@ -67,9 +92,7 @@ export default class CodeEditor extends React.Component {
readOnly: true
})
editor.on('blur', () => {
if (this.props.onBlur) this.props.onBlur()
})
editor.commands.on('afterExec', this.afterExecHandler)
var session = editor.getSession()
let mode = _.findWhere(modes, {name: article.mode})
@@ -92,6 +115,8 @@ export default class CodeEditor extends React.Component {
componentWillUnmount () {
ipc.removeListener('config-apply', this.configApplyHandler)
this.editor.getSession().removeListener('change', this.changeHandler)
this.editor.removeListener('blur', this.blurHandler)
this.editor.commands.removeListener('afterExec', this.afterExecHandler)
}
componentDidUpdate (prevProps, prevState) {
@@ -166,8 +191,8 @@ CodeEditor.propTypes = {
key: PropTypes.string
}),
className: PropTypes.string,
onChange: PropTypes.func,
onBlur: PropTypes.func,
onChange: PropTypes.func,
readOnly: PropTypes.bool
}

View File

@@ -11,10 +11,12 @@ const ipc = electron.ipcRenderer
const katex = window.katex
const OSX = global.process.platform === 'darwin'
const sanitizeOpts = {
allowedTags: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'img', 'span', 'cite', 'del', 'u', 'sub', 'sup' ],
'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'img', 'span', 'cite', 'del', 'u', 'sub', 'sup', 's', 'input', 'label' ],
allowedClasses: {
'a': ['lineAnchor'],
'div': ['math'],
@@ -24,14 +26,20 @@ const sanitizeOpts = {
allowedAttributes: {
a: ['href', 'data-key'],
img: [ 'src' ],
label: ['for'],
input: ['checked', 'type'],
'*': ['id', 'name']
},
transformTags: {
'*': function (tagName, attribs) {
let href = attribs.href
if (tagName === 'input' && attribs.type !== 'checkbox') {
return false
}
if (_.isString(href) && href.match(/^#.+$/)) attribs.href = href.replace(/^#/, '#md-anchor-')
if (attribs.id) attribs.id = 'md-anchor-' + attribs.id
if (attribs.name) attribs.name = 'md-anchor-' + attribs.name
if (attribs.for) attribs.for = 'md-anchor-' + attribs.for
return {
tagName: tagName,
attribs: attribs
@@ -46,7 +54,10 @@ function handleAnchorClick (e) {
}
e.preventDefault()
e.stopPropagation()
shell.openExternal(e.target.href)
let href = e.target.href
if (href.match(/^http:\/\/|https:\/\/|mailto:\/\//)) {
shell.openExternal(href)
}
}
function stopPropagation (e) {
@@ -57,7 +68,7 @@ function stopPropagation (e) {
function math2Katex (display) {
return function (el) {
try {
katex.render(el.innerHTML, el, {display: display})
katex.render(el.innerHTML.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&amp;/g, '&'), el, {display: display})
el.className = 'math-rendered'
} catch (e) {
el.innerHTML = e.message
@@ -111,22 +122,30 @@ export default class MarkdownPreview extends React.Component {
addListener () {
var anchors = ReactDOM.findDOMNode(this).querySelectorAll('a:not(.lineAnchor)')
var inputs = ReactDOM.findDOMNode(this).querySelectorAll('input')
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener('click', handleAnchorClick)
anchors[i].addEventListener('mousedown', stopPropagation)
anchors[i].addEventListener('mouseup', stopPropagation)
}
Array.prototype.forEach.call(anchors, anchor => {
anchor.addEventListener('click', handleAnchorClick)
anchor.addEventListener('mousedown', stopPropagation)
anchor.addEventListener('mouseup', stopPropagation)
})
Array.prototype.forEach.call(inputs, input => {
input.addEventListener('click', stopPropagation)
})
}
removeListener () {
var anchors = ReactDOM.findDOMNode(this).querySelectorAll('a:not(.lineAnchor)')
var inputs = ReactDOM.findDOMNode(this).querySelectorAll('input')
for (var i = 0; i < anchors.length; i++) {
anchors[i].removeEventListener('click', handleAnchorClick)
anchors[i].removeEventListener('mousedown', stopPropagation)
anchors[i].removeEventListener('mouseup', stopPropagation)
}
Array.prototype.forEach.call(anchors, anchor => {
anchor.removeEventListener('click', handleAnchorClick)
anchor.removeEventListener('mousedown', stopPropagation)
anchor.removeEventListener('mouseup', stopPropagation)
})
Array.prototype.forEach.call(inputs, input => {
input.removeEventListener('click', stopPropagation)
})
}
handleClick (e) {
@@ -185,7 +204,7 @@ export default class MarkdownPreview extends React.Component {
dangerouslySetInnerHTML={{__html: ' ' + content}}
style={{
fontSize: this.state.fontSize,
fontFamily: this.state.fontFamily.trim() + ', helvetica, arial, sans-serif'
fontFamily: this.state.fontFamily.trim() + (OSX ? '' : ', meiryo, \'Microsoft YaHei\'') + ', helvetica, arial, sans-serif'
}}
/>
)

View File

@@ -11,9 +11,17 @@ import _ from 'lodash'
import dataStore from 'browser/lib/dataStore'
const electron = require('electron')
const { clipboard, ipcRenderer } = electron
const { clipboard, ipcRenderer, remote } = electron
const path = require('path')
if (process.env.NODE_ENV !== 'production') {
window.addEventListener('keydown', function (e) {
if (e.keyCode === 73 && e.metaKey && e.altKey) {
remote.getCurrentWindow().toggleDevTools()
}
})
}
function hideFinder () {
ipcRenderer.send('hide-finder')
}

View File

@@ -41,7 +41,7 @@ Post all records(except today)
and remove all posted records
*/
export function postRecords (data) {
if (process.NODE_ENV !== 'production') {
if (process.env.NODE_ENV !== 'production') {
console.log('post failed - NOT PRODUCTION ')
return
}

View File

@@ -17,7 +17,9 @@ var md = markdownit({
return str
}
})
md.use(emoji)
md.use(emoji, {
shortcuts: {}
})
md.use(math, {
inlineRenderer: function (str) {
return `<span class='math'>${str}</span>`
@@ -26,6 +28,7 @@ md.use(math, {
return `<div class='math'>${str}</div>`
}
})
md.use(require('markdown-it-checkbox'))
let originalRenderToken = md.renderer.renderToken
md.renderer.renderToken = function renderToken (tokens, idx, options) {

View File

@@ -2,21 +2,44 @@ import React, { PropTypes } from 'react'
import ReactDOM from 'react-dom'
import MarkdownPreview from 'browser/components/MarkdownPreview'
import CodeEditor from 'browser/components/CodeEditor'
import activityRecord from 'browser/lib/activityRecord'
import fetchConfig from 'browser/lib/fetchConfig'
const electron = require('electron')
const ipc = electron.ipcRenderer
export const PREVIEW_MODE = 'PREVIEW_MODE'
export const EDIT_MODE = 'EDIT_MODE'
let config = fetchConfig()
ipc.on('config-apply', function (e, newConfig) {
config = newConfig
})
export default class ArticleEditor extends React.Component {
constructor (props) {
super(props)
this.configApplyHandler = (e, config) => this.handleConfigApply(e, config)
this.isMouseDown = false
this.state = {
status: PREVIEW_MODE,
cursorPosition: null,
firstVisibleRow: null
firstVisibleRow: null,
switchPreview: config['switch-preview'],
isTemporary: false
}
}
componentDidMount () {
console.log(this.state.switchPreview)
ipc.on('config-apply', this.configApplyHandler)
}
componentWillUnmount () {
ipc.removeListener('config-apply', this.configApplyHandler)
}
componentWillReceiveProps (nextProps) {
if (nextProps.article.key !== this.props.article.key) {
this.setState({
@@ -25,6 +48,12 @@ export default class ArticleEditor extends React.Component {
}
}
handleConfigApply (e, newConfig) {
this.setState({
switchPreview: newConfig['switch-preview']
})
}
resetCursorPosition () {
this.setState({
cursorPosition: null,
@@ -35,13 +64,14 @@ export default class ArticleEditor extends React.Component {
})
}
switchPreviewMode () {
switchPreviewMode (isTemporary = false) {
let cursorPosition = this.refs.editor.getCursorPosition()
let firstVisibleRow = this.refs.editor.getFirstVisibleRow()
this.setState({
status: PREVIEW_MODE,
cursorPosition,
firstVisibleRow
firstVisibleRow,
isTemporary: isTemporary
}, function () {
let previewEl = ReactDOM.findDOMNode(this.refs.preview)
let anchors = previewEl.querySelectorAll('.lineAnchor')
@@ -55,43 +85,27 @@ export default class ArticleEditor extends React.Component {
})
}
switchEditMode () {
switchEditMode (isTemporary = false) {
this.setState({
status: EDIT_MODE
status: EDIT_MODE,
isTemporary: false
}, function () {
if (this.state.cursorPosition != null) {
this.refs.editor.moveCursorTo(this.state.cursorPosition.row, this.state.cursorPosition.column)
this.refs.editor.scrollToLine(this.state.firstVisibleRow)
}
this.refs.editor.editor.focus()
if (!isTemporary) activityRecord.emit('ARTICLE_UPDATE', this.props.article)
})
}
handlePreviewMouseDown (e) {
if (e.button === 2) return true
this.isDrag = false
this.isMouseDown = true
this.moveCount = 0
}
handlePreviewMouseMove () {
if (this.isMouseDown) {
this.moveCount++
if (this.moveCount > 5) {
this.isDrag = true
}
handleBlurCodeEditor (e) {
let isFocusingToThis = e.relatedTarget === ReactDOM.findDOMNode(this)
if (isFocusingToThis || this.state.switchPreview !== 'blur') {
return
}
}
handlePreviewMouseUp () {
this.isMouseDown = false
this.moveCount = 0
if (!this.isDrag) {
this.switchEditMode()
}
}
handleBlurCodeEditor () {
let { article } = this.props
if (article.mode === 'markdown') {
this.switchPreviewMode()
@@ -102,36 +116,97 @@ export default class ArticleEditor extends React.Component {
this.props.onChange(value)
}
handleRightClick (e) {
let { article } = this.props
if (this.state.switchPreview === 'rightclick' && article.mode === 'markdown') {
if (this.state.status === EDIT_MODE) this.switchPreviewMode()
else this.switchEditMode()
}
}
handleMouseUp (e) {
switch (this.state.switchPreview) {
case 'blur':
switch (e.button) {
case 0:
this.isMouseDown = false
this.moveCount = 0
if (!this.isDrag) {
this.switchEditMode()
}
break
case 2:
if (this.state.isTemporary) this.switchEditMode(true)
}
break
case 'rightclick':
}
}
handleMouseMove (e) {
if (this.state.switchPreview === 'blur' && this.isMouseDown) {
this.moveCount++
if (this.moveCount > 5) {
this.isDrag = true
}
}
}
handleMouseDowm (e) {
switch (this.state.switchPreview) {
case 'blur':
switch (e.button) {
case 0:
this.isDrag = false
this.isMouseDown = true
this.moveCount = 0
break
case 2:
if (this.state.status === EDIT_MODE && this.props.article.mode === 'markdown') {
this.switchPreviewMode(true)
}
}
break
case 'rightclick':
}
}
render () {
let { article } = this.props
let showPreview = article.mode === 'markdown' && this.state.status === PREVIEW_MODE
if (showPreview) {
return (
<div className='ArticleEditor'>
<MarkdownPreview
ref='preview'
onMouseUp={e => this.handlePreviewMouseUp(e)}
onMouseDown={e => this.handlePreviewMouseDown(e)}
onMouseMove={e => this.handlePreviewMouseMove(e)}
content={article.content}
/>
<div className='ArticleDetail-panel-content-tooltip'>Click to Edit</div>
</div>
)
}
return (
<div className='ArticleEditor'>
<CodeEditor
ref='editor'
onBlur={e => this.handleBlurCodeEditor(e)}
onChange={value => this.handleCodeEditorChange(value)}
article={article}
/>
<div
tabIndex='5'
onContextMenu={e => this.handleRightClick(e)}
onMouseUp={e => this.handleMouseUp(e)}
onMouseMove={e => this.handleMouseMove(e)}
onMouseDown={e => this.handleMouseDowm(e)}
className='ArticleEditor'
>
{showPreview
? <MarkdownPreview
ref='preview'
content={article.content}
/>
: <CodeEditor
ref='editor'
onBlur={e => this.handleBlurCodeEditor(e)}
onChange={value => this.handleCodeEditorChange(value)}
article={article}
/>
}
{article.mode === 'markdown'
? (
<div className='ArticleDetail-panel-content-tooltip'>Press ESC to watch Preview</div>
)
? <div className='ArticleDetail-panel-content-tooltip' children={
showPreview
? this.state.switchPreview === 'blur'
? 'Click to Edit'
: 'Right Click to Edit'
: this.state.switchPreview === 'blur'
? 'Press ESC to Watch Preview'
: 'Right Click to Watch Preview'
}
/>
: null
}
</div>
@@ -145,5 +220,6 @@ ArticleEditor.propTypes = {
key: PropTypes.string,
mode: PropTypes.string
}),
onChange: PropTypes.func
onChange: PropTypes.func,
parent: PropTypes.object
}

View File

@@ -165,7 +165,7 @@ export default class ArticleDetail extends React.Component {
dispatch(updateArticle(article))
let targetFolderKey = this.state.article.FolderKey
let targetFolderKey = e.target.value
if (status.targetFolders.length > 0) {
let targetFolder = _.findWhere(folders, {key: targetFolderKey})
dispatch(switchFolder(targetFolder.name))

View File

@@ -10,6 +10,15 @@ import activityRecord from 'browser/lib/activityRecord'
const electron = require('electron')
const ipc = electron.ipcRenderer
const path = require('path')
const remote = electron.remote
if (process.env.NODE_ENV !== 'production') {
window.addEventListener('keydown', function (e) {
if (e.keyCode === 73 && e.metaKey && e.altKey) {
remote.getCurrentWindow().toggleDevTools()
}
})
}
activityRecord.init()
window.addEventListener('online', function () {

View File

@@ -138,9 +138,9 @@ export default class AppSettingTab extends React.Component {
<label>Editor Font Family</label>
<input valueLink={this.linkState('config.editor-font-family')} onKeyDown={e => this.handleConfigKeyDown(e)} type='text'/>
</div>
<div className='sectionSelect'>
<div className='sectionMultiSelect'>
<label>Editor Indent Style</label>
<div className='sectionSelect-input'>
<div className='sectionMultiSelect-input'>
type
<select valueLink={this.linkState('config.editor-indent-type')}>
<option value='space'>Space</option>
@@ -162,8 +162,15 @@ export default class AppSettingTab extends React.Component {
<label>Preview Font Family</label>
<input valueLink={this.linkState('config.preview-font-family')} onKeyDown={e => this.handleConfigKeyDown(e)} type='text'/>
</div>
<div className='sectionSelect'>
<label>Switching Preview</label>
<select valueLink={this.linkState('config.switch-preview')}>
<option value='blur'>When Editor Blurred</option>
<option value='rightclick'>When Right Clicking</option>
</select>
</div>
{
true// !OSX
global.process.platform === 'win32'
? (
<div className='sectionCheck'>
<label><input onClick={e => this.handleDisableDirectWriteClick(e)} checked={this.state.config['disable-direct-write']} disabled={OSX} type='checkbox'/>Disable Direct Write<span className='sectionCheck-warn'>It will be applied after restarting</span></label>

View File

@@ -60,7 +60,7 @@ iptFocusBorderColor = #369DCD
left 180px
overflow-y auto
&>.section
padding 10px
padding 10px 20px
border-bottom 1px solid borderColor
overflow-y auto
&:nth-last-child(1)
@@ -102,7 +102,6 @@ iptFocusBorderColor = #369DCD
&:focus
border-color iptFocusBorderColor
&>.sectionSelect
margin-bottom 5px
clearfix()
height 33px
@@ -111,7 +110,28 @@ iptFocusBorderColor = #369DCD
padding-left 15px
float left
line-height 33px
.sectionSelect-input
select
float left
width 200px
height 25px
margin-top 4px
border-radius 5px
border 1px solid borderColor
padding 0 10px
font-size 14px
outline none
&:focus
border-color iptFocusBorderColor
&>.sectionMultiSelect
margin-bottom 5px
clearfix()
height 33px
label
width 150px
padding-left 15px
float left
line-height 33px
.sectionMultiSelect-input
float left
select
width 80px

View File

@@ -78,7 +78,7 @@ marked()
font-weight bold
em, i
font-style italic
s
s, del, strike
text-decoration line-through
u
text-decoration underline

View File

@@ -11,6 +11,7 @@ const defaultConfig = {
'editor-indent-size': '4',
'preview-font-size': '14',
'preview-font-family': 'Lato',
'switch-preview': 'blur',
'disable-direct-write': false
}
const configFile = 'config.json'

View File

@@ -75,16 +75,6 @@ var view = {
click: function () {
BrowserWindow.getFocusedWindow().reload()
}
// },
// {
// label: 'Toggle Developer Tools',
// accelerator: (function () {
// if (process.platform === 'darwin') return 'Alt+Command+I'
// else return 'Ctrl+Shift+I'
// })(),
// click: function (item, focusedWindow) {
// if (focusedWindow) BrowserWindow.getFocusedWindow().toggleDevTools()
// }
}
]
}

View File

@@ -10,8 +10,8 @@ const nodeIpc = require('@rokt33r/node-ipc')
const OSX = global.process.platform === 'darwin'
const defaultKeymap = {
toggleFinder: OSX ? 'Cmd + alt + s' : 'Super + alt + s',
toggleMain: OSX ? 'Cmd + alt + v' : 'Super + alt + v'
toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
}
const keymapFilename = 'keymap.json'

View File

@@ -149,16 +149,6 @@ var view = {
click: function () {
BrowserWindow.getFocusedWindow().reload()
}
// },
// {
// label: 'Toggle Developer Tools',
// accelerator: (function () {
// if (process.platform === 'darwin') return 'Alt+Command+I'
// else return 'Ctrl+Shift+I'
// })(),
// click: function (item, focusedWindow) {
// if (focusedWindow) BrowserWindow.getFocusedWindow().toggleDevTools()
// }
}
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "boost",
"version": "0.5.1",
"version": "0.5.2",
"description": "Boostnote",
"main": "index.js",
"scripts": {
@@ -39,6 +39,7 @@
"highlight.js": "^8.9.1",
"lodash": "^3.10.1",
"markdown-it": "^4.4.0",
"markdown-it-checkbox": "^1.1.0",
"markdown-it-emoji": "^1.1.0",
"markdown-it-math": "^3.0.2",
"md5": "^2.0.0",
@@ -72,7 +73,7 @@
"webpack": "^1.12.2",
"webpack-dev-server": "^1.12.0"
},
"optionalDependencies": {},
"optional": false,
"standard": {
"ignore": [],
"globals": [

View File

@@ -32,7 +32,8 @@ var config = {
'markdown-it-emoji',
'fs-jetpack',
'markdown-it-math',
'@rokt33r/sanitize-html'
'@rokt33r/sanitize-html',
'markdown-it-checkbox'
]
}

View File

@@ -1,5 +1,4 @@
const skeleton = require('./webpack-skeleton')
const webpack = require('webpack')
const path = require('path')
var config = Object.assign({}, skeleton, {