1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 09:46:22 +00:00

Merge branch 'master' into export-note-with-images

# Conflicts:
#	browser/components/MarkdownPreview.js
This commit is contained in:
Nikolay Lopin
2018-02-05 12:59:45 +03:00
87 changed files with 695 additions and 2445 deletions

View File

@@ -10,7 +10,6 @@
"theme": "monokai"
},
"hotkey": {
"toggleFinder": "Cmd + Alt + S",
"toggleMain": "Cmd + Alt + L"
},
"isSideNavFolded": false,

View File

@@ -1,7 +1,6 @@
language: node_js
node_js:
- stable
- lts/*
- 6
script:
- npm run lint && npm run test
- 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then npm install -g grunt npm@5.2 && grunt pre-build; fi'

View File

@@ -51,9 +51,10 @@ export default class CodeEditor extends React.Component {
componentDidMount () {
this.value = this.props.value
this.editor = CodeMirror(this.refs.root, {
value: this.props.value,
lineNumbers: true,
lineNumbers: this.props.displayLineNumbers,
lineWrapping: true,
theme: this.props.theme,
indentUnit: this.props.indentSize,
@@ -63,6 +64,7 @@ export default class CodeEditor extends React.Component {
scrollPastEnd: this.props.scrollPastEnd,
inputStyle: 'textarea',
dragDrop: false,
autoCloseBrackets: true,
extraKeys: {
Tab: function (cm) {
const cursor = cm.getCursor()
@@ -70,7 +72,7 @@ export default class CodeEditor extends React.Component {
if (cm.somethingSelected()) cm.indentSelection('add')
else {
const tabs = cm.getOption('indentWithTabs')
if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)\] )?$/)) {
if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) {
cm.execCommand('goLineStart')
if (tabs) {
cm.execCommand('insertTab')
@@ -155,6 +157,10 @@ export default class CodeEditor extends React.Component {
this.editor.setOption('indentWithTabs', this.props.indentType !== 'space')
}
if (prevProps.displayLineNumbers !== this.props.displayLineNumbers) {
this.editor.setOption('lineNumbers', this.props.displayLineNumbers)
}
if (prevProps.scrollPastEnd !== this.props.scrollPastEnd) {
this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd)
}
@@ -229,7 +235,7 @@ export default class CodeEditor extends React.Component {
if (!dataTransferItem.type.match('image')) return
const blob = dataTransferItem.getAsFile()
const reader = new FileReader()
const reader = new window.FileReader()
let base64data
reader.readAsDataURL(blob)

View File

@@ -242,6 +242,7 @@ class MarkdownEditor extends React.Component {
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
displayLineNumbers={config.editor.displayLineNumbers}
scrollPastEnd={config.editor.scrollPastEnd}
storageKey={storageKey}
onChange={(e) => this.handleChange(e)}
@@ -260,7 +261,7 @@ class MarkdownEditor extends React.Component {
codeBlockFontFamily={config.editor.fontFamily}
lineNumber={config.preview.lineNumber}
indentSize={editorIndentSize}
scrollPastEnd={config.editor.scrollPastEnd}
scrollPastEnd={config.preview.scrollPastEnd}
ref='preview'
onContextMenu={(e) => this.handleContextMenu(e)}
tabIndex='0'

View File

@@ -23,13 +23,12 @@ const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
const appPath = 'file://' + (process.env.NODE_ENV === 'production'
? app.getAppPath()
: path.resolve())
const CSS_FILES = [
`${appPath}/node_modules/katex/dist/katex.min.css`,
`${appPath}/node_modules/codemirror/lib/codemirror.css`
]
function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber) {
function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd) {
return `
@font-face {
font-family: 'Lato';
@@ -53,6 +52,7 @@ ${markdownStyle}
body {
font-family: '${fontFamily.join("','")}';
font-size: ${fontSize}px;
${scrollPastEnd && 'padding-bottom: 90vh;'}
}
code {
font-family: '${codeBlockFontFamily.join("','")}';
@@ -258,6 +258,7 @@ export default class MarkdownPreview extends React.Component {
let styles = `
<style id='style'></style>
<link rel="stylesheet" id="codeTheme">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
`
CSS_FILES.forEach((file) => {
@@ -298,15 +299,16 @@ export default class MarkdownPreview extends React.Component {
prevProps.codeBlockTheme !== this.props.codeBlockTheme ||
prevProps.lineNumber !== this.props.lineNumber ||
prevProps.showCopyNotification !== this.props.showCopyNotification ||
prevProps.theme !== this.props.theme) {
prevProps.theme !== this.props.theme ||
prevProps.scrollPastEnd !== this.props.scrollPastEnd) {
this.applyStyle()
this.rewriteIframe()
}
}
getStyleParams () {
const {fontSize, lineNumber, codeBlockTheme} = this.props
let {fontFamily, codeBlockFontFamily} = this.props
const { fontSize, lineNumber, codeBlockTheme, scrollPastEnd } = this.props
let { fontFamily, codeBlockFontFamily } = this.props
fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0
? fontFamily.split(',').map(fontName => fontName.trim()).concat(defaultFontFamily)
: defaultFontFamily
@@ -314,14 +316,14 @@ export default class MarkdownPreview extends React.Component {
? codeBlockFontFamily.split(',').map(fontName => fontName.trim()).concat(defaultCodeBlockFontFamily)
: defaultCodeBlockFontFamily
return {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme}
return {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd}
}
applyStyle () {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams()
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd} = this.getStyleParams()
this.getWindow().document.getElementById('codeTheme').href = this.GetCodeThemeLink(codeBlockTheme)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd)
}
GetCodeThemeLink (theme) {

View File

@@ -62,6 +62,7 @@ class MarkdownSplitEditor extends React.Component {
keyMap={config.editor.keyMap}
fontFamily={config.editor.fontFamily}
fontSize={editorFontSize}
displayLineNumbers={config.editor.displayLineNumbers}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
scrollPastEnd={config.editor.scrollPastEnd}
@@ -78,6 +79,7 @@ class MarkdownSplitEditor extends React.Component {
codeBlockTheme={config.preview.codeBlockTheme}
codeBlockFontFamily={config.editor.fontFamily}
lineNumber={config.preview.lineNumber}
scrollPastEnd={config.preview.scrollPastEnd}
ref='preview'
tabInde='0'
value={value}

View File

@@ -92,7 +92,6 @@ body[data-theme="white"]
color $ui-inactive-text-color
.menu-button--active
@extend .menu-button
color #e74c3c
background-color $ui-button--active-backgroundColor
.menu-button-label
@@ -109,7 +108,6 @@ body[data-theme="white"]
color $ui-text-color
.menu-button-star--active
@extend .menu-button
color #F9BF3B
background-color $ui-button--active-backgroundColor
.menu-button-label
@@ -126,7 +124,6 @@ body[data-theme="white"]
color $ui-text-color
.menu-button-trash--active
@extend .menu-button
color #5D9E36
background-color $ui-button--active-backgroundColor
.menu-button-label

View File

@@ -1,6 +1,6 @@
.percentageBar
position absolute
top 50px
top 72px
right 0px
left 0px
background-color #DADFE1

View File

@@ -105,7 +105,6 @@ a
border-radius 5px
margin -5px
transition .1s
display inline-block
img
vertical-align sub
&:hover

View File

@@ -1,156 +0,0 @@
$search-height = 50px
$nav-width = 175px
$list-width = 250px
.root
absolute top left right bottom
.search
height $search-height
padding 10px
box-sizing border-box
border-bottom $ui-border
text-align center
.search-input
height 30px
width 100%
margin 0 auto
font-size 18px
border none
outline none
text-align center
background-color transparent
.result
absolute left right bottom
top $search-height
background-color $ui-noteDetail-backgroundColor
.result-nav
user-select none
absolute left top bottom
width $nav-width
background-color $ui-backgroundColor
.result-nav-filter
margin-bottom 10px
.result-nav-filter-option
height 25px
line-height 25px
padding 0 10px
label
cursor pointer
.result-nav-menu
navButtonColor()
height 32px
padding 0 10px
font-size 14px
width 100%
outline none
text-align left
line-height 32px
box-sizing border-box
cursor pointer
.result-nav-menu--active
@extend .result-nav-menu
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
&:hover
background-color $ui-button--active-backgroundColor
.result-nav-storageList
absolute bottom left right
top 110px + 32px + 10px + 10px + 20px
overflow-y auto
.result-list
user-select none
absolute top bottom
left $nav-width
width $list-width
box-sizing border-box
overflow-y auto
box-shadow 2px 0 15px -8px #b1b1b1
z-index 1
.result-detail
absolute top bottom right
left $nav-width + $list-width
background-color $ui-noteDetail-backgroundColor
body[data-theme="dark"]
.root
background-color $ui-dark-backgroundColor
.search
border-color $ui-dark-borderColor
.search-input
color $ui-dark-text-color
.result
background-color $ui-dark-noteList-backgroundColor
.result-nav
background-color $ui-dark-backgroundColor
label
color $ui-dark-text-color
.result-nav-menu
navDarkButtonColor()
.result-nav-menu--active
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-button--active-color
&:hover
background-color $ui-dark-button--active-backgroundColor
.result-list
border-color $ui-dark-borderColor
box-shadow none
top 0
.result-detail
absolute top bottom right
left $nav-width + $list-width
background-color $ui-dark-noteDetail-backgroundColor
body[data-theme="solarized-dark"]
.root
background-color $ui-solarized-dark-backgroundColor
.search
border-color $ui-solarized-dark-borderColor
.search-input
color $ui-dark-text-color
.result
background-color $ui-solarized-dark-backgroundColor
.result-nav
background-color $ui-solarized-dark-backgroundColor
label
color $ui-dark-text-color
.result-nav-menu
navDarkButtonColor()
.result-nav-menu--active
background-color $ui-solarized-dark-button-backgroundColor
color $ui-dark-button--active-color
&:hover
background-color $ui-dark-button--active-backgroundColor
.result-list
border-color $ui-solarized-dark-borderColor
box-shadow none
top 0
.result-detail
absolute top bottom right
left $nav-width + $list-width
background-color $ui-solarized-dark-backgroundColor

View File

@@ -1,211 +0,0 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './NoteDetail.styl'
import MarkdownPreview from 'browser/components/MarkdownPreview'
import MarkdownEditor from 'browser/components/MarkdownEditor'
import CodeEditor from 'browser/components/CodeEditor'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import { findStorage } from 'browser/lib/findStorage'
const electron = require('electron')
const { clipboard } = electron
const path = require('path')
function pass (name) {
switch (name) {
case 'ejs':
return 'Embedded Javascript'
case 'html_ruby':
return 'Embedded Ruby'
case 'objectivec':
return 'Objective C'
case 'text':
return 'Plain Text'
default:
return name
}
}
function notify (title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
}
return new window.Notification(title, options)
}
class NoteDetail extends React.Component {
constructor (props) {
super(props)
this.state = {
snippetIndex: 0
}
}
componentWillReceiveProps (nextProps) {
if (nextProps.note !== this.props.note) {
this.setState({
snippetIndex: 0
}, () => {
if (nextProps.note.type === 'SNIPPET_NOTE') {
nextProps.note.snippets.forEach((snippet, index) => {
this.refs['code-' + index].reload()
})
}
})
}
}
selectPriorSnippet () {
const { note } = this.props
if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
this.setState({
snippetIndex: (this.state.snippetIndex + note.snippets.length - 1) % note.snippets.length
})
}
}
selectNextSnippet () {
const { note } = this.props
if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
this.setState({
snippetIndex: (this.state.snippetIndex + 1) % note.snippets.length
})
}
}
saveToClipboard () {
const { note } = this.props
if (note.type === 'MARKDOWN_NOTE') {
clipboard.writeText(note.content)
} else {
clipboard.writeText(note.snippets[this.state.snippetIndex].content)
}
notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
}
handleTabButtonClick (e, index) {
this.setState({
snippetIndex: index
})
}
render () {
const { note, config } = this.props
if (note == null) {
return (
<div styleName='root' />
)
}
let editorFontSize = parseInt(config.editor.fontSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
let editorIndentSize = parseInt(config.editor.indentSize, 10)
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
const storage = findStorage(note.storage)
if (note.type === 'SNIPPET_NOTE') {
const tabList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
return <div styleName={isActive
? 'tabList-item--active'
: 'tabList-item'
}
key={index}
>
<button styleName='tabList-item-button'
onClick={(e) => this.handleTabButtonClick(e, index)}
>
{snippet.name.trim().length > 0
? snippet.name
: <span styleName='tabList-item-unnamed'>
Unnamed
</span>
}
</button>
</div>
})
const viewList = note.snippets.map((snippet, index) => {
const isActive = this.state.snippetIndex === index
let syntax = CodeMirror.findModeByName(pass(snippet.mode))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
return <div styleName='tabView'
key={index}
style={{zIndex: isActive ? 5 : 4}}
>
{snippet.mode === 'markdown'
? <MarkdownEditor styleName='tabView-content'
config={config}
value={snippet.content}
ref={'code-' + index}
storageKey={note.storage}
/>
: <CodeEditor styleName='tabView-content'
mode={snippet.mode}
value={snippet.content}
theme={config.editor.theme}
fontFamily={config.editor.fontFamily}
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
keyMap={config.editor.keyMap}
scrollPastEnd={config.editor.scrollPastEnd}
readOnly
ref={'code-' + index}
/>
}
</div>
})
return (
<div styleName='root'>
<div styleName='description'>
<textarea styleName='description-textarea'
style={{
fontFamily: config.preview.fontFamily,
fontSize: parseInt(config.preview.fontSize, 10)
}}
ref='description'
placeholder='Description...'
value={note.description}
readOnly
/>
</div>
<div styleName='tabList'>
{tabList}
</div>
{viewList}
</div>
)
}
return (
<MarkdownPreview styleName='root'
theme={config.ui.theme}
fontSize={config.preview.fontSize}
fontFamily={config.preview.fontFamily}
codeBlockTheme={config.preview.codeBlockTheme}
codeBlockFontFamily={config.editor.fontFamily}
lineNumber={config.preview.lineNumber}
indentSize={editorIndentSize}
value={note.content}
showCopyNotification={config.ui.showCopyNotification}
storagePath={storage.path}
/>
)
}
}
NoteDetail.propTypes = {
}
export default CSSModules(NoteDetail, styles)

View File

@@ -1,129 +0,0 @@
@import('../main/Detail/DetailVars.styl')
.root
absolute top bottom left right
bottom 30px
margin 0 25px
height 100%
width 365px
background-color $ui-noteDetail-backgroundColor
.description
absolute top left right
height 80px
box-sizing border-box
.description-textarea
display block
height 100%
width 100%
resize none
border none
padding 10px
line-height 1.6
box-sizing border-box
background-color $ui-noteDetail-backgroundColor
.tabList
absolute left right
top 80px
box-sizing border-box
height 30px
display flex
background-color $ui-noteDetail-backgroundColor
.tabList-item
position relative
flex 1
overflow hidden
&:hover
background-color $ui-button--hover-backgroundColorg
.tabList-item--active
@extend .tabList-item
border-bottom $ui-border
.tabList-item-button
width 100%
height 29px
overflow ellipsis
text-align left
padding-right 30px
padding-left 10px
border none
background-color transparent
transition 0.15s
&:hover
background-color $ui-button--hover-backgroundColor
.tabView
absolute left right bottom
top 130px
.tabView-content
absolute top left right bottom
box-sizing border-box
height 100%
width 100%
body[data-theme="dark"]
.root
background-color $ui-dark-noteDetail-backgroundColor
.description
border-color $ui-dark-borderColor
background-color $ui-dark-noteDetail-backgroundColor
.description-textarea
background-color $ui-dark-noteDetail-backgroundColor
color white
.tabList
background-color $ui-dark-noteDetail-backgroundColor
.tabList-item
border-color $ui-dark-borderColor
&:hover
background-color $ui-dark-button--hover-backgroundColor
.tabList-item-button
border none
color $ui-dark-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
&:hover
color white
background-color $ui-dark-button--hover-backgroundColor
body[data-theme="solarized-dark"]
.root
background-color $ui-solarized-dark-backgroundColor
.description
border-color $ui-dark-borderColor
background-color $ui-solarized-dark-backgroundColor
.description-textarea
background-color $ui-solarized-dark-backgroundColor
color white
.tabList
background-color $ui-solarized-dark-backgroundColor
.tabList-item
border-color $ui-dark-borderColor
&:hover
background-color $ui-dark-button--hover-backgroundColor
.tabList-item-button
border none
color $ui-dark-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
&:hover
color white
background-color $ui-dark-button--hover-backgroundColor

View File

@@ -1,90 +0,0 @@
import React from 'react'
import NoteItem from 'browser/components/NoteItem'
import moment from 'moment'
class NoteList extends React.Component {
constructor (props) {
super(props)
this.state = {
range: 0
}
}
componentWillReceiveProps (nextProps) {
if (this.props.search !== nextProps.search) {
this.resetScroll()
}
}
componentDidUpdate () {
const { index } = this.props
if (index > -1) {
const list = this.refs.root
const item = list.childNodes[index]
if (item == null) return null
const overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
if (overflowBelow) {
list.scrollTop = item.offsetTop + item.clientHeight - list.clientHeight
}
const overflowAbove = list.scrollTop > item.offsetTop
if (overflowAbove) {
list.scrollTop = item.offsetTop
}
}
}
resetScroll () {
this.refs.root.scrollTop = 0
this.setState({
range: 0
})
}
handleScroll (e) {
const { notes } = this.props
if (e.target.offsetHeight + e.target.scrollTop > e.target.scrollHeight - 100 && notes.length > this.state.range * 10 + 10) {
this.setState({
range: this.state.range + 1
})
}
}
render () {
const { notes, index } = this.props
const notesList = notes
.slice(0, 10 + 10 * this.state.range)
.map((note, _index) => {
const isActive = (index === _index)
const key = `${note.storage}-${note.key}`
const dateDisplay = moment(note.updatedAt).fromNow()
return (
<NoteItem
isActive={isActive}
note={note}
dateDisplay={dateDisplay}
key={key}
handleNoteClick={(e) => this.props.handleNoteClick(e, _index)}
/>
)
})
return (
<div className={this.props.className}
onScroll={(e) => this.handleScroll(e)}
ref='root'
>
{notesList}
</div>
)
}
}
NoteList.propTypes = {
}
export default NoteList

View File

@@ -1,77 +0,0 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StorageSection.styl'
import StorageItem from 'browser/components/StorageItem'
class StorageSection extends React.Component {
constructor (props) {
super(props)
this.state = {
isOpen: true
}
}
handleToggleButtonClick (e) {
this.setState({
isOpen: !this.state.isOpen
})
}
handleHeaderClick (e) {
const { storage } = this.props
this.props.handleStorageButtonClick(e, storage.key)
}
handleFolderClick (e, folder) {
const { storage } = this.props
this.props.handleFolderButtonClick(e, storage.key, folder.key)
}
render () {
const { storage, filter } = this.props
const folderList = storage.folders
.map(folder => (
<StorageItem
key={folder.key}
isActive={filter.type === 'FOLDER' && filter.folder === folder.key && filter.storage === storage.key}
handleButtonClick={(e) => this.handleFolderClick(e, folder)}
folderName={folder.name}
folderColor={folder.color}
isFolded={false}
/>
))
return (
<div styleName='root'>
<div styleName='header'>
<button styleName='header-toggleButton'
onClick={(e) => this.handleToggleButtonClick(e)}
>
<i className={this.state.isOpen
? 'fa fa-caret-down'
: 'fa fa-caret-right'
}
/>
</button>
<button styleName={filter.type === 'STORAGE' && filter.storage === storage.key
? 'header-name--active'
: 'header-name'
}
onClick={(e) => this.handleHeaderClick(e)}
>{storage.name}</button>
</div>
{this.state.isOpen &&
<div styleName='folderList'>
{folderList}
</div>
}
</div>
)
}
}
StorageSection.propTypes = {
}
export default CSSModules(StorageSection, styles)

View File

@@ -1,85 +0,0 @@
.root
position relative
.header
height 26px
.header-toggleButton
absolute top left
width 25px
height 26px
navButtonColor()
border none
outline none
.header-name
display block
height 26px
navButtonColor()
padding 0 10px 0 25px
font-size 14px
width 100%
text-align left
line-height 26px
box-sizing border-box
cursor pointer
outline none
.header-name--active
@extend .header-name
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
&:hover
background-color $ui-button--active-backgroundColor
.folderList-item
display block
width 100%
height 26px
navButtonColor()
padding 0 10px 0 25px
font-size 14px
width 100%
text-align left
line-height 26px
box-sizing border-box
cursor pointer
outline none
padding 0 10px
margin 2px 0
height 26px
line-height 26px
border-width 0 0 0 6px
border-style solid
border-color transparent
.folderList-item--active
@extend .folderList-item
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
&:hover
background-color $ui-button--active-backgroundColor
body[data-theme="dark"]
.header-toggleButton
navDarkButtonColor()
.header-name
navDarkButtonColor()
.header-name--active
@extend .header-name
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
&:hover
background-color $ui-button--active-backgroundColor
.folderList-item
navDarkButtonColor()
border-width 0 0 0 6px
border-style solid
border-color transparent
.folderList-item--active
@extend .folderList-item
background-color $ui-button--active-backgroundColor
color $ui-button--active-color
&:hover
background-color $ui-button--active-backgroundColor

View File

@@ -1,357 +0,0 @@
import PropTypes from 'prop-types'
import React from 'react'
import ReactDOM from 'react-dom'
import { connect, Provider } from 'react-redux'
import _ from 'lodash'
import store from './store'
import CSSModules from 'browser/lib/CSSModules'
import styles from './FinderMain.styl'
import StorageSection from './StorageSection'
import NoteList from './NoteList'
import NoteDetail from './NoteDetail'
import SideNavFilter from 'browser/components/SideNavFilter'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
require('!!style!css!stylus?sourceMap!../main/global.styl')
require('../lib/customMeta')
require('./ipcClient.js')
const electron = require('electron')
const { remote } = electron
const { Menu } = remote
function hideFinder () {
const finderWindow = remote.getCurrentWindow()
if (global.process.platform === 'win32') {
finderWindow.blur()
finderWindow.hide()
}
if (global.process.platform === 'darwin') {
Menu.sendActionToFirstResponder('hide:')
}
remote.getCurrentWindow().hide()
}
require('!!style!css!stylus?sourceMap!../styles/finder/index.styl')
class FinderMain extends React.Component {
constructor (props) {
super(props)
this.state = {
search: '',
index: 0,
filter: {
includeSnippet: true,
includeMarkdown: false,
type: 'ALL',
storage: null,
folder: null
}
}
this.focusHandler = (e) => this.handleWindowFocus(e)
this.blurHandler = (e) => this.handleWindowBlur(e)
}
componentDidMount () {
this.refs.search.focus()
window.addEventListener('focus', this.focusHandler)
window.addEventListener('blur', this.blurHandler)
}
componentWillUnmount () {
window.removeEventListener('focus', this.focusHandler)
window.removeEventListener('blur', this.blurHandler)
}
handleWindowFocus (e) {
this.refs.search.focus()
}
handleWindowBlur (e) {
this.setState({
search: ''
})
}
handleKeyDown (e) {
this.refs.search.focus()
if (e.keyCode === 9) {
if (e.shiftKey) {
this.refs.detail.selectPriorSnippet()
} else {
this.refs.detail.selectNextSnippet()
}
e.preventDefault()
}
if (e.keyCode === 38) {
this.selectPrevious()
e.preventDefault()
}
if (e.keyCode === 40) {
this.selectNext()
e.preventDefault()
}
if (e.keyCode === 13) {
this.refs.detail.saveToClipboard()
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('COPY_FINDER')
hideFinder()
e.preventDefault()
}
if (e.keyCode === 27) {
hideFinder()
e.preventDefault()
}
if (e.keyCode === 91 || e.metaKey) {
return
}
}
handleSearchChange (e) {
this.setState({
search: e.target.value,
index: 0
})
}
selectArticle (article) {
this.setState({currentArticle: article})
}
selectPrevious () {
if (this.state.index > 0) {
this.setState({
index: this.state.index - 1
})
}
}
selectNext () {
if (this.state.index < this.noteCount - 1) {
this.setState({
index: this.state.index + 1
})
}
}
handleOnlySnippetCheckboxChange (e) {
const { filter } = this.state
filter.includeSnippet = e.target.checked
this.setState({
filter: filter,
index: 0
}, () => {
this.refs.search.focus()
})
}
handleOnlyMarkdownCheckboxChange (e) {
const { filter } = this.state
filter.includeMarkdown = e.target.checked
this.refs.list.resetScroll()
this.setState({
filter: filter,
index: 0
}, () => {
this.refs.search.focus()
})
}
handleAllNotesButtonClick (e) {
const { filter } = this.state
filter.type = 'ALL'
this.refs.list.resetScroll()
this.setState({
filter,
index: 0
}, () => {
this.refs.search.focus()
})
}
handleStarredButtonClick (e) {
const { filter } = this.state
filter.type = 'STARRED'
this.refs.list.resetScroll()
this.setState({
filter,
index: 0
}, () => {
this.refs.search.focus()
})
}
handleStorageButtonClick (e, storage) {
const { filter } = this.state
filter.type = 'STORAGE'
filter.storage = storage
this.refs.list.resetScroll()
this.setState({
filter,
index: 0
}, () => {
this.refs.search.focus()
})
}
handleFolderButtonClick (e, storage, folder) {
const { filter } = this.state
filter.type = 'FOLDER'
filter.storage = storage
filter.folder = folder
this.refs.list.resetScroll()
this.setState({
filter,
index: 0
}, () => {
this.refs.search.focus()
})
}
handleNoteClick (e, index) {
this.setState({
index
}, () => {
this.refs.search.focus()
})
}
render () {
const { data, config } = this.props
const { filter, search } = this.state
const storageList = []
for (const key in data.storageMap) {
const storage = data.storageMap[key]
const item = (
<StorageSection
filter={filter}
storage={storage}
key={storage.key}
handleStorageButtonClick={(e, storage) => this.handleStorageButtonClick(e, storage)}
handleFolderButtonClick={(e, storage, folder) => this.handleFolderButtonClick(e, storage, folder)}
/>
)
storageList.push(item)
}
let notes = []
let noteIds
switch (filter.type) {
case 'STORAGE':
noteIds = data.storageNoteMap[filter.storage]
break
case 'FOLDER':
noteIds = data.folderNoteMap[filter.storage + '-' + filter.folder]
break
case 'STARRED':
noteIds = data.starredSet
}
if (noteIds != null) {
noteIds.forEach((id) => {
notes.push(data.noteMap[id])
})
} else {
for (const key in data.noteMap) {
notes.push(data.noteMap[key])
}
}
if (!filter.includeSnippet && filter.includeMarkdown) {
notes = notes.filter((note) => note.type === 'MARKDOWN_NOTE')
} else if (filter.includeSnippet && !filter.includeMarkdown) {
notes = notes.filter((note) => note.type === 'SNIPPET_NOTE')
}
if (search.trim().length > 0) {
const needle = new RegExp(_.escapeRegExp(search.trim()), 'i')
notes = notes.filter((note) => note.title.match(needle))
}
notes = notes
.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt))
const activeNote = notes[this.state.index]
this.noteCount = notes.length
return (
<div className='Finder'
styleName='root'
ref='-1'
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='search'>
<input
styleName='search-input'
ref='search'
value={search}
placeholder='Search...'
onChange={(e) => this.handleSearchChange(e)}
/>
</div>
<div styleName='result'>
<div styleName='result-nav'>
<div styleName='result-nav-filter'>
<div styleName='result-nav-filter-option'>
<label>
<input type='checkbox'
checked={filter.includeSnippet}
onChange={(e) => this.handleOnlySnippetCheckboxChange(e)}
/> Only Snippets</label>
</div>
<div styleName='result-nav-filter-option'>
<label>
<input type='checkbox'
checked={filter.includeMarkdown}
onChange={(e) => this.handleOnlyMarkdownCheckboxChange(e)}
/> Only Markdown</label>
</div>
</div>
<SideNavFilter
isHomeActive={filter.type === 'ALL'}
handleAllNotesButtonClick={(e) => this.handleAllNotesButtonClick(e)}
isStarredActive={filter.type === 'STARRED'}
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
/>
<div styleName='result-nav-storageList'>
{storageList}
</div>
</div>
<NoteList styleName='result-list'
storageMap={data.storageMap}
notes={notes}
ref='list'
search={search}
index={this.state.index}
handleNoteClick={(e, _index) => this.handleNoteClick(e, _index)}
/>
<div styleName='result-detail'>
<NoteDetail
note={activeNote}
config={config}
ref='detail'
/>
</div>
</div>
</div>
)
}
}
FinderMain.propTypes = {
dispatch: PropTypes.func
}
var Finder = connect((x) => x)(CSSModules(FinderMain, styles))
function refreshData () {
// let data = dataStore.getData(true)
}
ReactDOM.render((
<Provider store={store}>
<Finder />
</Provider>
), document.getElementById('content'), function () {
refreshData()
})

View File

@@ -1,126 +0,0 @@
const nodeIpc = require('node-ipc')
const { remote, ipcRenderer } = require('electron')
const { app, Menu } = remote
const path = require('path')
const store = require('./store')
const consts = require('browser/lib/consts')
nodeIpc.config.id = 'finder'
nodeIpc.config.retry = 1500
nodeIpc.config.silent = true
function killFinder () {
const finderWindow = remote.getCurrentWindow()
finderWindow.removeAllListeners()
if (global.process.platform === 'darwin') {
// Only OSX has another app process.
nodeIpc.of.node.emit('quit-from-finder')
} else {
finderWindow.close()
}
}
function toggleFinder () {
const finderWindow = remote.getCurrentWindow()
if (global.process.platform === 'darwin') {
if (finderWindow.isVisible()) {
finderWindow.hide()
Menu.sendActionToFirstResponder('hide:')
} else {
nodeIpc.of.node.emit('request-data-from-finder')
finderWindow.show()
}
} else {
if (finderWindow.isVisible()) {
finderWindow.blur()
finderWindow.hide()
} else {
nodeIpc.of.node.emit('request-data-from-finder')
finderWindow.show()
finderWindow.focus()
}
}
}
nodeIpc.connectTo(
'node',
path.join(app.getPath('userData'), 'boostnote.service'),
function () {
nodeIpc.of.node.on('error', function (err) {
console.log(err)
})
nodeIpc.of.node.on('connect', function () {
console.log('Conncted successfully')
})
nodeIpc.of.node.on('disconnect', function () {
console.log('disconnected')
})
nodeIpc.of.node.on('open-finder', function () {
toggleFinder()
})
ipcRenderer.on('open-finder-from-tray', function () {
toggleFinder()
})
ipcRenderer.on('open-main-from-tray', function () {
nodeIpc.of.node.emit('open-main-from-finder')
})
ipcRenderer.on('quit-from-tray', function () {
nodeIpc.of.node.emit('quit-from-finder')
killFinder()
})
nodeIpc.of.node.on('throttle-data', function (payload) {
console.log('Received data from Main renderer')
store.default.dispatch({
type: 'THROTTLE_DATA',
data: payload
})
})
nodeIpc.of.node.on('config-renew', function (payload) {
const { config } = payload
if (config.ui.theme === 'dark') {
document.body.setAttribute('data-theme', 'dark')
} else if (config.ui.theme === 'white') {
document.body.setAttribute('data-theme', 'white')
} else if (config.ui.theme === 'solarized-dark') {
document.body.setAttribute('data-theme', 'solarized-dark')
} else {
document.body.setAttribute('data-theme', 'default')
}
let editorTheme = document.getElementById('editorTheme')
if (editorTheme == null) {
editorTheme = document.createElement('link')
editorTheme.setAttribute('id', 'editorTheme')
editorTheme.setAttribute('rel', 'stylesheet')
document.head.appendChild(editorTheme)
}
config.editor.theme = consts.THEMES.some((theme) => theme === config.editor.theme)
? config.editor.theme
: 'default'
if (config.editor.theme !== 'default') {
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
}
store.default.dispatch({
type: 'SET_CONFIG',
config: config
})
})
nodeIpc.of.node.on('quit-finder-app', function () {
nodeIpc.of.node.emit('quit-finder-app-confirm')
killFinder()
})
}
)
const ipc = {}
module.exports = ipc

View File

@@ -1,51 +0,0 @@
import { combineReducers, createStore } from 'redux'
import { routerReducer } from 'react-router-redux'
import { DEFAULT_CONFIG } from 'browser/main/lib/ConfigManager'
const defaultData = {
storageMap: {},
noteMap: {},
starredSet: [],
storageNoteMap: {},
folderNoteMap: {},
tagNoteMap: {}
}
function data (state = defaultData, action) {
switch (action.type) {
case 'THROTTLE_DATA':
console.log(action)
state = action.data
}
return state
}
function config (state = DEFAULT_CONFIG, action) {
switch (action.type) {
case 'INIT_CONFIG':
case 'SET_CONFIG':
return Object.assign({}, state, action.config)
case 'SET_IS_SIDENAV_FOLDED':
state.isSideNavFolded = action.isFolded
return Object.assign({}, state)
case 'SET_ZOOM':
state.zoom = action.zoom
return Object.assign({}, state)
case 'SET_LIST_WIDTH':
state.listWidth = action.listWidth
return Object.assign({}, state)
case 'SET_UI':
return Object.assign({}, state, action.config)
}
return state
}
const reducer = combineReducers({
data,
config,
routing: routerReducer
})
const store = createStore(reducer)
export default store

View File

@@ -2,7 +2,7 @@ const { remote } = require('electron')
const { Menu, MenuItem } = remote
function popup (templates) {
let menu = new Menu()
const menu = new Menu()
templates.forEach((item) => {
menu.append(new MenuItem(item))
})

View File

@@ -31,10 +31,10 @@ var md = markdownit({
return `<pre class="sequence">${str}</pre>`
}
return '<pre class="code">' +
createGutter(str) +
'<code class="' + lang + '">' +
str +
'</code></pre>'
createGutter(str) +
'<code class="' + lang + '">' +
str +
'</code></pre>'
}
})
md.use(emoji, {
@@ -57,7 +57,7 @@ md.use(math, {
blockRenderer: function (str) {
let output = ''
try {
output = katex.renderToString(str.trim(), {displayMode: true})
output = katex.renderToString(str.trim(), { displayMode: true })
} catch (err) {
output = `<div class="katex-error">${err.message}</div>`
}
@@ -76,7 +76,17 @@ md.use(require('markdown-it-named-headers'), {
}
})
md.use(require('markdown-it-kbd'))
md.use(require('markdown-it-plantuml'))
const deflate = require('markdown-it-plantuml/lib/deflate')
md.use(require('markdown-it-plantuml'), '', {
generateSource: function (umlCode) {
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`@startuml\n${s}\n@enduml`, 9)
)
return `http://www.plantuml.com/plantuml/svg/${zippedCode}`
}
})
// Override task item
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
@@ -110,7 +120,7 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
state.line = nextLine
token = state.push('paragraph_open', 'p', 1)
token.map = [ startLine, state.line ]
token.map = [startLine, state.line]
if (state.parentType === 'list') {
const match = content.match(/^\[( |x)\] ?(.+)/i)
@@ -121,7 +131,7 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
token = state.push('inline', '', 0)
token.content = content
token.map = [ startLine, state.line ]
token.map = [startLine, state.line]
token.children = []
token = state.push('paragraph_close', 'p', -1)

View File

@@ -20,11 +20,13 @@
body[data-theme="dark"]
.root
background-color $ui-dark-backgroundColor
border-left 1px solid $ui-dark-borderColor
.empty-message
color $ui-dark-inactive-text-color
body[data-theme="solarized-dark"]
.root
background-color $ui-solarized-dark-noteDetail-backgroundColor
border-left 1px solid $ui-solarized-dark-borderColor
.empty-message
color $ui-solarized-dark-text-color

View File

@@ -3,20 +3,14 @@
border solid 1px transparent
vertical-align middle
border-radius 2px
height 30px
transition 0.15s
user-select none
margin-right 10px
&:hover
background-color $ui-button--hover-backgroundColor
.root--search, .root--focus
@extend .root
background-color $ui-noteDetail-backgroundColor = #fff
border-color $ui-input--focus-borderColor
width 154px
height 30px
&:hover
border-color $ui-input--focus-borderColor = #fff
.idle
position relative

View File

@@ -8,8 +8,8 @@
tooltip()
position absolute
pointer-events none
top 26px
right 0
top 50px
right 70px
z-index 200
padding 5px
line-height normal

View File

@@ -8,8 +8,8 @@
tooltip()
position absolute
pointer-events none
top 26px
right 0
top 50px
right 20px
z-index 200
padding 5px
line-height normal

View File

@@ -2,82 +2,97 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
import copy from 'copy-to-clipboard'
const InfoPanel = ({
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print
}) => (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>MODIFICATION DATE</p>
</div>
class InfoPanel extends React.Component {
copyNoteLink () {
const {noteLink} = this.props
this.refs.noteLink.select()
copy(noteLink)
}
<hr />
{type === 'SNIPPET_NOTE'
? ''
: <div styleName='count-wrap'>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{wordCount}</p>
<p styleName='infoPanel-sub-count'>Words</p>
render () {
const {
storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print
} = this.props
return (
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>MODIFICATION DATE</p>
</div>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{letterCount}</p>
<p styleName='infoPanel-sub-count'>Letters</p>
<hr />
{type === 'SNIPPET_NOTE'
? ''
: <div styleName='count-wrap'>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{wordCount}</p>
<p styleName='infoPanel-sub-count'>Words</p>
</div>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{letterCount}</p>
<p styleName='infoPanel-sub-count'>Letters</p>
</div>
</div>
}
{type === 'SNIPPET_NOTE'
? ''
: <hr />
}
<div>
<p styleName='infoPanel-default'>{storageName}</p>
<p styleName='infoPanel-sub'>STORAGE</p>
</div>
<div>
<p styleName='infoPanel-default'>{folderName}</p>
<p styleName='infoPanel-sub'>FOLDER</p>
</div>
<div>
<p styleName='infoPanel-default'>{createdAt}</p>
<p styleName='infoPanel-sub'>CREATION DATE</p>
</div>
<div>
<input styleName='infoPanel-noteLink' ref='noteLink' value={noteLink} onClick={(e) => { e.target.select() }} />
<button onClick={() => this.copyNoteLink()} styleName='infoPanel-copyButton'>
<i className='fa fa-clipboard' />
</button>
<p styleName='infoPanel-sub'>NOTE LINK</p>
</div>
<hr />
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<i className='fa fa-html5' />
<p>.html</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e)}>
<i className='fa fa-print' />
<p>Print</p>
</button>
</div>
</div>
}
{type === 'SNIPPET_NOTE'
? ''
: <hr />
}
<div>
<p styleName='infoPanel-default'>{storageName}</p>
<p styleName='infoPanel-sub'>STORAGE</p>
</div>
<div>
<p styleName='infoPanel-default'>{folderName}</p>
<p styleName='infoPanel-sub'>FOLDER</p>
</div>
<div>
<p styleName='infoPanel-default'>{createdAt}</p>
<p styleName='infoPanel-sub'>CREATION DATE</p>
</div>
<div>
<input styleName='infoPanel-noteLink' value={noteLink} onClick={(e) => { e.target.select() }} />
<p styleName='infoPanel-sub'>NOTE LINK</p>
</div>
<hr />
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o' />
<p>.md</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o' />
<p>.txt</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<i className='fa fa-html5' />
<p>.html</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e)}>
<i className='fa fa-print' />
<p>Print</p>
</button>
</div>
</div>
)
)
}
}
InfoPanel.propTypes = {
storageName: PropTypes.string.isRequired,

View File

@@ -11,11 +11,10 @@
.control-infoButton-panel
z-index 200
margin-top 0px
right 0
right 25px
position absolute
padding 20px 25px 0 25px
width 300px
height 350px
overflow auto
background-color $ui-noteList-backgroundColor
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
@@ -70,15 +69,30 @@
color $ui-text-color
.infoPanel-sub
font-size 14px
font-size 12px
font-weight 600
color $ui-inactive-text-color
padding-bottom 8px
.infoPanel-noteLink
padding-right 5px
width 200px
width 210px
height 25px
margin-bottom 6px
margin 6px 0
.infoPanel-copyButton
outline none
font-size 16px
color #A0A0A0
background-color transparent
border none
margin 0 5px
border-radius 5px
cursor pointer
&:hover
transition 0.2s
background-color alpha($ui-button--hover-backgroundColor, 30%)
color $ui-inactive-text-color
.infoPanel-trash
color #EA4447

View File

@@ -28,10 +28,6 @@ import { formatDate } from 'browser/lib/date-formatter'
import { getTodoPercentageOfCompleted } from 'browser/lib/getTodoStatus'
import striptags from 'striptags'
const electron = require('electron')
const { remote } = electron
const { dialog } = remote
class MarkdownNoteDetail extends React.Component {
constructor (props) {
super(props)
@@ -187,38 +183,37 @@ class MarkdownNoteDetail extends React.Component {
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
const { confirmDeletion } = this.props
if (isTrashed) {
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
const { note, dispatch } = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
noteKey: data.noteKey
})
}
ee.once('list:moved', dispatchHandler)
})
if (confirmDeletion(true)) {
const {note, dispatch} = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
noteKey: data.noteKey
})
}
ee.once('list:moved', dispatchHandler)
})
}
} else {
note.isTrashed = true
if (confirmDeletion()) {
note.isTrashed = true
this.setState({
note
}, () => {
this.save()
})
this.setState({
note
}, () => {
this.save()
})
ee.emit('list:next')
}
}
ee.emit('list:next')
}
handleUndoButtonClick (e) {
@@ -372,10 +367,6 @@ class MarkdownNoteDetail extends React.Component {
<TodoListPercentage percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
</div>
<div styleName='info-right'>
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<StarButton
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
@@ -389,6 +380,7 @@ class MarkdownNoteDetail extends React.Component {
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
>
<img styleName='iconInfo' src={imgSrc} />
{this.state.isLocked ? <span styleName='tooltip'>Unlock</span> : <span styleName='tooltip'>Lock</span>}
</button>
return (
@@ -400,6 +392,10 @@ class MarkdownNoteDetail extends React.Component {
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
@@ -447,7 +443,8 @@ MarkdownNoteDetail.propTypes = {
style: PropTypes.shape({
left: PropTypes.number
}),
ignorePreviewPointerEvents: PropTypes.bool
ignorePreviewPointerEvents: PropTypes.bool,
confirmDeletion: PropTypes.bool.isRequired
}
export default CSSModules(MarkdownNoteDetail, styles)

View File

@@ -12,11 +12,27 @@
padding-bottom 3px
.control-lockButton
top 150px
topBarButtonRight()
position absolute
right 225px
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 35px
right -10px
width 50px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.trashed-infopanel
top 40px
position relative
.body
@@ -25,7 +41,7 @@
right 0
top $info-height + $info-margin-under-border
bottom $statusBar-height
margin 0 45px
margin 0 30px
.body-noteEditor
absolute top bottom left right

View File

@@ -1,6 +1,6 @@
@import('DetailVars')
$info-height = 50px
$info-height = 60px
$info-margin-under-border = 30px
.info
@@ -8,11 +8,11 @@ $info-margin-under-border = 30px
left 0
right 0
height $info-height
border-bottom 1px solid #eee
background-color $ui-noteDetail-backgroundColor
width 100%
display flex
align-items center
padding 0 20px
.info-left
padding 0 10px
@@ -20,7 +20,6 @@ $info-margin-under-border = 30px
display flex
align-items center
.info-left-top-folderSelect
display flex
align-items center
@@ -45,12 +44,9 @@ $info-margin-under-border = 30px
color $ui-button--color
.info-right
position absolute
right 40px
top 60px
bottom 1px
padding-left 30px
z-index 101
display inline-flex
margin-top 3px
.undo-button
width 34px

View File

@@ -176,38 +176,37 @@ class SnippetNoteDetail extends React.Component {
handleTrashButtonClick (e) {
const { note } = this.state
const { isTrashed } = note
const { confirmDeletion } = this.props
if (isTrashed) {
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
})
if (dialogueButtonIndex === 1) return
const { note, dispatch } = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
noteKey: data.noteKey
})
}
ee.once('list:moved', dispatchHandler)
})
if (confirmDeletion(true)) {
const {note, dispatch} = this.props
dataApi
.deleteNote(note.storage, note.key)
.then((data) => {
const dispatchHandler = () => {
dispatch({
type: 'DELETE_NOTE',
storageKey: data.storageKey,
noteKey: data.noteKey
})
}
ee.once('list:moved', dispatchHandler)
})
}
} else {
note.isTrashed = true
if (confirmDeletion()) {
note.isTrashed = true
this.setState({
note
}, () => {
this.save()
})
this.setState({
note
}, () => {
this.save()
})
ee.emit('list:next')
}
}
ee.emit('list:next')
}
handleUndoButtonClick (e) {
@@ -565,6 +564,7 @@ class SnippetNoteDetail extends React.Component {
fontSize={editorFontSize}
indentType={config.editor.indentType}
indentSize={editorIndentSize}
displayLineNumbers={config.editor.displayLineNumbers}
keyMap={config.editor.keyMap}
scrollPastEnd={config.editor.scrollPastEnd}
onChange={(e) => this.handleCodeChange(index)(e)}
@@ -627,10 +627,6 @@ class SnippetNoteDetail extends React.Component {
/>
</div>
<div styleName='info-right'>
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<StarButton
onClick={(e) => this.handleStarButtonClick(e)}
isActive={note.isStarred}
@@ -638,10 +634,16 @@ class SnippetNoteDetail extends React.Component {
<button styleName='control-fullScreenButton' title='Fullscreen'
onMouseDown={(e) => this.handleFullScreenButton(e)}>
<img styleName='iconInfo' src='../resources/icon/icon-sidebar.svg' />
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
<span styleName='tooltip'>Fullscreen</span>
</button>
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
<InfoButton
onClick={(e) => this.handleInfoButtonClick(e)}
/>
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
@@ -731,7 +733,8 @@ SnippetNoteDetail.propTypes = {
style: PropTypes.shape({
left: PropTypes.number
}),
ignorePreviewPointerEvents: PropTypes.bool
ignorePreviewPointerEvents: PropTypes.bool,
confirmDeletion: PropTypes.bool.isRequired
}
export default CSSModules(SnippetNoteDetail, styles)

View File

@@ -9,8 +9,7 @@
.body
absolute left right
left $snippet-note-detail-left-margin
right $snippet-note-detail-right-margin
margin 0 30px
top $info-height + $info-margin-under-border
bottom $statusBar-height
background-color $ui-noteDetail-backgroundColor
@@ -70,6 +69,21 @@
top 80px
margin-bottom 10px
topBarButtonRight()
&:hover .tooltip
opacity 1
.tooltip
tooltip()
position absolute
pointer-events none
top 50px
right 70px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
body[data-theme="white"]
.root

View File

@@ -11,9 +11,9 @@
tooltip()
position absolute
pointer-events none
top 26px
right 0
width 100%
top 50px
right 115px
width 40px
z-index 200
padding 5px
line-height normal

View File

@@ -64,7 +64,8 @@ class TagSelect extends React.Component {
submitTag () {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
let { value } = this.props
const newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
newTag = newTag.charAt(0) === '#' ? newTag.substring(1) : newTag
if (newTag.length <= 0) {
this.setState({

View File

@@ -6,7 +6,8 @@
width 100%
overflow-x scroll
white-space nowrap
margin-right 10px
margin-top 31px
position absolute
.root::-webkit-scrollbar
display none

View File

@@ -8,10 +8,10 @@ const ToggleModeButton = ({
}) => (
<div styleName='control-toggleModeButton'>
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => onClick('SPLIT')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-split-on.svg' : '../resources/icon/icon-mode-split-on-active.svg'} />
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : ''} />
</div>
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : '../resources/icon/icon-mode-markdown-off.svg'} />
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
</div>
<span styleName='tooltip'>Toggle Mode</span>
</div>

View File

@@ -1,24 +1,28 @@
.control-toggleModeButton
border 1px solid #eee
height 34px
height 25px
border-radius 50px
background-color #F4F4F4
width 52px
display flex
align-items center
position absolute
right 165px
.active
background-color #1EC38B
width 33px
height 24px
box-shadow 2px 0px 7px #eee
z-index 1
div
width 40px
height 100%
background-color #f9f9f9
border-radius 50%
display flex
align-items center
justify-content center
cursor pointer
&:first-child
border-right 1px solid #eee
.active
background-color #fff
box-shadow 2px 0px 7px #eee
z-index 1
&:hover .tooltip
opacity 1
@@ -26,9 +30,10 @@
tooltip()
position absolute
pointer-events none
top 47px
right 11px
top 33px
left -10px
z-index 200
width 80px
padding 5px
line-height normal
border-radius 2px
@@ -40,22 +45,14 @@ body[data-theme="dark"]
topBarButtonDark()
.control-toggleModeButton
border 1px solid #444444
div
background-color $ui-dark-noteDetail-backgroundColor
&:first-child
border-right 1px solid #444444
.active
background-color #3A404C
.active
background-color #1EC38B
box-shadow 2px 0px 7px #444444
body[data-theme="solarized-dark"]
.control-toggleModeButton
border 1px solid #586E75
div
background-color $ui-solarized-dark-noteDetail-backgroundColor
&:first-child
border-right 1px solid #586E75
.active
background-color #002B36
.active
background-color #1EC38B
box-shadow 2px 0px 7px #222222

View File

@@ -8,8 +8,8 @@
tooltip()
position absolute
pointer-events none
top 26px
right 0
top 50px
right 50px
z-index 200
padding 5px
line-height normal

View File

@@ -32,6 +32,26 @@ class Detail extends React.Component {
ee.off('detail:delete', this.deleteHandler)
}
confirmDeletion (permanent) {
if (this.props.config.ui.confirmDeletion || permanent) {
const electron = require('electron')
const { remote } = electron
const { dialog } = remote
const alertConfig = {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
}
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), alertConfig)
return dialogueButtonIndex === 0
}
return true
}
render () {
const { location, data, config } = this.props
let note = null
@@ -64,6 +84,7 @@ class Detail extends React.Component {
<SnippetNoteDetail
note={note}
config={config}
confirmDeletion={(permanent) => this.confirmDeletion(permanent)}
ref='root'
{..._.pick(this.props, [
'dispatch',
@@ -80,6 +101,7 @@ class Detail extends React.Component {
<MarkdownNoteDetail
note={note}
config={config}
confirmDeletion={(permanent) => this.confirmDeletion(permanent)}
ref='root'
{..._.pick(this.props, [
'dispatch',

View File

@@ -10,10 +10,13 @@ import Detail from './Detail'
import dataApi from 'browser/main/lib/dataApi'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import modal from 'browser/main/lib/modal'
import InitModal from 'browser/main/modals/InitModal'
import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig'
import eventEmitter from 'browser/main/lib/eventEmitter'
import { hashHistory } from 'react-router'
import store from 'browser/main/store'
const path = require('path')
const electron = require('electron')
const { remote } = electron
class Main extends React.Component {
@@ -48,6 +51,91 @@ class Main extends React.Component {
}
}
init () {
dataApi
.addStorage({
name: 'My Storage',
path: path.join(remote.app.getPath('home'), 'Boostnote')
})
.then((data) => {
return data
})
.then((data) => {
if (data.storage.folders[0] != null) {
return data
} else {
return dataApi
.createFolder(data.storage.key, {
color: '#1278BD',
name: 'Default'
})
.then((_data) => {
return {
storage: _data.storage,
notes: data.notes
}
})
}
})
.then((data) => {
console.log(data)
store.dispatch({
type: 'ADD_STORAGE',
storage: data.storage,
notes: data.notes
})
const defaultSnippetNote = dataApi
.createNote(data.storage.key, {
type: 'SNIPPET_NOTE',
folder: data.storage.folders[0].key,
title: 'Snippet note example',
description: 'Snippet note example\nYou can store a series of snippets as a single note, like Gist.',
snippets: [
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Enjoy Boostnote!</h1>\n</body>\n</html>'
},
{
name: 'example.js',
mode: 'javascript',
content: 'var boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)'
}
]
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
const defaultMarkdownNote = dataApi
.createNote(data.storage.key, {
type: 'MARKDOWN_NOTE',
folder: data.storage.folders[0].key,
title: 'Welcome to Boostnote!',
content: '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)'
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
return Promise.resolve(defaultSnippetNote)
.then(defaultMarkdownNote)
.then(() => data.storage)
})
.then((storage) => {
hashHistory.push('/storages/' + storage.key)
})
.catch((err) => {
throw err
})
}
componentDidMount () {
const { dispatch, config } = this.props
@@ -71,7 +159,7 @@ class Main extends React.Component {
})
if (data.storages.length < 1) {
modal.open(InitModal)
this.init()
}
})

View File

@@ -86,7 +86,7 @@ class NewNoteButton extends React.Component {
onClick={(e) => this.handleNewNoteButtonClick(e)}>
<img styleName='iconTag' src='../resources/icon/icon-newnote.svg' />
<span styleName='control-newNoteButton-tooltip'>
Make a Note {OSX ? '⌘' : '^'} + n
Make a note {OSX ? '⌘' : 'Ctrl'} + N
</span>
</button>
</div>

View File

@@ -11,10 +11,8 @@ import NoteItem from 'browser/components/NoteItem'
import NoteItemSimple from 'browser/components/NoteItemSimple'
import searchFromNotes from 'browser/lib/search'
import fs from 'fs'
import path from 'path'
import { hashHistory } from 'react-router'
import markdown from 'browser/lib/markdownTextHelper'
import { findNoteTitle } from 'browser/lib/findNoteTitle'
import store from 'browser/main/store'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
const { remote } = require('electron')
@@ -171,9 +169,8 @@ class NoteList extends React.Component {
if (this.notes == null || this.notes.length === 0) {
return
}
let { router } = this.context
let { location } = this.props
let { selectedNoteKeys, shiftKeyDown } = this.state
let { selectedNoteKeys } = this.state
const { shiftKeyDown } = this.state
let targetIndex = this.getTargetIndex()
@@ -199,9 +196,8 @@ class NoteList extends React.Component {
if (this.notes == null || this.notes.length === 0) {
return
}
let { router } = this.context
let { location } = this.props
let { selectedNoteKeys, shiftKeyDown } = this.state
let { selectedNoteKeys } = this.state
const { shiftKeyDown } = this.state
let targetIndex = this.getTargetIndex()
const isTargetLastNote = targetIndex === this.notes.length - 1
@@ -242,7 +238,6 @@ class NoteList extends React.Component {
}
handleNoteListKeyDown (e) {
const { shiftKeyDown } = this.state
if (e.metaKey || e.ctrlKey) return true
if (e.keyCode === 65 && !e.shiftKey) {
@@ -284,7 +279,7 @@ class NoteList extends React.Component {
getNotes () {
const { data, params, location } = this.props
if (location.pathname.match(/\/home/) || location.pathname.match(/\alltags/)) {
if (location.pathname.match(/\/home/) || location.pathname.match(/alltags/)) {
const allNotes = data.noteMap.map((note) => note)
this.contextNotes = allNotes
return allNotes
@@ -355,9 +350,10 @@ class NoteList extends React.Component {
}
handleNoteClick (e, uniqueKey) {
let { router } = this.context
let { location } = this.props
let { shiftKeyDown, selectedNoteKeys } = this.state
const { router } = this.context
const { location } = this.props
let { selectedNoteKeys } = this.state
const { shiftKeyDown } = this.state
if (shiftKeyDown && selectedNoteKeys.includes(uniqueKey)) {
const newSelectedNoteKeys = selectedNoteKeys.filter((noteKey) => noteKey !== uniqueKey)
@@ -443,6 +439,7 @@ class NoteList extends React.Component {
const pinLabel = note.isPinned ? 'Remove pin' : 'Pin to Top'
const deleteLabel = 'Delete Note'
const cloneNote = 'Clone Note'
const menu = new Menu()
if (!location.pathname.match(/\/home|\/starred|\/trash/)) {
@@ -455,6 +452,10 @@ class NoteList extends React.Component {
label: deleteLabel,
click: this.deleteNote
}))
menu.append(new MenuItem({
label: cloneNote,
click: this.cloneNote.bind(this)
}))
menu.popup()
}
@@ -545,6 +546,42 @@ class NoteList extends React.Component {
this.setState({ selectedNoteKeys: [] })
}
cloneNote () {
const { selectedNoteKeys } = this.state
const { dispatch, location } = this.props
const { storage, folder } = this.resolveTargetFolder()
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
const firstNote = selectedNotes[0]
const eventName = firstNote.type === 'MARKDOWN_NOTE' ? 'ADD_MARKDOWN' : 'ADD_SNIPPET'
AwsMobileAnalyticsConfig.recordDynamicCustomEvent(eventName)
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
dataApi
.createNote(storage.key, {
type: firstNote.type,
folder: folder.key,
title: firstNote.title + ' copy',
content: firstNote.content
})
.then((note) => {
const uniqueKey = note.storage + '-' + note.key
dispatch({
type: 'UPDATE_NOTE',
note: note
})
this.setState({
selectedNoteKeys: [uniqueKey]
})
hashHistory.push({
pathname: location.pathname,
query: {key: uniqueKey}
})
})
}
importFromFile () {
const options = {
filters: [
@@ -582,7 +619,7 @@ class NoteList extends React.Component {
const newNote = {
content: content,
folder: folder.key,
title: markdown.strip(findNoteTitle(content)),
title: path.basename(filepath, path.extname(filepath)),
type: 'MARKDOWN_NOTE',
createdAt: birthtime,
updatedAt: mtime
@@ -642,9 +679,10 @@ class NoteList extends React.Component {
}
render () {
let { location, notes, config, dispatch } = this.props
let { selectedNoteKeys } = this.state
let sortFunc = config.sortBy === 'CREATED_AT'
const { location, config } = this.props
let { notes } = this.props
const { selectedNoteKeys } = this.state
const sortFunc = config.sortBy === 'CREATED_AT'
? sortByCreatedAt
: config.sortBy === 'ALPHABETICAL'
? sortByAlphabetical
@@ -689,7 +727,6 @@ class NoteList extends React.Component {
config.sortBy === 'CREATED_AT'
? note.createdAt : note.updatedAt
).fromNow('D')
const key = `${note.storage}-${note.key}`
if (isDefault) {
return (

View File

@@ -8,12 +8,10 @@ import CreateFolderModal from 'browser/main/modals/CreateFolderModal'
import RenameFolderModal from 'browser/main/modals/RenameFolderModal'
import dataApi from 'browser/main/lib/dataApi'
import StorageItemChild from 'browser/components/StorageItem'
import eventEmitter from 'browser/main/lib/eventEmitter'
import _ from 'lodash'
import * as path from 'path'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
const { Menu, dialog } = remote
class StorageItem extends React.Component {
constructor (props) {

View File

@@ -21,19 +21,20 @@
color white
.zoom
navButtonColor()
color rgba(0,0,0,.54)
height 20px
display flex
padding 0
align-items center
background-color transparent
&:hover
color $ui-active-color
&:active
color $ui-active-color
span
margin-left 5px
display none
// navButtonColor()
// color rgba(0,0,0,.54)
// height 20px
// display flex
// padding 0
// align-items center
// background-color transparent
// &:hover
// color $ui-active-color
// &:active
// color $ui-active-color
// span
// margin-left 5px
.update
navButtonColor()

View File

@@ -97,7 +97,7 @@ body[data-theme="dark"]
.CodeMirror
font-family inherit !important
line-height 1.4em
height 96%
height 100%
.CodeMirror > div > textarea
margin-bottom -1em
.CodeMirror-focused .CodeMirror-selected

View File

@@ -18,7 +18,6 @@ export const DEFAULT_CONFIG = {
listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL'
amaEnabled: true,
hotkey: {
toggleFinder: OSX ? 'Cmd + Alt + S' : 'Super + Alt + S',
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
},
ui: {
@@ -34,6 +33,7 @@ export const DEFAULT_CONFIG = {
fontFamily: win ? 'Segoe UI' : 'Monaco, Consolas',
indentType: 'space',
indentSize: '2',
displayLineNumbers: true,
switchPreview: 'BLUR', // Available value: RIGHTCLICK, BLUR
scrollPastEnd: false,
type: 'SPLIT'
@@ -46,7 +46,8 @@ export const DEFAULT_CONFIG = {
latexInlineOpen: '$',
latexInlineClose: '$',
latexBlockOpen: '$$',
latexBlockClose: '$$'
latexBlockClose: '$$',
scrollPastEnd: false
}
}

View File

@@ -46,9 +46,7 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) {
.filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE')
.forEach(snippet => {
const notePath = path.join(exportDir, `${snippet.title}.${fileType}`)
fs.writeFileSync(notePath, snippet.content, (err) => {
if (err) throw err
})
fs.writeFileSync(notePath, snippet.content)
})
return {

View File

@@ -24,20 +24,6 @@ nodeIpc.connectTo(
nodeIpc.of.node.on('disconnect', function () {
console.log('disconnected')
})
nodeIpc.of.node.on('request-data-from-finder', function () {
console.log('throttle')
var { data } = store.getState()
console.log(data.starredSet.toJS())
nodeIpc.of.node.emit('throttle-data', {
storageMap: data.storageMap.toJS(),
noteMap: data.noteMap.toJS(),
starredSet: data.starredSet.toJS(),
storageNoteMap: data.storageNoteMap.toJS(),
folderNoteMap: data.folderNoteMap.toJS(),
tagNoteMap: data.tagNoteMap.toJS()
})
})
}
)

View File

@@ -29,7 +29,7 @@
width 490px
padding 0 5px
margin 10px 0
border 1px solid #C9C9C9 // TODO: use variable.
border 1px solid $ui-input--create-folder-modal
border-radius 2px
background-color transparent
outline none
@@ -68,7 +68,7 @@ body[data-theme="dark"]
color $ui-dark-text-color
.control-folder-input
border 1px solid #C9C9C9 // TODO: use variable.
border 1px solid $ui-input--create-folder-modal
color white
.description
@@ -76,3 +76,29 @@ body[data-theme="dark"]
.control-confirmButton
colorDarkPrimaryButton()
body[data-theme="solarized-dark"]
.root
modalSolarizedDark()
width 500px
height 270px
overflow hidden
position relative
.header
background-color transparent
border-color $ui-dark-borderColor
color $ui-solarized-dark-text-color
.control-folder-label
color $ui-solarized-dark-text-color
.control-folder-input
border 1px solid $ui-input--create-folder-modal
color white
.description
color $ui-inactive-text-color
.control-confirmButton
colorSolarizedDarkPrimaryButton()

View File

@@ -1,254 +0,0 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InitModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { hashHistory } from 'react-router'
import _ from 'lodash'
const CSON = require('@rokt33r/season')
const path = require('path')
const electron = require('electron')
const { remote } = electron
function browseFolder () {
const dialog = remote.dialog
const defaultPath = remote.app.getPath('home')
return new Promise((resolve, reject) => {
dialog.showOpenDialog({
title: 'Select Directory',
defaultPath,
properties: ['openDirectory', 'createDirectory']
}, function (targetPaths) {
if (targetPaths == null) return resolve('')
resolve(targetPaths[0])
})
})
}
class InitModal extends React.Component {
constructor (props) {
super(props)
this.state = {
path: path.join(remote.app.getPath('home'), 'Boostnote'),
migrationRequested: true,
isLoading: true,
data: null,
legacyStorageExists: false,
isSending: false
}
}
handlePathChange (e) {
this.setState({
path: e.target.value
})
}
componentDidMount () {
let data = null
try {
data = CSON.readFileSync(path.join(remote.app.getPath('userData'), 'local.json'))
} catch (err) {
console.error(err)
}
const newState = {
isLoading: false
}
if (data != null) {
newState.legacyStorageExists = true
newState.data = data
}
this.setState(newState, () => {
this.refs.createButton.focus()
})
}
handlePathBrowseButtonClick (e) {
browseFolder()
.then((targetPath) => {
if (targetPath.length > 0) {
this.setState({
path: targetPath
})
}
})
.catch((err) => {
console.error('BrowseFAILED')
console.error(err)
})
}
handleSubmitButtonClick (e) {
this.setState({
isSending: true
}, () => {
dataApi
.addStorage({
name: 'My Storage',
path: this.state.path
})
.then((data) => {
if (this.state.migrationRequested && _.isObject(this.state.data) && _.isArray(this.state.data.folders) && _.isArray(this.state.data.articles)) {
return dataApi.migrateFromV5Storage(data.storage.key, this.state.data)
}
return data
})
.then((data) => {
if (data.storage.folders[0] != null) {
return data
} else {
return dataApi
.createFolder(data.storage.key, {
color: '#1278BD',
name: 'Default'
})
.then((_data) => {
return {
storage: _data.storage,
notes: data.notes
}
})
}
})
.then((data) => {
console.log(data)
store.dispatch({
type: 'ADD_STORAGE',
storage: data.storage,
notes: data.notes
})
const defaultSnippetNote = dataApi
.createNote(data.storage.key, {
type: 'SNIPPET_NOTE',
folder: data.storage.folders[0].key,
title: 'Snippet note example',
description: 'Snippet note example\nYou can store a series of snippets as a single note, like Gist.',
snippets: [
{
name: 'example.html',
mode: 'html',
content: '<html>\n<body>\n<h1 id=\'hello\'>Enjoy Boostnote!</h1>\n</body>\n</html>'
},
{
name: 'example.js',
mode: 'javascript',
content: 'var boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)'
}
]
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
const defaultMarkdownNote = dataApi
.createNote(data.storage.key, {
type: 'MARKDOWN_NOTE',
folder: data.storage.folders[0].key,
title: 'Welcome to Boostnote!',
content: '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)'
})
.then((note) => {
store.dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
return Promise.resolve(defaultSnippetNote)
.then(defaultMarkdownNote)
.then(() => data.storage)
})
.then((storage) => {
hashHistory.push('/storages/' + storage.key)
this.props.close()
})
.catch((err) => {
this.setState({
isSending: false
})
throw err
})
})
}
handleMigrationRequestedChange (e) {
this.setState({
migrationRequested: e.target.checked
})
}
handleKeyDown (e) {
if (e.keyCode === 27) {
this.props.close()
}
}
render () {
if (this.state.isLoading) {
return <div styleName='root--loading'>
<i styleName='spinner' className='fa fa-spin fa-spinner' />
<div styleName='loadingMessage'>Preparing initialization...</div>
</div>
}
return (
<div styleName='root'
tabIndex='-1'
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='body'>
<div styleName='body-welcome'>
Welcome to Boostnote!
</div>
<div styleName='body-description'>
Please select a directory for data storage.
</div>
<div styleName='body-path'>
<input styleName='body-path-input'
placeholder='Select Folder'
value={this.state.path}
onChange={(e) => this.handlePathChange(e)}
/>
<button styleName='body-path-button'
onClick={(e) => this.handlePathBrowseButtonClick(e)}
>
...
</button>
</div>
{this.state.legacyStorageExists &&
<div styleName='body-migration'>
<label><input type='checkbox' checked={this.state.migrationRequested} onChange={(e) => this.handleMigrationRequestedChange(e)} /> Migrate old data from the legacy app v0.5</label>
</div>
}
<div styleName='body-control'>
<button styleName='body-control-createButton'
ref='createButton'
onClick={(e) => this.handleSubmitButtonClick(e)}
disabled={this.state.isSending}
>
{this.state.isSending
? <span>
<i className='fa fa-spin fa-spinner' /> Loading...
</span>
: 'CREATE'
}
</button>
</div>
</div>
</div>
)
}
}
InitModal.propTypes = {
}
export default CSSModules(InitModal, styles)

View File

@@ -1,76 +0,0 @@
.root
modal()
background-color #fff
max-width 100vw
max-height 100vh
overflow hidden
margin 0
padding 150px 0
position relative
.root--loading
@extend .root
text-align center
.spinner
font-size 100px
margin 35px auto
color $ui-text-color
.loadingMessage
color $ui-text-color
margin 15px auto 35px
.body
padding 30px
.body-welcome
text-align center
margin-bottom 25px
font-size 32px
color $ui-text-color
.body-description
font-size 16px
color $ui-text-color
text-align center
margin-bottom 25px
.body-path
margin 0 auto 25px
width 330px
.body-path-input
height 40px
vertical-align middle
width 300px
font-size 14px
border-style solid
border-width 1px 0 1px 1px
border-color $border-color
border-top-left-radius 2px
border-bottom-left-radius 2px
padding 0 5px
.body-path-button
height 42px
width 30px
font-size 16px
font-weight 600
border none
border-top-right-radius 2px
border-bottom-right-radius 2px
colorPrimaryButton()
vertical-align middle
.body-migration
margin 0 auto 25px
text-align center
.body-control
text-align center
.body-control-createButton
colorPrimaryButton()
font-size 14px
font-weight 600
border none
border-radius 2px
height 40px
padding 0 25px

View File

@@ -106,7 +106,7 @@ class NewNoteModal extends React.Component {
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='header'>
<div styleName='title'>Make a Note</div>
<div styleName='title'>Make a note</div>
</div>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />
<div styleName='control'>

View File

@@ -89,9 +89,9 @@
margin-right 10px
.group-control-rightButton
position absolute
top 10px
right 20px
position fixed
top 80px
right 100px
colorPrimaryButton()
border none
border-radius 2px

View File

@@ -22,18 +22,18 @@ class Crowdfunding extends React.Component {
return (
<div styleName='root'>
<div styleName='header'>Crowdfunding</div>
<p>Dear all,</p>
<p>Dear everyone,</p>
<br />
<p>Thanks for your using!</p>
<p>Boostnote is used in about 200 countries and regions, it is a awesome developer community.</p>
<p>Thank you for using Boostnote!</p>
<p>Boostnote is used in about 200 different countries and regions by an awesome community of developers.</p>
<br />
<p>To continue supporting this growth, and to satisfy community expectations,</p>
<p>we would like to invest more time in this project.</p>
<p>we would like to invest more time and resources in this project.</p>
<br />
<p>If you like this project and see its potential, you can help!</p>
<p>If you like this project and see its potential, you can help by supporting us on OpenCollective!</p>
<br />
<p>Thanks,</p>
<p>Boostnote maintainers.</p>
<p>Boostnote maintainers</p>
<br />
<button styleName='cf-link'>
<a href='https://opencollective.com/boostnoteio' onClick={(e) => this.handleLinkClick(e)}>Support via OpenCollective</a>

View File

@@ -66,7 +66,6 @@ class HotkeyTab extends React.Component {
handleHotkeyChange (e) {
const { config } = this.state
config.hotkey = {
toggleFinder: this.refs.toggleFinder.value,
toggleMain: this.refs.toggleMain.value
}
this.setState({
@@ -103,9 +102,9 @@ class HotkeyTab extends React.Component {
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>Hotkey</div>
<div styleName='group-header'>Hotkeys</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Toggle Main</div>
<div styleName='group-section-label'>Show/Hide Boostnote</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
@@ -115,24 +114,13 @@ class HotkeyTab extends React.Component {
/>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Toggle Finder (Quick search)</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
ref='toggleFinder'
value={config.hotkey.toggleFinder}
type='text'
/>
</div>
</div>
<div styleName='group-control'>
<button styleName='group-control-leftButton'
onClick={(e) => this.handleHintToggleButtonClick(e)}
>
{this.state.isHotkeyHintOpen
? 'Hide Hint'
: 'Hint?'
? 'Hide Help'
: 'Help'
}
</button>
<button styleName='group-control-rightButton'

View File

@@ -31,7 +31,7 @@ class InfoTab extends React.Component {
}
handleSaveButtonClick (e) {
let newConfig = {
const newConfig = {
amaEnabled: this.state.config.amaEnabled
}
@@ -102,7 +102,7 @@ class InfoTab extends React.Component {
<hr />
<div styleName='header--sub'>Info</div>
<div styleName='header--sub'>About</div>
<div styleName='top'>
<div styleName='icon-space'>
@@ -137,17 +137,19 @@ class InfoTab extends React.Component {
<hr styleName='separate-line' />
<div styleName='policy'>Data collection policy</div>
<div>We collect only the number of DAU for Boostnote and **DO NOT collect** any detail information such as your note content.</div>
<div styleName='policy'>Analytics</div>
<div>Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.</div>
<div>You can see how it works on <a href='https://github.com/BoostIO/Boostnote' onClick={(e) => this.handleLinkClick(e)}>GitHub</a>.</div>
<div>This data is only used for Boostnote improvements.</div>
<br />
<div>You can choose to enable or disable this option.</div>
<input onChange={(e) => this.handleConfigChange(e)}
checked={this.state.config.amaEnabled}
ref='amaEnabled'
type='checkbox'
/>
Enable to send analytics to our servers<br />
Enable analytics to help improve Boostnote<br />
<button styleName='policy-submit' onClick={(e) => this.handleSaveButtonClick(e)}>Save</button>
<br />
{this.infoMessage()}
</div>
)

View File

@@ -84,3 +84,17 @@ body[data-theme="dark"]
top 25px
z-index 10
white-space nowrap
body[data-theme="solarized-dark"]
.header
border-color $ui-solarized-dark-button-backgroundColor
.header-label-path
color $ui-solarized-dark-text-color
.header-label-editButton
color $ui-solarized-dark-text-color
.header-control-button
border-color $ui-solarized-dark-button-backgroundColor
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color

View File

@@ -78,7 +78,7 @@ class StoragesTab extends React.Component {
<button styleName='list-control-addStorageButton'
onClick={(e) => this.handleAddStorageButton(e)}
>
<i className='fa fa-plus' /> Add Storage
<i className='fa fa-plus' /> Add Storage Location
</button>
</div>
</div>
@@ -167,7 +167,7 @@ class StoragesTab extends React.Component {
<option value='FILESYSTEM'>File System</option>
</select>
<div styleName='addStorage-body-section-type-description'>
3rd party cloud integration:
Setting up 3rd-party cloud storage integration:{' '}
<a href='https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup'
onClick={(e) => this.handleLinkClick(e)}
>Cloud-Syncing-and-Backup</a>
@@ -196,7 +196,7 @@ class StoragesTab extends React.Component {
<div styleName='addStorage-body-control'>
<button styleName='addStorage-body-control-createButton'
onClick={(e) => this.handleAddStorageCreateButton(e)}
>Create</button>
>Add</button>
<button styleName='addStorage-body-control-cancelButton'
onClick={(e) => this.handleAddStorageCancelButton(e)}
>Cancel</button>

View File

@@ -62,6 +62,7 @@ class UiTab extends React.Component {
ui: {
theme: this.refs.uiTheme.value,
showCopyNotification: this.refs.showCopyNotification.checked,
confirmDeletion: this.refs.confirmDeletion.checked,
disableDirectWrite: this.refs.uiD2w != null
? this.refs.uiD2w.checked
: false
@@ -72,6 +73,7 @@ class UiTab extends React.Component {
fontFamily: this.refs.editorFontFamily.value,
indentType: this.refs.editorIndentType.value,
indentSize: this.refs.editorIndentSize.value,
displayLineNumbers: this.refs.editorDisplayLineNumbers.checked,
switchPreview: this.refs.editorSwitchPreview.value,
keyMap: this.refs.editorKeyMap.value,
scrollPastEnd: this.refs.scrollPastEnd.checked
@@ -84,7 +86,8 @@ class UiTab extends React.Component {
latexInlineOpen: this.refs.previewLatexInlineOpen.value,
latexInlineClose: this.refs.previewLatexInlineClose.value,
latexBlockOpen: this.refs.previewLatexBlockOpen.value,
latexBlockClose: this.refs.previewLatexBlockClose.value
latexBlockClose: this.refs.previewLatexBlockClose.value,
scrollPastEnd: this.refs.previewScrollPastEnd.checked
}
}
@@ -147,10 +150,10 @@ class UiTab extends React.Component {
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>UI</div>
<div styleName='group-header'>Interface</div>
<div styleName='group-section'>
Color Theme
Interface Theme
<div styleName='group-section-control'>
<select value={config.ui.theme}
onChange={(e) => this.handleUIChange(e)}
@@ -173,6 +176,16 @@ class UiTab extends React.Component {
Show &quot;Saved to Clipboard&quot; notification when copying
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.confirmDeletion}
ref='confirmDeletion'
type='checkbox'
/>&nbsp;
Show a confirmation dialog when deleting notes
</label>
</div>
{
global.process.platform === 'win32'
? <div styleName='group-checkBoxSection'>
@@ -182,7 +195,7 @@ class UiTab extends React.Component {
refs='uiD2w'
disabled={OSX}
type='checkbox'
/>
/>&nbsp;
Disable Direct Write(It will be applied after restarting)
</label>
</div>
@@ -292,6 +305,17 @@ class UiTab extends React.Component {
</div>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.editor.displayLineNumbers}
ref='editorDisplayLineNumbers'
type='checkbox'
/>&nbsp;
Show line numbers in the editor
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
@@ -345,6 +369,16 @@ class UiTab extends React.Component {
</select>
</div>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.preview.scrollPastEnd}
ref='previewScrollPastEnd'
type='checkbox'
/>&nbsp;
Allow preview to scroll past the last line
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}

View File

@@ -1,6 +1,5 @@
import PropTypes from 'prop-types'
import React from 'react'
import ReactDOM from 'react-dom'
import { connect } from 'react-redux'
import HotkeyTab from './HotkeyTab'
import UiTab from './UiTab'
@@ -11,6 +10,7 @@ import ModalEscButton from 'browser/components/ModalEscButton'
import CSSModules from 'browser/lib/CSSModules'
import styles from './PreferencesModal.styl'
import RealtimeNotification from 'browser/components/RealtimeNotification'
import _ from 'lodash'
class Preferences extends React.Component {
constructor (props) {
@@ -94,8 +94,7 @@ class Preferences extends React.Component {
}
getContentBoundingBox () {
const node = ReactDOM.findDOMNode(this.refs.content)
return node.getBoundingClientRect()
return this.refs.content.getBoundingClientRect()
}
haveToSaveNotif (type, message) {
@@ -108,10 +107,10 @@ class Preferences extends React.Component {
const content = this.renderContent()
const tabs = [
{target: 'STORAGES', label: 'Storages'},
{target: 'HOTKEY', label: 'Hotkey', Hotkey: this.state.HotkeyAlert},
{target: 'UI', label: 'UI', UI: this.state.UIAlert},
{target: 'INFO', label: 'Community / Info'},
{target: 'STORAGES', label: 'Storage'},
{target: 'HOTKEY', label: 'Hotkeys', Hotkey: this.state.HotkeyAlert},
{target: 'UI', label: 'Interface', UI: this.state.UIAlert},
{target: 'INFO', label: 'About'},
{target: 'CROWDFUNDING', label: 'Crowdfunding'}
]

View File

@@ -355,11 +355,9 @@ function data (state = defaultDataMap(), action) {
state.storageMap.set(action.storage.key, action.storage)
return state
case 'EXPORT_FOLDER':
{
state = Object.assign({}, state)
state.storageMap = new Map(state.storageMap)
state.storageMap.set(action.storage.key, action.storage)
}
state = Object.assign({}, state)
state.storageMap = new Map(state.storageMap)
state.storageMap.set(action.storage.key, action.storage)
return state
case 'DELETE_FOLDER':
{

View File

@@ -1,129 +0,0 @@
@import '../../../node_modules/nib/lib/nib'
@import '../vars'
@import '../mixins/*'
global-reset()
@import '../shared/*'
@import '../theme/*'
iptBgColor = #E6E6E6
iptFocusBorderColor = #369DCD
DEFAULT_FONTS = 'Lato', 'MS Gothic', 'Malgun Gothic', 'Sans-serif'
body
font-family DEFAULT_FONTS
color textColor
font-size fontSize
width 100%
height 100%
overflow hidden
button, input
font-family DEFAULT_FONTS
.Finder
absolute top bottom left right
.FinderInput
padding 11px
margin 0 auto
height 55px
box-sizing border-box
border-bottom solid 1px borderColor
background-color iptBgColor
z-index 200
input
display block
width 100%
border solid 1px borderColor
padding 0 10px
font-size 1em
height 33px
border-radius 5px
box-sizing border-box
border-radius 5px
&:focus, &.focus
border-color iptFocusBorderColor
outline none
.FinderList
absolute left bottom
top 55px
border-right solid 1px borderColor
box-sizing border-box
width 250px
overflow-y auto
z-index 0
user-select none
&>ul>li
.articleItem
padding 10px
border solid 2px transparent
box-sizing border-box
cursor pointer
white-space nowrap
overflow-x hidden
text-overflow ellipsis
.divider
box-sizing border-box
border-bottom solid 1px borderColor
&.active
.articleItem
border-color brandColor
.FinderDetail
absolute right bottom
top 55px
left 250px
box-shadow 0px 0px 10px 0 #CCC
z-index 100
.header
absolute top left right
height 55px
box-sizing border-box
padding 0 10px
border-bottom solid 1px borderColor
line-height 55px
font-size 18px
white-space nowrap
text-overflow ellipsis
overflow-x hidden
clearfix()
.left
float left
.right
float right
button
border-radius 16.5px
cursor pointer
height 33px
width 33px
border none
margin-right 5px
font-size 18px
color inactiveTextColor
background-color transparent
padding 0
.tooltip
tooltip()
&.clipboardBtn .tooltip
margin-left -160px
margin-top 25px
&:hover
color textColor
.tooltip
opacity 1
.content
position absolute
top 55px
padding 10px
bottom 0
left 0
right 0
box-sizing border-box
overflow-y auto
.MarkdownPreview
marked()
&.empty
color lighten(inactiveTextColor, 10%)
user-select none
font-size 14px
.CodeEditor
absolute top bottom left right

View File

@@ -46,6 +46,7 @@ tooltip()
// UI Input
$ui-input--focus-borderColor = #369DCD
$ui-input--disabled-backgroundColor = #DDD
$ui-input--create-folder-modal = #C9C9C9
// Parts
$ui-favorite-star-button-color = #FFC216
@@ -187,7 +188,6 @@ modal()
border-radius $modal-border-radius
topBarButtonRight()
position absolute
width 34px
height 34px
border-radius 17px
@@ -339,4 +339,12 @@ $ui-solarized-dark-button-backgroundColor = #002b36
$ui-solarized-dark-button--active-color = #93a1a1
$ui-solarized-dark-button--active-backgroundColor = #073642
$ui-solarized-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%)
$ui-solarized-dark-button--focus-borderColor = lighten(#369DCD, 25%)
$ui-solarized-dark-button--focus-borderColor = lighten(#369DCD, 25%)
modalSolarizedDark()
position relative
z-index $modal-z-index
width 100%
background-color $ui-solarized-dark-backgroundColor
overflow hidden
border-radius $modal-border-radius

View File

@@ -378,52 +378,10 @@ body[data-theme="dark"]
&:hover
color themeDarkFocusButton
.Finder
.FinderInput
color themeDarkText
border-color themeDarkBorder
background-color themeDarkBackground
input
color themeDarkText
border-color lighten(themeDarkBackground, 10%)
background-color lighten(themeDarkBackground, 10%)
&:focus
border-color themeDarkTopicColor
.FinderList
color themeDarkText
border-color themeDarkBorder
background-color themeDarkList
.divider
border-color themeDarkBorder
.FinderDetail
color themeDarkText
border-color themeDarkBorder
background-color themeDarkPreview
box-shadow 0px 0px 10px 0 darken(themeDarkBorder, 20%);
.header
border-color themeDarkBorder
.right
.clipboardBtn
transition 0.1s
&:hover
color themeDarkFocusButton
.tooltip
background-color themeDarkTooltip
.ArticleDetail-panel
border-radius 0
// Markdown Preview
.Finder .FinderDetail .content,
.ArticleDetail .ArticleDetail-panel .ArticleEditor
.MarkdownPreview
color themeDarkText

View File

@@ -4,11 +4,6 @@ const path = require('path')
var error = null
function isFinderCalled () {
var argv = process.argv.slice(1)
return argv.some((arg) => arg.match(/--finder/))
}
function execMainApp () {
const appRootPath = path.join(process.execPath, '../..')
const updateDotExePath = path.join(appRootPath, 'Update.exe')
@@ -78,8 +73,4 @@ function execMainApp () {
require('./lib/main-app')
}
if (isFinderCalled()) {
require('./lib/finder-app')
} else {
execMainApp()
}
execMainApp()

View File

@@ -1,14 +0,0 @@
const electron = require('electron')
const app = electron.app
app.on('ready', function () {
if (process.platform === 'darwin') {
app.dock.hide()
}
/* eslint-disable */
finderWindow = require('./finder-window')
/* eslint-enable */
})
module.exports = app

View File

@@ -1,101 +0,0 @@
const electron = require('electron')
const { app } = electron
const BrowserWindow = electron.BrowserWindow
const Menu = electron.Menu
const MenuItem = electron.MenuItem
const Tray = electron.Tray
const path = require('path')
var config = {
width: 840,
height: 540,
show: false,
frame: false,
resizable: false,
zoomFactor: 1.0,
webPreferences: {
blinkFeatures: 'OverlayScrollbars'
},
skipTaskbar: true,
standardWindow: false
}
if (process.platform === 'darwin') {
config['always-on-top'] = true
}
var finderWindow = new BrowserWindow(config)
var url = path.resolve(__dirname, './finder.html')
finderWindow.loadURL('file://' + url)
finderWindow.setSkipTaskbar(true)
if (process.platform === 'darwin') {
finderWindow.setVisibleOnAllWorkspaces(true)
}
finderWindow.on('blur', function () {
hideFinder()
})
finderWindow.on('close', function (e) {
e.preventDefault()
finderWindow.hide()
})
var trayIcon = process.platform === 'darwin' || process.platform === 'win32'
? path.join(__dirname, '../resources/tray-icon-default.png')
: path.join(__dirname, '../resources/tray-icon.png')
var appIcon = new Tray(trayIcon)
appIcon.setToolTip('Boostnote')
if (process.platform === 'darwin') {
appIcon.setPressedImage(path.join(__dirname, '../resources/tray-icon-dark.png'))
}
var trayMenu = new Menu()
trayMenu.append(new MenuItem({
label: 'Open Main window',
click: function () {
finderWindow.webContents.send('open-main-from-tray')
}
}))
if (process.env.platform !== 'linux' || process.env.DESKTOP_SESSION === 'cinnamon') {
trayMenu.append(new MenuItem({
label: 'Open Finder window',
click: function () {
finderWindow.webContents.send('open-finder-from-tray')
}
}))
}
trayMenu.append(new MenuItem({
label: 'Quit',
click: function () {
finderWindow.webContents.send('quit-from-tray')
}
}))
appIcon.setContextMenu(trayMenu)
appIcon.on('click', function (e) {
e.preventDefault()
appIcon.popUpContextMenu(trayMenu)
})
function hideFinder () {
if (process.platform === 'win32') {
finderWindow.minimize()
return
}
if (process.platform === 'darwin') {
Menu.sendActionToFirstResponder('hide:')
}
finderWindow.hide()
}
app.on('before-quit', function (e) {
finderWindow.removeAllListeners()
})
module.exports = finderWindow

View File

@@ -1,64 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Boostnote Finder</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
<link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css" media="screen" charset="utf-8">
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
<style>
@font-face {
font-family: 'Lato';
src: url('../resources/fonts/Lato-Regular.woff2') format('woff2'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.woff') format('woff'), /* Modern Browsers */
url('../resources/fonts/Lato-Regular.ttf') format('truetype');
font-style: normal;
font-weight: normal;
text-rendering: optimizeLegibility;
}
</style>
</head>
<body>
<div id="content"></div>
<script src="../node_modules/codemirror/lib/codemirror.js"></script>
<script src="../node_modules/codemirror/mode/meta.js"></script>
<script src="../node_modules/codemirror-mode-elixir/dist/elixir.js"></script>
<script src="../node_modules/codemirror/addon/mode/overlay.js"></script>
<script src="../node_modules/codemirror/addon/mode/loadmode.js"></script>
<script src="../node_modules/codemirror/keymap/sublime.js"></script>
<script src="../node_modules/codemirror/keymap/vim.js"></script>
<script src="../node_modules/codemirror/keymap/emacs.js"></script>
<script src="../node_modules/codemirror/addon/runmode/runmode.js"></script>
<script src="../node_modules/raphael/raphael.min.js"></script>
<script src="../node_modules/flowchart.js/release/flowchart.min.js"></script>
<script src="../node_modules/katex/dist/katex.min.js"></script>
<script src="../node_modules/react/dist/react.min.js"></script>
<script src="../node_modules/react-dom/dist/react-dom.min.js"></script>
<script src="../node_modules/redux/dist/redux.min.js"></script>
<script src="../node_modules/react-redux/dist/react-redux.min.js"></script>
<script>
window._ = require('lodash');
</script>
<script src="../node_modules/js-sequence-diagrams/fucknpm/sequence-diagram-min.js"></script>
<script>
const electron = require('electron')
electron.webFrame.setZoomLevelLimits(1, 1)
const _ = require('lodash')
var scriptUrl = _.find(electron.remote.process.argv, (a) => a === '--hot')
? 'http://localhost:8080/assets/finder.js'
: '../compiled/finder.js'
var scriptEl = document.createElement('script')
scriptEl.setAttribute('type', 'text/javascript')
scriptEl.setAttribute('src', scriptUrl)
document.body.appendChild(scriptEl)
</script>
</body>
</html>

View File

@@ -26,10 +26,6 @@ function toggleMainWindow () {
}
}
function toggleFinder () {
nodeIpc.server.broadcast('open-finder')
}
ipcMain.on('config-renew', (e, payload) => {
nodeIpc.server.broadcast('config-renew', payload)
@@ -37,11 +33,6 @@ ipcMain.on('config-renew', (e, payload) => {
var { config } = payload
var errors = []
try {
globalShortcut.register(config.hotkey.toggleFinder, toggleFinder)
} catch (err) {
errors.push('toggleFinder')
}
try {
globalShortcut.register(config.hotkey.toggleMain, toggleMainWindow)
} catch (err) {
@@ -61,12 +52,6 @@ ipcMain.on('config-renew', (e, payload) => {
nodeIpc.serve(
path.join(app.getPath('userData'), 'boostnote.service'),
function () {
nodeIpc.server.on('open-main-from-finder', toggleMainWindow)
nodeIpc.server.on('quit-from-finder', function () {
app.quit()
})
nodeIpc.server.on('connect', function (socket) {
nodeIpc.log('ipc server >> socket joinned'.rainbow)
socket.on('close', function () {
@@ -76,14 +61,6 @@ nodeIpc.serve(
nodeIpc.server.on('error', function (err) {
nodeIpc.log('Node IPC error'.rainbow, err)
})
// Todo: Direct connection between Main window renderer & Finder window renderer
nodeIpc.server.on('request-data-from-finder', function () {
nodeIpc.server.broadcast('request-data-from-finder')
})
nodeIpc.server.on('throttle-data', function (payload) {
nodeIpc.server.broadcast('throttle-data', payload)
})
}
)

View File

@@ -2,9 +2,6 @@ const electron = require('electron')
const app = electron.app
const Menu = electron.Menu
const ipc = electron.ipcMain
const path = require('path')
const ChildProcess = require('child_process')
const _ = require('lodash')
const GhReleases = require('electron-gh-releases')
// electron.crashReporter.start()
var ipcServer = null
@@ -65,14 +62,6 @@ updater.autoUpdater.on('error', (err) => {
console.log(err)
})
ipc.on('update-check', function (event, msg) {
if (isUpdateReady) {
mainWindow.webContents.send('update-ready', 'Update available!')
} else {
checkUpdate()
}
})
ipc.on('update-app-confirm', function (event, msg) {
if (isUpdateReady) {
mainWindow.removeAllListeners()
@@ -80,17 +69,6 @@ ipc.on('update-app-confirm', function (event, msg) {
}
})
function spawnFinder () {
var finderArgv = [path.join(__dirname, 'finder-app.js'), '--finder']
if (_.find(process.argv, a => a === '--hot')) finderArgv.push('--hot')
var finderProcess = ChildProcess
.execFile(process.execPath, finderArgv)
app.on('before-quit', function () {
finderProcess.kill()
})
}
app.on('ready', function () {
mainWindow = require('./main-window')
@@ -98,15 +76,12 @@ app.on('ready', function () {
var menu = Menu.buildFromTemplate(template)
switch (process.platform) {
case 'darwin':
spawnFinder()
Menu.setApplicationMenu(menu)
break
case 'win32':
require('./finder-window')
mainWindow.setMenu(menu)
break
case 'linux':
require('./finder-window')
Menu.setApplicationMenu(menu)
mainWindow.setMenu(menu)
}
@@ -114,12 +89,22 @@ app.on('ready', function () {
// Check update every hour
setInterval(function () {
checkUpdate()
}, 1000 * 60 * 60)
}, 1000 * 60 * 60 * 24)
checkUpdate()
// Check update after 10 secs to prevent file locking of Windows
setTimeout(() => {
checkUpdate()
ipc.on('update-check', function (event, msg) {
if (isUpdateReady) {
mainWindow.webContents.send('update-ready', 'Update available!')
} else {
checkUpdate()
}
})
}, 10000)
ipcServer = require('./ipcServer')
ipcServer.server.start()
})
module.exports = app

View File

@@ -4,6 +4,7 @@ const BrowserWindow = electron.BrowserWindow
const path = require('path')
const Config = require('electron-config')
const config = new Config()
const _ = require('lodash')
var showMenu = process.platform !== 'win32'
const windowSize = config.get('windowsize') || { width: 1080, height: 720 }
@@ -39,41 +40,28 @@ mainWindow.webContents.sendInputEvent({
keyCode: '\u0008'
})
if (process.platform !== 'linux' || process.env.DESKTOP_SESSION === 'cinnamon') {
if (process.platform === 'darwin' || process.env.DESKTOP_SESSION === 'cinnamon') {
mainWindow.on('close', function (e) {
e.preventDefault()
if (process.platform === 'win32') {
quitApp()
} else {
if (mainWindow.isFullScreen()) {
mainWindow.once('leave-full-screen', function () {
mainWindow.hide()
})
mainWindow.setFullScreen(false)
} else {
if (mainWindow.isFullScreen()) {
mainWindow.once('leave-full-screen', function () {
mainWindow.hide()
}
})
mainWindow.setFullScreen(false)
} else {
mainWindow.hide()
}
})
app.on('before-quit', function (e) {
storeWindowSize()
mainWindow.removeAllListeners()
})
} else {
mainWindow.on('close', function () {
storeWindowSize()
})
app.on('window-all-closed', function () {
app.quit()
})
}
function quitApp () {
storeWindowSize()
app.quit()
}
mainWindow.on('resize', _.throttle(storeWindowSize, 500))
function storeWindowSize () {
try {

View File

@@ -80,6 +80,8 @@
<script src="../node_modules/codemirror/addon/edit/continuelist.js"></script>
<script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
<script src="../node_modules/codemirror/addon/search/search.js"></script>
<script src="../node_modules/codemirror/addon/search/searchcursor.js"></script>
<script src="../node_modules/codemirror/addon/scroll/annotatescrollbar.js"></script>

View File

@@ -1,7 +1,7 @@
{
"name": "boost",
"productName": "Boostnote",
"version": "0.8.19",
"version": "0.9.0",
"main": "index.js",
"description": "Boostnote",
"license": "GPL-3.0",
@@ -10,13 +10,13 @@
"hot": "electron ./index.js --hot",
"webpack": "webpack-dev-server --hot --inline --config webpack.config.js",
"compile": "grunt compile",
"test": "PWD=$(pwd) NODE_ENV=test ava",
"test": "PWD=$(pwd) NODE_ENV=test ava --serial",
"fix": "npm run lint --fix",
"lint": "eslint .",
"dev-start": "concurrently --kill-others \"npm run webpack\" \"npm run hot\""
},
"config": {
"electron-version": "1.6.15"
"electron-version": "1.7.11"
},
"repository": {
"type": "git",
@@ -60,7 +60,7 @@
"font-awesome": "^4.3.0",
"immutable": "^3.8.1",
"js-sequence-diagrams": "^1000000.0.6",
"katex": "^0.7.1",
"katex": "^0.8.3",
"lodash": "^4.11.1",
"lodash-move": "^1.1.1",
"markdown-it": "^6.0.1",
@@ -103,7 +103,7 @@
"css-loader": "^0.19.0",
"devtron": "^1.1.0",
"dom-storage": "^2.0.2",
"electron": "^1.6.15",
"electron": "1.7.11",
"electron-packager": "^6.0.0",
"eslint": "^3.13.1",
"eslint-config-standard": "^6.2.1",
@@ -112,7 +112,7 @@
"eslint-plugin-standard": "^3.0.1",
"faker": "^3.1.0",
"grunt": "^0.4.5",
"grunt-electron-installer": "^1.2.0",
"grunt-electron-installer": "2.1.0",
"history": "^1.17.0",
"jsdom": "^9.4.2",
"json-loader": "^0.5.4",

View File

@@ -10,7 +10,6 @@
## Authors & Maintainers
- [Rokt33r](https://github.com/rokt33r)
- [Kohei TAKATA](https://github.com/kohei-takata)
- [Sosuke](https://github.com/sosukesuzuki)
- [Kazz](https://github.com/kazup01)
@@ -26,16 +25,17 @@ Boostnote is an open source project. It's an independent project with its ongoin
## Community
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
- [Twitter](https://twitter.com/boostnoteapp)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMjkxMzMwODYxMDI1LTgwZmRiODg0NzA5MWRmOTJjNzBjZjAwMmMyZGQ4Y2RkOGE0MDg0YjcyMjA5OGUzMmZhNmFiNTMzOTlkYWNlMTM)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzAzMjI1MTIyNTQ3LTc2MjNiYWU3NTc1YjZlMTk3NzFmOWE1ZWU1MGRhMzBkMGIwMWFjOWMxMDRiM2I2NzkzYzc4OGZhNmVhZjYzZTM)
- [Blog](https://medium.com/boostnote)
- [Reddit](https://www.reddit.com/r/Boostnote/)
#### More Information
* [Website](https://boostnote.io)
* [Subscribe to the Newsletter](https://boostnote.io/#community): Get updates on Boostnote progress. No spam, ever :)
* Website: https://boostnote.io
* Newsletters: https://boostnote.io/#subscribe
* [Development](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md): Development configurations for Boostnote.
* Copyright (C) 2017 Maisin&Co.
* Copyright (C) 2016 - 2018 BoostIO, Inc.
#### License

View File

@@ -1,5 +1,5 @@
<?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">
<svg width="13px" height="13px" 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 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>icon-full</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 954 B

After

Width:  |  Height:  |  Size: 954 B

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="15px" height="15px" viewBox="0 0 18 18" 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>icon-info</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="15px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>icon-mode-markdown-off</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="13px" height="13px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>icon-mode-split-on</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="14px" height="14px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>icon-previewoff-off</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="14px" height="14px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>icon-previewoff-on</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="17px" viewBox="0 0 18 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="14px" height="13px" viewBox="0 0 18 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.1 (47250) - http://www.bohemiancoding.com/sketch -->
<title>icon-star</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="19px" viewBox="0 0 20 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="15px" height="16px" viewBox="0 0 20 19" 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>icon-starred</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="18px" viewBox="0 0 16 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="13px" height="15px" viewBox="0 0 16 18" 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>icon-trash</title>
<desc>Created with Sketch.</desc>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -10,7 +10,6 @@
"theme": "monokai"
},
"hotkey": {
"toggleFinder": "Cmd + Alt + S",
"toggleMain": "Cmd + Alt + L"
},
"isSideNavFolded": false,

View File

@@ -5,7 +5,7 @@ const { parse } = require('browser/lib/RcParser')
// Unit test
test('RcParser should return a json object', t => {
const validJson = { 'editor': { 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Control + L' }, 'listWidth': 135, 'navWidth': 135 }
const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleFinder': 'Cmd + Alt + S', 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': 'UPDATED_AT', 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 }
const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': 'UPDATED_AT', 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 }
// [input, expected]
const validTestCases = [

View File

@@ -38,11 +38,6 @@ var config = Object.assign({}, skeleton, {
'NODE_ENV': JSON.stringify('production'),
'BABEL_ENV': JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
warnings: false
}
})
]
})

View File

@@ -4,8 +4,7 @@ const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin')
var config = {
entry: {
main: './browser/main/index.js',
finder: './browser/finder/index.js'
main: './browser/main/index.js'
},
resolve: {
extensions: ['', '.js', '.jsx', '.styl'],

View File

@@ -245,18 +245,6 @@ asar@^0.9.0:
mkdirp "^0.5.0"
mksnapshot "0.1.0"
asar@~0.8.0:
version "0.8.3"
resolved "https://registry.yarnpkg.com/asar/-/asar-0.8.3.tgz#c2e03f9054516dbbf56759e854e9ce8d1a9d39d3"
dependencies:
chromium-pickle-js "0.1.0"
commander "2.3.0"
cuint "0.1.5"
glob "^5.0.5"
minimatch "2.0.4"
mkdirp "^0.5.0"
mksnapshot "0.1.0"
asn1@0.1.11:
version "0.1.11"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.1.11.tgz#559be18376d08a4ec4dbe80877d27818639b2df7"
@@ -1183,6 +1171,10 @@ bluebird@^3.0.0, bluebird@^3.1.1, bluebird@^3.4.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
bluebird@^3.3.4:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
boom@2.x.x:
version "2.10.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
@@ -1503,10 +1495,20 @@ code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
codemirror-mode-elixir@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/codemirror-mode-elixir/-/codemirror-mode-elixir-1.1.1.tgz#cc5b79bf5f93b6da426e32364a673a681391416c"
dependencies:
codemirror "^5.20.2"
codemirror@^5.18.2, codemirror@^5.19.0:
version "5.26.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.26.0.tgz#bcbee86816ed123870c260461c2b5c40b68746e5"
codemirror@^5.20.2:
version "5.33.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.33.0.tgz#462ad9a6fe8d38b541a9536a3997e1ef93b40c6a"
coffee-script@^1.10.0:
version "1.12.6"
resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.6.tgz#285a3f7115689065064d6bf9ef4572db66695cbf"
@@ -2199,9 +2201,20 @@ electron-to-chromium@^1.2.7:
version "1.3.11"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.11.tgz#744761df1d67b492b322ce9aa0aba5393260eb61"
electron@^1.6.15:
version "1.7.9"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.9.tgz#add54e9f8f83ed02f6519ec10135f698b19336cf"
electron-winstaller@^2.2.0:
version "2.6.3"
resolved "https://registry.yarnpkg.com/electron-winstaller/-/electron-winstaller-2.6.3.tgz#d54f77c0cececc4fc55eeb5968d345cf69645ea4"
dependencies:
asar "^0.11.0"
bluebird "^3.3.4"
debug "^2.2.0"
fs-extra "^0.26.7"
lodash.template "^4.2.2"
temp "^0.8.3"
electron@1.7.11:
version "1.7.11"
resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.11.tgz#993b6aa79e0e79a7cfcc369f4c813fbd9a0b08d9"
dependencies:
"@types/node" "^7.0.18"
electron-download "^3.0.1"
@@ -2885,7 +2898,7 @@ fs-extra@0.18.2:
jsonfile "^2.0.0"
rimraf "^2.2.8"
fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.5:
fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.5, fs-extra@^0.26.7:
version "0.26.7"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9"
dependencies:
@@ -3156,13 +3169,11 @@ grunt-electron-installer-redhat@^0.3.1:
dependencies:
electron-installer-redhat "^0.3.0"
grunt-electron-installer@^1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/grunt-electron-installer/-/grunt-electron-installer-1.2.3.tgz#50652ec4d0248233da76b4ac2ca69f3894c7240e"
grunt-electron-installer@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/grunt-electron-installer/-/grunt-electron-installer-2.1.0.tgz#b39e7eb1abb4488a1d8b7587fd4e72d68a741030"
dependencies:
asar "~0.8.0"
temp "^0.8.1"
underscore "^1.7.0"
electron-winstaller "^2.2.0"
grunt-legacy-log-utils@~0.1.1:
version "0.1.1"
@@ -3895,9 +3906,9 @@ jsx-ast-utils@^2.0.0:
dependencies:
array-includes "^3.0.3"
katex@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.7.1.tgz#06bb5298efad05e1e7228035ba8e1591f3061b8f"
katex@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.8.3.tgz#909d99864baf964c3ccae39c4a99a8e0fb9a1bd0"
dependencies:
match-at "^0.1.0"
@@ -4016,7 +4027,7 @@ lodash.some@^4.5.1:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
lodash.template@^4.3.0:
lodash.template@^4.2.2, lodash.template@^4.3.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0"
dependencies:
@@ -6336,7 +6347,7 @@ tar@^2.2.1:
fstream "^1.0.2"
inherits "2"
temp@^0.8.1, temp@^0.8.3:
temp@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59"
dependencies:
@@ -6540,7 +6551,7 @@ underscore.string@~2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b"
underscore@^1.7.0, underscore@^1.8.2:
underscore@^1.8.2:
version "1.8.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"