1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-14 02:06:29 +00:00

Merge branch 'master' into improve-french-translations

This commit is contained in:
jacob
2018-11-07 07:54:44 -06:00
committed by GitHub
106 changed files with 2212 additions and 316 deletions

View File

@@ -1,15 +1,25 @@
# Current behavior # Current behavior
<!-- <!--
Please paste some **screenshots** with the **developer tool** open (console tab) when you report a bug. Let us know what is currently happening.
If your issue is regarding boostnote mobile, move to https://github.com/BoostIO/boostnote-mobile. Please include some **screenshots** with the **developer tools** open (console tab) when you report a bug.
If your issue is regarding Boostnote mobile, please open an issue in the Boostnote Mobile repo 👉 https://github.com/BoostIO/boostnote-mobile.
--> -->
# Expected behavior # Expected behavior
<!--
Let us know what you think should happen!
-->
# Steps to reproduce # Steps to reproduce
<!--
Please be thorough, issues we can reproduce are easier to fix!
-->
1. 1.
2. 2.
3. 3.

View File

@@ -14,6 +14,8 @@ import consts from 'browser/lib/consts'
import fs from 'fs' import fs from 'fs'
const { ipcRenderer } = require('electron') const { ipcRenderer } = require('electron')
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
import TurndownService from 'turndown'
import { gfm } from 'turndown-plugin-gfm'
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -57,6 +59,7 @@ export default class CodeEditor extends React.Component {
} }
this.searchHandler = (e, msg) => this.handleSearch(msg) this.searchHandler = (e, msg) => this.handleSearch(msg)
this.searchState = null this.searchState = null
this.scrollToLineHandeler = this.scrollToLine.bind(this)
this.formatTable = () => this.handleFormatTable() this.formatTable = () => this.handleFormatTable()
this.editorActivityHandler = () => this.handleEditorActivity() this.editorActivityHandler = () => this.handleEditorActivity()
@@ -125,6 +128,7 @@ export default class CodeEditor extends React.Component {
componentDidMount () { componentDidMount () {
const { rulers, enableRulers } = this.props const { rulers, enableRulers } = this.props
const expandSnippet = this.expandSnippet.bind(this) const expandSnippet = this.expandSnippet.bind(this)
eventEmitter.on('line:jump', this.scrollToLineHandeler)
const defaultSnippet = [ const defaultSnippet = [
{ {
@@ -475,7 +479,13 @@ export default class CodeEditor extends React.Component {
moveCursorTo (row, col) {} moveCursorTo (row, col) {}
scrollToLine (num) {} scrollToLine (event, num) {
const cursor = {
line: num,
ch: 1
}
this.editor.setCursor(cursor)
}
focus () { focus () {
this.editor.focus() this.editor.focus()
@@ -538,7 +548,11 @@ export default class CodeEditor extends React.Component {
) )
return prevChar === '](' && nextChar === ')' return prevChar === '](' && nextChar === ')'
} }
if (dataTransferItem.type.match('image')) {
const pastedHtml = clipboardData.getData('text/html')
if (pastedHtml !== '') {
this.handlePasteHtml(e, editor, pastedHtml)
} else if (dataTransferItem.type.match('image')) {
attachmentManagement.handlePastImageEvent( attachmentManagement.handlePastImageEvent(
this, this,
storageKey, storageKey,
@@ -608,6 +622,12 @@ export default class CodeEditor extends React.Component {
}) })
} }
handlePasteHtml (e, editor, pastedHtml) {
e.preventDefault()
const markdown = this.turndownService.turndown(pastedHtml)
editor.replaceSelection(markdown)
}
mapNormalResponse (response, pastedTxt) { mapNormalResponse (response, pastedTxt) {
return this.decodeResponse(response).then(body => { return this.decodeResponse(response).then(body => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@@ -6,6 +6,7 @@ import CodeEditor from 'browser/components/CodeEditor'
import MarkdownPreview from 'browser/components/MarkdownPreview' import MarkdownPreview from 'browser/components/MarkdownPreview'
import eventEmitter from 'browser/main/lib/eventEmitter' import eventEmitter from 'browser/main/lib/eventEmitter'
import { findStorage } from 'browser/lib/findStorage' import { findStorage } from 'browser/lib/findStorage'
import ConfigManager from 'browser/main/lib/ConfigManager'
class MarkdownEditor extends React.Component { class MarkdownEditor extends React.Component {
constructor (props) { constructor (props) {
@@ -18,7 +19,7 @@ class MarkdownEditor extends React.Component {
this.supportMdSelectionBold = [16, 17, 186] this.supportMdSelectionBold = [16, 17, 186]
this.state = { this.state = {
status: 'PREVIEW', status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'PREVIEW',
renderValue: props.value, renderValue: props.value,
keyPressed: new Set(), keyPressed: new Set(),
isLocked: false isLocked: false
@@ -64,6 +65,10 @@ class MarkdownEditor extends React.Component {
}) })
} }
setValue (value) {
this.refs.code.setValue(value)
}
handleChange (e) { handleChange (e) {
this.value = this.refs.code.value this.value = this.refs.code.value
this.props.onChange(e) this.props.onChange(e)
@@ -72,9 +77,7 @@ class MarkdownEditor extends React.Component {
handleContextMenu (e) { handleContextMenu (e) {
const { config } = this.props const { config } = this.props
if (config.editor.switchPreview === 'RIGHTCLICK') { if (config.editor.switchPreview === 'RIGHTCLICK') {
const newStatus = this.state.status === 'PREVIEW' const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW'
? 'CODE'
: 'PREVIEW'
this.setState({ this.setState({
status: newStatus status: newStatus
}, () => { }, () => {
@@ -84,6 +87,10 @@ class MarkdownEditor extends React.Component {
this.refs.preview.focus() this.refs.preview.focus()
} }
eventEmitter.emit('topbar:togglelockbutton', this.state.status) eventEmitter.emit('topbar:togglelockbutton', this.state.status)
const newConfig = Object.assign({}, config)
newConfig.editor.delfaultStatus = newStatus
ConfigManager.set(newConfig)
}) })
} }
} }
@@ -250,7 +257,7 @@ class MarkdownEditor extends React.Component {
: 'codeEditor--hide' : 'codeEditor--hide'
} }
ref='code' ref='code'
mode='GitHub Flavored Markdown' mode='Boost Flavored Markdown'
value={value} value={value}
theme={config.editor.theme} theme={config.editor.theme}
keyMap={config.editor.keyMap} keyMap={config.editor.keyMap}
@@ -300,6 +307,7 @@ class MarkdownEditor extends React.Component {
noteKey={noteKey} noteKey={noteKey}
customCSS={config.preview.customCSS} customCSS={config.preview.customCSS}
allowCustomCSS={config.preview.allowCustomCSS} allowCustomCSS={config.preview.allowCustomCSS}
lineThroughCheckbox={config.preview.lineThroughCheckbox}
/> />
</div> </div>
) )

View File

@@ -16,7 +16,6 @@
.preview .preview
display block display block
absolute top bottom left right absolute top bottom left right
z-index 100
background-color white background-color white
height 100% height 100%
width 100% width 100%

View File

@@ -16,7 +16,6 @@ import convertModeName from 'browser/lib/convertModeName'
import copy from 'copy-to-clipboard' import copy from 'copy-to-clipboard'
import mdurl from 'mdurl' import mdurl from 'mdurl'
import exportNote from 'browser/main/lib/dataApi/exportNote' import exportNote from 'browser/main/lib/dataApi/exportNote'
import { escapeHtmlCharacters } from 'browser/lib/utils'
import context from 'browser/lib/context' import context from 'browser/lib/context'
import i18n from 'browser/lib/i18n' import i18n from 'browser/lib/i18n'
import fs from 'fs' import fs from 'fs'
@@ -80,7 +79,6 @@ function buildStyle (
url('${appPath}/resources/fonts/MaterialIcons-Regular.woff') format('woff'), url('${appPath}/resources/fonts/MaterialIcons-Regular.woff') format('woff'),
url('${appPath}/resources/fonts/MaterialIcons-Regular.ttf') format('truetype'); url('${appPath}/resources/fonts/MaterialIcons-Regular.ttf') format('truetype');
} }
${allowCustomCSS ? customCSS : ''}
${markdownStyle} ${markdownStyle}
body { body {
@@ -88,6 +86,11 @@ body {
font-size: ${fontSize}px; font-size: ${fontSize}px;
${scrollPastEnd && 'padding-bottom: 90vh;'} ${scrollPastEnd && 'padding-bottom: 90vh;'}
} }
@media print {
body {
padding-bottom: initial;
}
}
code { code {
font-family: '${codeBlockFontFamily.join("','")}'; font-family: '${codeBlockFontFamily.join("','")}';
background-color: rgba(0,0,0,0.04); background-color: rgba(0,0,0,0.04);
@@ -144,6 +147,8 @@ body p {
display: none display: none
} }
} }
${allowCustomCSS ? customCSS : ''}
` `
} }
@@ -325,9 +330,7 @@ export default class MarkdownPreview extends React.Component {
allowCustomCSS, allowCustomCSS,
customCSS customCSS
) )
let body = this.markdown.render( let body = this.markdown.render(noteContent)
escapeHtmlCharacters(noteContent, { detectCodeBlock: true })
)
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
noteContent, noteContent,
@@ -425,6 +428,7 @@ export default class MarkdownPreview extends React.Component {
case 'dark': case 'dark':
case 'solarized-dark': case 'solarized-dark':
case 'monokai': case 'monokai':
case 'dracula':
return scrollBarDarkStyle return scrollBarDarkStyle
default: default:
return scrollBarStyle return scrollBarStyle
@@ -526,7 +530,8 @@ export default class MarkdownPreview extends React.Component {
prevProps.smartQuotes !== this.props.smartQuotes || prevProps.smartQuotes !== this.props.smartQuotes ||
prevProps.sanitize !== this.props.sanitize || prevProps.sanitize !== this.props.sanitize ||
prevProps.smartArrows !== this.props.smartArrows || prevProps.smartArrows !== this.props.smartArrows ||
prevProps.breaks !== this.props.breaks prevProps.breaks !== this.props.breaks ||
prevProps.lineThroughCheckbox !== this.props.lineThroughCheckbox
) { ) {
this.initMarkdown() this.initMarkdown()
this.rewriteIframe() this.rewriteIframe()
@@ -732,7 +737,6 @@ export default class MarkdownPreview extends React.Component {
el.addEventListener('click', this.linkClickHandler) el.addEventListener('click', this.linkClickHandler)
}) })
} catch (e) { } catch (e) {
console.error(e)
el.className = 'flowchart-error' el.className = 'flowchart-error'
el.innerHTML = 'Flowchart parse error: ' + e.message el.innerHTML = 'Flowchart parse error: ' + e.message
} }
@@ -753,7 +757,6 @@ export default class MarkdownPreview extends React.Component {
el.addEventListener('click', this.linkClickHandler) el.addEventListener('click', this.linkClickHandler)
}) })
} catch (e) { } catch (e) {
console.error(e)
el.className = 'sequence-error' el.className = 'sequence-error'
el.innerHTML = 'Sequence diagram parse error: ' + e.message el.innerHTML = 'Sequence diagram parse error: ' + e.message
} }
@@ -766,12 +769,18 @@ export default class MarkdownPreview extends React.Component {
try { try {
const chartConfig = JSON.parse(el.innerHTML) const chartConfig = JSON.parse(el.innerHTML)
el.innerHTML = '' el.innerHTML = ''
var canvas = document.createElement('canvas')
const canvas = document.createElement('canvas')
el.appendChild(canvas) el.appendChild(canvas)
/* eslint-disable no-new */
new Chart(canvas, chartConfig) const height = el.attributes.getNamedItem('data-height')
if (height && height.value !== 'undefined') {
el.style.height = height.value + 'vh'
canvas.height = height.value + 'vh'
}
const chart = new Chart(canvas, chartConfig)
} catch (e) { } catch (e) {
console.error(e)
el.className = 'chart-error' el.className = 'chart-error'
el.innerHTML = 'chartjs diagram parse error: ' + e.message el.innerHTML = 'chartjs diagram parse error: ' + e.message
} }
@@ -855,6 +864,15 @@ export default class MarkdownPreview extends React.Component {
return return
} }
const regexIsLine = /^:line:[0-9]/
if (regexIsLine.test(linkHash)) {
const numberPattern = /\d+/g
const lineNumber = parseInt(linkHash.match(numberPattern)[0])
eventEmitter.emit('line:jump', lineNumber)
return
}
// this will match the old link format storage.key-note.key // this will match the old link format storage.key-note.key
// e.g. // e.g.
// 877f99c3268608328037-1c211eb7dcb463de6490 // 877f99c3268608328037-1c211eb7dcb463de6490

View File

@@ -20,12 +20,18 @@ class MarkdownSplitEditor extends React.Component {
} }
} }
setValue (value) {
this.refs.code.setValue(value)
}
handleOnChange () { handleOnChange () {
this.value = this.refs.code.value this.value = this.refs.code.value
this.props.onChange() this.props.onChange()
} }
handleScroll (e) { handleScroll (e) {
if (!this.props.config.preview.scrollSync) return
const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document') const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document')
const codeDoc = _.get(this, 'refs.code.editor.doc') const codeDoc = _.get(this, 'refs.code.editor.doc')
let srcTop, srcHeight, targetTop, targetHeight let srcTop, srcHeight, targetTop, targetHeight
@@ -145,7 +151,7 @@ class MarkdownSplitEditor extends React.Component {
styleName='codeEditor' styleName='codeEditor'
ref='code' ref='code'
width={this.state.codeEditorWidthInPercent + '%'} width={this.state.codeEditorWidthInPercent + '%'}
mode='GitHub Flavored Markdown' mode='Boost Flavored Markdown'
value={value} value={value}
theme={config.editor.theme} theme={config.editor.theme}
keyMap={config.editor.keyMap} keyMap={config.editor.keyMap}
@@ -192,6 +198,7 @@ class MarkdownSplitEditor extends React.Component {
noteKey={noteKey} noteKey={noteKey}
customCSS={config.preview.customCSS} customCSS={config.preview.customCSS}
allowCustomCSS={config.preview.allowCustomCSS} allowCustomCSS={config.preview.allowCustomCSS}
lineThroughCheckbox={config.preview.lineThroughCheckbox}
/> />
</div> </div>
) )

View File

@@ -24,16 +24,19 @@ const TagElement = ({ tagName }) => (
/** /**
* @description Tag element list component. * @description Tag element list component.
* @param {Array|null} tags * @param {Array|null} tags
* @param {boolean} showTagsAlphabetically
* @return {React.Component} * @return {React.Component}
*/ */
const TagElementList = tags => { const TagElementList = (tags, showTagsAlphabetically) => {
if (!isArray(tags)) { if (!isArray(tags)) {
return [] return []
} }
const tagElements = tags.map(tag => TagElement({ tagName: tag })) if (showTagsAlphabetically) {
return _.sortBy(tags).map(tag => TagElement({ tagName: tag }))
return tagElements } else {
return tags.map(tag => TagElement({ tagName: tag }))
}
} }
/** /**
@@ -55,7 +58,8 @@ const NoteItem = ({
pathname, pathname,
storageName, storageName,
folderName, folderName,
viewType viewType,
showTagsAlphabetically
}) => ( }) => (
<div <div
styleName={isActive ? 'item--active' : 'item'} styleName={isActive ? 'item--active' : 'item'}
@@ -93,7 +97,7 @@ const NoteItem = ({
<div styleName='item-bottom'> <div styleName='item-bottom'>
<div styleName='item-bottom-tagList'> <div styleName='item-bottom-tagList'>
{note.tags.length > 0 {note.tags.length > 0
? TagElementList(note.tags) ? TagElementList(note.tags, showTagsAlphabetically)
: <span : <span
style={{ fontStyle: 'italic', opacity: 0.5 }} style={{ fontStyle: 'italic', opacity: 0.5 }}
styleName='item-bottom-tagList-empty' styleName='item-bottom-tagList-empty'

View File

@@ -394,3 +394,76 @@ body[data-theme="monokai"]
.item-bottom-tagList-empty .item-bottom-tagList-empty
color $ui-inactive-text-color color $ui-inactive-text-color
vertical-align middle vertical-align middle
body[data-theme="dracula"]
.root
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
.item
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
&:hover
transition 0.15s
// background-color alpha($ui-dracula-noteList-backgroundColor, 20%)
color $ui-dracula-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dracula-noteList-backgroundColor, 20%)
color $ui-dracula-text-color
&:active
transition 0.15s
background-color $ui-dracula-noteList-backgroundColor
color $ui-dracula-text-color
.item-title
.item-title-icon
.item-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-bottom-tagList-item
transition 0.15s
background-color alpha($ui-dracula-noteList-backgroundColor, 10%)
color $ui-dracula-text-color
.item-wrapper
border-color alpha($ui-dracula-button-backgroundColor, 60%)
.item--active
border-color $ui-dracula-borderColor
background-color $ui-dracula-button-backgroundColor
.item-wrapper
border-color transparent
.item-title
.item-title-icon
.item-bottom-time
color $ui-dracula-active-color
.item-bottom-tagList-item
background-color alpha(#f8f8f2, 10%)
color $ui-dracula-text-color
&:hover
// background-color alpha($ui-dracula-button--active-backgroundColor, 60%)
color #ff79c6
.item-bottom-tagList-item
background-color alpha(#f8f8f2, 20%)
.item-title
color $ui-inactive-text-color
.item-title-icon
color $ui-inactive-text-color
.item-title-empty
color $ui-inactive-text-color
.item-bottom-tagList-item
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
color $ui-inactive-text-color
.item-bottom-tagList-empty
color $ui-inactive-text-color
vertical-align middle

View File

@@ -286,3 +286,67 @@ body[data-theme="monokai"]
.item-simple-right-storageName .item-simple-right-storageName
padding-left 4px padding-left 4px
opacity 0.4 opacity 0.4
body[data-theme="dracula"]
.root
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
.item-simple
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
&:hover
transition 0.15s
background-color alpha($ui-dracula-button-backgroundColor, 60%)
color $ui-dracula-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#f8f8f2, 20%)
color $ui-dracula-text-color
&:active
transition 0.15s
background-color $ui-dracula-button--active-backgroundColor
color $ui-dracula-text-color
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
transition 0.15s
color $ui-dracula-text-color
.item-simple-bottom-tagList-item
transition 0.15s
background-color alpha(#f8f8f2, 10%)
color $ui-dracula-text-color
.item-simple--active
border-color $ui-dracula-borderColor
background-color $ui-dracula-button--active-backgroundColor
.item-simple-wrapper
border-color transparent
.item-simple-title
.item-simple-title-empty
.item-simple-title-icon
.item-simple-bottom-time
color $ui-dracula-text-color
.item-simple-bottom-tagList-item
background-color alpha(#f8f8f2, 10%)
color $ui-dracula-text-color
&:hover
// background-color alpha($ui-dark-button--active-backgroundColor, 60%)
color #c0392b
.item-simple-bottom-tagList-item
background-color alpha(#f8f8f2, 20%)
.item-simple-title
color $ui-dark-text-color
border-bottom $ui-dark-borderColor
.item-simple-right
float right
.item-simple-right-storageName
padding-left 4px
opacity 0.4

View File

@@ -52,3 +52,14 @@ body[data-theme="monokai"]
background-color $ui-monokai-button-backgroundColor background-color $ui-monokai-button-backgroundColor
&:hover &:hover
color #5CB85C color #5CB85C
body[data-theme="dracula"]
.notification-area
background-color none
.notification-link
color $ui-dracula-text-color
border none
background-color $ui-dracula-button-backgroundColor
&:hover
color #ff79c6

View File

@@ -264,3 +264,45 @@ body[data-theme="monokai"]
color $ui-monokai-text-color color $ui-monokai-text-color
.menu-button-label .menu-button-label
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.menu-button
&:active
background-color $ui-dracula-noteList-backgroundColor
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.menu-button-label
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button-label
color $ui-dracula-text-color
.menu-button-star--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.menu-button-label
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button-label
color $ui-dracula-text-color
.menu-button-trash--active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.menu-button-label
color $ui-dracula-text-color
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.menu-button-label
color $ui-dracula-text-color

View File

@@ -181,3 +181,47 @@ body[data-theme="monokai"]
.deleteButton .deleteButton
color alpha($ui-monokai-text-color, 30%) color alpha($ui-monokai-text-color, 30%)
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
border-color $ui-dark-borderColor
&:hover
background-color $ui-dracula-noteDetail-backgroundColor
.deleteButton
color $ui-dracula-text-color
&:hover
background-color darken($ui-dracula-noteDetail-backgroundColor, 15%)
&:active
color $ui-dracula-text-color
background-color $ui-dark-button--active-backgroundColor
.root--active
color $ui-dracula-text-color
border-color $ui-dracula-borderColor
&:hover
background-color $ui-dracula-noteDetail-backgroundColor
.deleteButton
color $ui-dracula-text-color
&:hover
background-color darken($ui-dracula-noteDetail-backgroundColor, 15%)
&:active
color $ui-dracula-text-color
background-color $ui-dark-button--active-backgroundColor
.button
border none
color $ui-dracula-text-color
background-color transparent
transition color background-color 0.15s
border-left 4px solid transparent
&:hover
color $ui-dracula-text-color
background-color $ui-dracula-noteDetail-backgroundColor
.input
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color
.deleteButton
color alpha($ui-dracula-text-color, 30%)

View File

@@ -157,3 +157,22 @@ body[data-theme="monokai"]
&:hover &:hover
color $ui-monokai-text-color color $ui-monokai-text-color
background-color $ui-monokai-button-backgroundColor background-color $ui-monokai-button-backgroundColor
body[data-theme="dracula"]
.folderList-item
&:hover
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
&:active
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
.folderList-item--active
@extend .folderList-item
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
&:active
background-color $ui-dracula-button-backgroundColor
&:hover
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor

View File

@@ -7,18 +7,18 @@ import styles from './StorageList.styl'
import CSSModules from 'browser/lib/CSSModules' import CSSModules from 'browser/lib/CSSModules'
/** /**
* @param {Array} storgaeList * @param {Array} storageList
*/ */
const StorageList = ({storageList, isFolded}) => ( const StorageList = ({storageList, isFolded}) => (
<div styleName={isFolded ? 'storageList-folded' : 'storageList'}> <div styleName={isFolded ? 'storageList-folded' : 'storageList'}>
{storageList.length > 0 ? storageList : ( {storageList.length > 0 ? storageList : (
<div styleName='storgaeList-empty'>No storage mount.</div> <div styleName='storageList-empty'>No storage mount.</div>
)} )}
</div> </div>
) )
StorageList.propTypes = { StorageList.propTypes = {
storgaeList: PropTypes.arrayOf(PropTypes.element).isRequired storageList: PropTypes.arrayOf(PropTypes.element).isRequired
} }
export default CSSModules(StorageList, styles) export default CSSModules(StorageList, styles)

View File

@@ -14,8 +14,8 @@ import CSSModules from 'browser/lib/CSSModules'
* @param {bool} isRelated * @param {bool} isRelated
*/ */
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isActive, isRelated, count}) => ( const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count}) => (
<div styleName='tagList-itemContainer'> <div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
{isRelated {isRelated
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}> ? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} /> <i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
@@ -25,7 +25,7 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isAc
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}> <button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-name'> <span styleName='tagList-item-name'>
{`# ${name}`} {`# ${name}`}
<span styleName='tagList-item-count'>{count}</span> <span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
</span> </span>
</button> </button>
</div> </div>

View File

@@ -12,7 +12,7 @@ import styles from './TodoListPercentage.styl'
*/ */
const TodoListPercentage = ({ const TodoListPercentage = ({
percentageOfTodo percentageOfTodo, onClearCheckboxClick
}) => ( }) => (
<div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}> <div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}>
<div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}> <div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}>
@@ -20,11 +20,15 @@ const TodoListPercentage = ({
<p styleName='percentageText'>{percentageOfTodo}%</p> <p styleName='percentageText'>{percentageOfTodo}%</p>
</div> </div>
</div> </div>
<div styleName='todoClear'>
<p styleName='todoClearText' onClick={(e) => onClearCheckboxClick(e)}>clear</p>
</div>
</div> </div>
) )
TodoListPercentage.propTypes = { TodoListPercentage.propTypes = {
percentageOfTodo: PropTypes.number.isRequired percentageOfTodo: PropTypes.number.isRequired,
onClearCheckboxClick: PropTypes.func.isRequired
} }
export default CSSModules(TodoListPercentage, styles) export default CSSModules(TodoListPercentage, styles)

View File

@@ -1,4 +1,5 @@
.percentageBar .percentageBar
display: flex
position absolute position absolute
top 72px top 72px
right 0px right 0px
@@ -30,6 +31,20 @@
color #f4f4f4 color #f4f4f4
font-weight 600 font-weight 600
.todoClear
display flex
justify-content: flex-end
position absolute
z-index 120
width 100%
height 100%
padding 2px 10px
.todoClearText
color #f4f4f4
cursor pointer
font-weight 500
body[data-theme="dark"] body[data-theme="dark"]
.percentageBar .percentageBar
background-color #444444 background-color #444444
@@ -40,6 +55,9 @@ body[data-theme="dark"]
.percentageText .percentageText
color $ui-dark-text-color color $ui-dark-text-color
.todoClearText
color $ui-dark-text-color
body[data-theme="solarized-dark"] body[data-theme="solarized-dark"]
.percentageBar .percentageBar
background-color #002b36 background-color #002b36
@@ -50,6 +68,9 @@ body[data-theme="solarized-dark"]
.percentageText .percentageText
color #fdf6e3 color #fdf6e3
.todoClearText
color #fdf6e3
body[data-theme="monokai"] body[data-theme="monokai"]
.percentageBar .percentageBar
background-color: $ui-monokai-borderColor background-color: $ui-monokai-borderColor
@@ -59,3 +80,16 @@ body[data-theme="monokai"]
.percentageText .percentageText
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.percentageBar
background-color $ui-dracula-borderColor
.progressBar
background-color: $ui-dracula-active-color
.percentageText
color $ui-dracula-text-color
.percentageText
color $ui-dracula-text-color

View File

@@ -80,6 +80,9 @@ li
&.checked &.checked
text-decoration line-through text-decoration line-through
opacity 0.5 opacity 0.5
&.taskListItem.checked
text-decoration line-through
opacity 0.5
div.math-rendered div.math-rendered
text-align center text-align center
.math-failed .math-failed
@@ -206,41 +209,39 @@ code
text-decoration none text-decoration none
margin-right 2px margin-right 2px
pre pre
padding 0.5em !important padding 0.5rem !important
border solid 1px #D1D1D1 border solid 1px #D1D1D1
border-radius 5px border-radius 5px
overflow-x auto overflow-x auto
margin 0 0 1em margin 0 0 1rem
display flex display flex
line-height 1.4em line-height 1.4em
&.flowchart, &.sequence, &.chart
display flex
justify-content center
background-color white
&.CodeMirror
height initial
flex-wrap wrap
&>code
flex 1
overflow-x auto
code code
background-color inherit background-color inherit
margin 0 margin 0
padding 0 padding 0
border none border none
border-radius 0 border-radius 0
&.CodeMirror
height initial
flex-wrap wrap
&>code
flex 1
overflow-x auto
&.mermaid svg
max-width 100% !important
&>span.filename &>span.filename
width 100% margin -0.5rem 100% 0.5rem -0.5rem
border-radius: 5px 0px 0px 0px padding 0.125rem 0.375rem
margin -8px 100% 8px -8px
padding 0px 6px
background-color #777; background-color #777;
color white color white
&:empty
display none
&>span.lineNumber &>span.lineNumber
display none display none
font-size 1em font-size 1em
padding 0.5em 0 padding 0.5rem 0
margin -0.5em 0.5em -0.5em -0.5em margin -0.5rem 0.5rem -0.5rem -0.5rem
border-right 1px solid border-right 1px solid
text-align right text-align right
border-top-left-radius 4px border-top-left-radius 4px
@@ -372,6 +373,19 @@ for name, val in admonition_types
color: val[color] color: val[color]
content: val[icon] content: val[icon]
pre.fence
flex-wrap wrap
.chart, .flowchart, .mermaid, .sequence
display flex
justify-content center
background-color white
max-width 100%
flex-grow 1
canvas, svg
max-width 100% !important
themeDarkBackground = darken(#21252B, 10%) themeDarkBackground = darken(#21252B, 10%)
themeDarkText = #f9f9f9 themeDarkText = #f9f9f9
themeDarkBorder = lighten(themeDarkBackground, 20%) themeDarkBorder = lighten(themeDarkBackground, 20%)
@@ -478,3 +492,32 @@ body[data-theme="monokai"]
border-right solid 1px themeMonokaiTableBorder border-right solid 1px themeMonokaiTableBorder
kbd kbd
background-color themeDarkBackground background-color themeDarkBackground
themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor
themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%)
themeDraculaTableHead = themeDraculaTableEven
themeDraculaTableBorder = themeDarkBorder
body[data-theme="dracula"]
color $ui-dracula-text-color
border-color themeDarkBorder
background-color $ui-dracula-noteDetail-backgroundColor
table
thead
tr
background-color themeDraculaTableHead
th
border-color themeDraculaTableBorder
&:last-child
border-right solid 1px themeDraculaTableBorder
tbody
tr:nth-child(2n + 1)
background-color themeDraculaTableOdd
tr:nth-child(2n)
background-color themeDraculaTableEven
td
border-color themeDraculaTableBorder
&:last-child
border-right solid 1px themeDraculaTableBorder
kbd
background-color themeDarkBackground

View File

@@ -11,9 +11,9 @@ function getRandomInt (min, max) {
} }
function getId () { function getId () {
var pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' const pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
var id = 'm-' let id = 'm-'
for (var i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
id += pool[getRandomInt(0, 16)] id += pool[getRandomInt(0, 16)]
} }
return id return id
@@ -21,16 +21,20 @@ function getId () {
function render (element, content, theme) { function render (element, content, theme) {
try { try {
let isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' const height = element.attributes.getNamedItem('data-height')
if (height && height.value !== 'undefined') {
element.style.height = height.value + 'vh'
}
let isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula'
mermaidAPI.initialize({ mermaidAPI.initialize({
theme: isDarkTheme ? 'dark' : 'default', theme: isDarkTheme ? 'dark' : 'default',
themeCSS: isDarkTheme ? darkThemeStyling : '' themeCSS: isDarkTheme ? darkThemeStyling : '',
useMaxWidth: false
}) })
mermaidAPI.render(getId(), content, (svgGraph) => { mermaidAPI.render(getId(), content, (svgGraph) => {
element.innerHTML = svgGraph element.innerHTML = svgGraph
}) })
} catch (e) { } catch (e) {
console.error(e)
element.className = 'mermaid-error' element.className = 'mermaid-error'
element.innerHTML = 'mermaid diagram parse error: ' + e.message element.innerHTML = 'mermaid diagram parse error: ' + e.message
} }

View File

@@ -49,7 +49,7 @@ const languages = [
}, },
{ {
name: 'Portuguese', name: 'Portuguese',
locale: 'pt' locale: 'pt-BR'
}, },
{ {
name: 'Russian', name: 'Russian',
@@ -61,6 +61,9 @@ const languages = [
}, { }, {
name: 'Turkish', name: 'Turkish',
locale: 'tr' locale: 'tr'
}, {
name: 'Thai',
locale: 'th'
} }
] ]

View File

@@ -1,4 +1,4 @@
export function findNoteTitle (value) { export function findNoteTitle (value, enableFrontMatterTitle, frontMatterTitleField = 'title') {
const splitted = value.split('\n') const splitted = value.split('\n')
let title = null let title = null
let isInsideCodeBlock = false let isInsideCodeBlock = false
@@ -6,6 +6,11 @@ export function findNoteTitle (value) {
if (splitted[0] === '---') { if (splitted[0] === '---') {
let line = 0 let line = 0
while (++line < splitted.length) { while (++line < splitted.length) {
if (enableFrontMatterTitle && splitted[line].startsWith(frontMatterTitleField + ':')) {
title = splitted[line].substring(frontMatterTitleField.length + 1).trim()
break
}
if (splitted[line] === '---') { if (splitted[line] === '---') {
splitted.splice(0, line + 1) splitted.splice(0, line + 1)
@@ -14,6 +19,7 @@ export function findNoteTitle (value) {
} }
} }
if (title === null) {
splitted.some((line, index) => { splitted.some((line, index) => {
const trimmedLine = line.trim() const trimmedLine = line.trim()
const trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim() const trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
@@ -25,6 +31,7 @@ export function findNoteTitle (value) {
return true return true
} }
}) })
}
if (title === null) { if (title === null) {
title = '' title = ''

View File

@@ -0,0 +1,130 @@
'use strict'
module.exports = function (md, renderers, defaultRenderer) {
function fence (state, startLine, endLine) {
let pos = state.bMarks[startLine] + state.tShift[startLine]
let max = state.eMarks[startLine]
if (state.sCount[startLine] - state.blkIndent >= 4 || pos + 3 > max) {
return false
}
const marker = state.src.charCodeAt(pos)
if (!(marker === 96 || marker === 126)) {
return false
}
let mem = pos
pos = state.skipChars(pos, marker)
let len = pos - mem
if (len < 3) {
return false
}
const markup = state.src.slice(mem, pos)
const params = state.src.slice(pos, max)
let nextLine = startLine
let haveEndMarker = false
while (true) {
nextLine++
if (nextLine >= endLine) {
break
}
pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
max = state.eMarks[nextLine]
if (pos < max && state.sCount[nextLine] < state.blkIndent) {
break
}
if (state.src.charCodeAt(pos) !== marker || state.sCount[nextLine] - state.blkIndent >= 4) {
continue
}
pos = state.skipChars(pos, marker)
if (pos - mem < len) {
continue
}
pos = state.skipSpaces(pos)
if (pos >= max) {
haveEndMarker = true
break
}
}
len = state.sCount[startLine]
state.line = nextLine + (haveEndMarker ? 1 : 0)
const parameters = {}
let langType = ''
let fileName = ''
let firstLineNumber = 1
let match = /^(\w[-\w]*)?(?:\(((?:\s*\w[-\w]*(?:=(?:'(?:.*?[^\\])?'|"(?:.*?[^\\])?"|(?:[^'"][^\s]*)))?)*)\))?(?::([^:]*)(?::(\d+))?)?\s*$/.exec(params)
if (match) {
if (match[1]) {
langType = match[1]
}
if (match[3]) {
fileName = match[3]
}
if (match[4]) {
firstLineNumber = parseInt(match[4], 10)
}
if (match[2]) {
const params = match[2]
const regex = /(\w[-\w]*)(?:=(?:'(.*?[^\\])?'|"(.*?[^\\])?"|([^'"][^\s]*)))?/g
let name, value
while ((match = regex.exec(params))) {
name = match[1]
value = match[2] || match[3] || match[4] || null
const height = /^(\d+)h$/.exec(name)
if (height && !value) {
parameters.height = height[1]
} else {
parameters[name] = value
}
}
}
}
let token
if (renderers[langType]) {
token = state.push(`${langType}_fence`, 'div', 0)
} else {
token = state.push('_fence', 'code', 0)
}
token.langType = langType
token.fileName = fileName
token.firstLineNumber = firstLineNumber
token.parameters = parameters
token.content = state.getLines(startLine + 1, nextLine, len, true)
token.markup = markup
token.map = [startLine, state.line]
return true
}
md.block.ruler.before('fence', '_fence', fence, {
alt: ['paragraph', 'reference', 'blockquote', 'list']
})
for (const name in renderers) {
md.renderer.rules[`${name}_fence`] = (tokens, index) => renderers[name](tokens[index])
}
if (defaultRenderer) {
md.renderer.rules['_fence'] = (tokens, index) => defaultRenderer(tokens[index])
}
}

View File

@@ -2,6 +2,7 @@
import sanitizeHtml from 'sanitize-html' import sanitizeHtml from 'sanitize-html'
import { escapeHtmlCharacters } from './utils' import { escapeHtmlCharacters } from './utils'
import url from 'url'
module.exports = function sanitizePlugin (md, options) { module.exports = function sanitizePlugin (md, options) {
options = options || {} options = options || {}
@@ -14,7 +15,7 @@ module.exports = function sanitizePlugin (md, options) {
options options
) )
} }
if (state.tokens[tokenIdx].type === 'fence') { if (state.tokens[tokenIdx].type === '_fence') {
// escapeHtmlCharacters has better performance // escapeHtmlCharacters has better performance
state.tokens[tokenIdx].content = escapeHtmlCharacters( state.tokens[tokenIdx].content = escapeHtmlCharacters(
state.tokens[tokenIdx].content, state.tokens[tokenIdx].content,
@@ -25,7 +26,7 @@ module.exports = function sanitizePlugin (md, options) {
const inlineTokens = state.tokens[tokenIdx].children const inlineTokens = state.tokens[tokenIdx].children
for (let childIdx = 0; childIdx < inlineTokens.length; childIdx++) { for (let childIdx = 0; childIdx < inlineTokens.length; childIdx++) {
if (inlineTokens[childIdx].type === 'html_inline') { if (inlineTokens[childIdx].type === 'html_inline') {
inlineTokens[childIdx].content = sanitizeHtml( inlineTokens[childIdx].content = sanitizeInline(
inlineTokens[childIdx].content, inlineTokens[childIdx].content,
options options
) )
@@ -35,3 +36,89 @@ module.exports = function sanitizePlugin (md, options) {
} }
}) })
} }
const tagRegex = /<([A-Z][A-Z0-9]*)\s*((?:\s*[A-Z][A-Z0-9]*(?:=("|')(?:[^\3]+?)\3)?)*)\s*\/?>|<\/([A-Z][A-Z0-9]*)\s*>/i
const attributesRegex = /([A-Z][A-Z0-9]*)(?:=("|')([^\2]+?)\2)?/ig
function sanitizeInline (html, options) {
let match = tagRegex.exec(html)
if (!match) {
return ''
}
const { allowedTags, allowedAttributes, selfClosing, allowedSchemesAppliedToAttributes } = options
if (match[1] !== undefined) {
// opening tag
const tag = match[1].toLowerCase()
if (allowedTags.indexOf(tag) === -1) {
return ''
}
const attributes = match[2]
let attrs = ''
let name
let value
while ((match = attributesRegex.exec(attributes))) {
name = match[1].toLowerCase()
value = match[3]
if (allowedAttributes['*'].indexOf(name) !== -1 || (allowedAttributes[tag] && allowedAttributes[tag].indexOf(name) !== -1)) {
if (allowedSchemesAppliedToAttributes.indexOf(name) !== -1) {
if (naughtyHRef(value, options) || (tag === 'iframe' && name === 'src' && naughtyIFrame(value, options))) {
continue
}
}
attrs += ` ${name}`
if (match[2]) {
attrs += `="${value}"`
}
}
}
if (selfClosing.indexOf(tag) === -1) {
return '<' + tag + attrs + '>'
} else {
return '<' + tag + attrs + ' />'
}
} else {
// closing tag
if (allowedTags.indexOf(match[4].toLowerCase()) !== -1) {
return html
} else {
return ''
}
}
}
function naughtyHRef (href, options) {
// href = href.replace(/[\x00-\x20]+/g, '')
href = href.replace(/<\!\-\-.*?\-\-\>/g, '')
const matches = href.match(/^([a-zA-Z]+)\:/)
if (!matches) {
if (href.match(/^[\/\\]{2}/)) {
return !options.allowProtocolRelative
}
// No scheme
return false
}
const scheme = matches[1].toLowerCase()
return options.allowedSchemes.indexOf(scheme) === -1
}
function naughtyIFrame (src, options) {
try {
const parsed = url.parse(src, false, true)
return options.allowedIframeHostnames.index(parsed.hostname) === -1
} catch (e) {
return true
}
}

View File

@@ -7,6 +7,7 @@ import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager' import ConfigManager from 'browser/main/lib/ConfigManager'
import katex from 'katex' import katex from 'katex'
import { lastFindInArray } from './utils' import { lastFindInArray } from './utils'
import ee from 'browser/main/lib/eventEmitter'
function createGutter (str, firstLineNumber) { function createGutter (str, firstLineNumber) {
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1 if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
@@ -27,32 +28,6 @@ class Markdown {
html: true, html: true,
xhtmlOut: true, xhtmlOut: true,
breaks: config.preview.breaks, breaks: config.preview.breaks,
highlight: function (str, lang) {
const delimiter = ':'
const langInfo = lang.split(delimiter)
const langType = langInfo[0]
const fileName = langInfo[1] || ''
const firstLineNumber = parseInt(langInfo[2], 10)
if (langType === 'flowchart') {
return `<pre class="flowchart">${str}</pre>`
}
if (langType === 'sequence') {
return `<pre class="sequence">${str}</pre>`
}
if (langType === 'chart') {
return `<pre class="chart">${str}</pre>`
}
if (langType === 'mermaid') {
return `<pre class="mermaid">${str}</pre>`
}
return '<pre class="code CodeMirror">' +
'<span class="filename">' + fileName + '</span>' +
createGutter(str, firstLineNumber) +
'<code class="' + langType + '">' +
str +
'</code></pre>'
},
sanitize: 'STRICT' sanitize: 'STRICT'
} }
@@ -105,7 +80,11 @@ class Markdown {
'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'], 'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
'input': ['type', 'id', 'checked'] 'input': ['type', 'id', 'checked']
}, },
allowedIframeHostnames: ['www.youtube.com'] allowedIframeHostnames: ['www.youtube.com'],
selfClosing: [ 'img', 'br', 'hr', 'input' ],
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ],
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
allowProtocolRelative: true
}) })
} }
@@ -152,6 +131,39 @@ class Markdown {
this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']}) this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']})
this.md.use(require('./markdown-it-frontmatter')) this.md.use(require('./markdown-it-frontmatter'))
this.md.use(require('./markdown-it-fence'), {
chart: token => {
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="chart" data-height="${token.parameters.height}">${token.content}</div>
</pre>`
},
flowchart: token => {
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
</pre>`
},
mermaid: token => {
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="mermaid" data-height="${token.parameters.height}">${token.content}</div>
</pre>`
},
sequence: token => {
return `<pre class="fence" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
<div class="sequence" data-height="${token.parameters.height}">${token.content}</div>
</pre>`
}
}, token => {
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
<span class="filename">${token.fileName}</span>
${createGutter(token.content, token.firstLineNumber)}
<code class="${token.langType}">${token.content}</code>
</pre>`
})
const deflate = require('markdown-it-plantuml/lib/deflate') const deflate = require('markdown-it-plantuml/lib/deflate')
this.md.use(require('markdown-it-plantuml'), '', { this.md.use(require('markdown-it-plantuml'), '', {
generateSource: function (umlCode) { generateSource: function (umlCode) {
@@ -223,8 +235,12 @@ class Markdown {
if (!liToken.attrs) { if (!liToken.attrs) {
liToken.attrs = [] liToken.attrs = []
} }
if (config.preview.lineThroughCheckbox) {
liToken.attrs.push(['class', `taskListItem${match[1] !== ' ' ? ' checked' : ''}`])
} else {
liToken.attrs.push(['class', 'taskListItem']) liToken.attrs.push(['class', 'taskListItem'])
} }
}
content = `<label class='taskListItem${match[1] !== ' ' ? ' checked' : ''}' for='checkbox-${startLine + 1}'><input type='checkbox'${match[1] !== ' ' ? ' checked' : ''} id='checkbox-${startLine + 1}'/> ${content.substring(4, content.length)}</label>` content = `<label class='taskListItem${match[1] !== ' ' ? ' checked' : ''}' for='checkbox-${startLine + 1}'><input type='checkbox'${match[1] !== ' ' ? ' checked' : ''} id='checkbox-${startLine + 1}'/> ${content.substring(4, content.length)}</label>`
} }
} }

View File

@@ -37,3 +37,10 @@ body[data-theme="monokai"]
border-left 1px solid $ui-monokai-borderColor border-left 1px solid $ui-monokai-borderColor
.empty-message .empty-message
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.root
background-color $ui-dracula-noteDetail-backgroundColor
border-left 1px solid $ui-dracula-borderColor
.empty-message
color $ui-dracula-text-color

View File

@@ -159,3 +159,29 @@ body[data-theme="monokai"]
color $ui-monokai-button--active-color color $ui-monokai-button--active-color
.search-optionList-item-name-surfix .search-optionList-item-name-surfix
color $ui-monokai-inactive-text-color color $ui-monokai-inactive-text-color
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
&:hover
color #f8f8f2
background-color $ui-dark-button--hover-backgroundColor
border-color $ui-dracula-borderColor
.search-optionList
color #f8f8f2
border-color $ui-dracula-borderColor
background-color $ui-dracula-button-backgroundColor
.search-optionList-item
&:hover
background-color lighten($ui-dracula-button--hover-backgroundColor, 15%)
.search-optionList-item--active
background-color $ui-dracula-button--active-backgroundColor
color $ui-dracula-button--active-color
&:hover
background-color $ui-dark-button--hover-backgroundColor
color $ui-dracula-button--active-color
.search-optionList-item-name-surfix
color $ui-dracula-inactive-text-color

View File

@@ -257,3 +257,43 @@ body[data-theme="monokai"]
color $ui-dark-inactive-text-color color $ui-dark-inactive-text-color
&:hover &:hover
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.control-infoButton-panel
background-color $ui-dracula-noteList-backgroundColor
.control-infoButton-panel-trash
background-color $ui-dracula-noteList-backgroundColor
.modification-date
color $ui-dracula-text-color
.modification-date-desc
color $ui-inactive-text-color
.infoPanel-defaul-count
color $ui-dracula-text-color
.infoPanel-sub-count
color $ui-inactive-text-color
.infoPanel-default
color $ui-dracula-text-color
.infoPanel-sub
color $ui-inactive-text-color
.infoPanel-noteLink
background-color alpha($ui-dracula-borderColor, 20%)
color $ui-dracula-text-color
[id=export-wrap]
button
color $ui-dark-inactive-text-color
&:hover
background-color alpha($ui-dracula-borderColor, 20%)
color $ui-dracula-text-color
p
color $ui-dark-inactive-text-color
&:hover
color $ui-dracula-text-color

View File

@@ -61,11 +61,14 @@ class MarkdownNoteDetail extends React.Component {
const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT' const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT'
this.handleSwitchMode(reversedType) this.handleSwitchMode(reversedType)
}) })
ee.on('hotkey:deletenote', this.handleDeleteNote.bind(this))
ee.on('code:generate-toc', this.generateToc) ee.on('code:generate-toc', this.generateToc)
} }
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if (nextProps.note.key !== this.props.note.key && !this.state.isMovingNote) { const isNewNote = nextProps.note.key !== this.props.note.key
const hasDeletedTags = nextProps.note.tags.length < this.props.note.tags.length
if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) {
if (this.saveQueue != null) this.saveNow() if (this.saveQueue != null) this.saveNow()
this.setState({ this.setState({
note: Object.assign({}, nextProps.note) note: Object.assign({}, nextProps.note)
@@ -91,7 +94,7 @@ class MarkdownNoteDetail extends React.Component {
handleUpdateContent () { handleUpdateContent () {
const { note } = this.state const { note } = this.state
note.content = this.refs.content.value note.content = this.refs.content.value
note.title = markdown.strip(striptags(findNoteTitle(note.content))) note.title = markdown.strip(striptags(findNoteTitle(note.content, this.props.config.editor.enableFrontMatterTitle, this.props.config.editor.frontMatterTitleField)))
this.updateNote(note) this.updateNote(note)
} }
@@ -293,9 +296,33 @@ class MarkdownNoteDetail extends React.Component {
}) })
} }
handleDeleteNote () {
this.handleTrashButtonClick()
}
handleClearTodo () {
const { note } = this.state
const splitted = note.content.split('\n')
const clearTodoContent = splitted.map((line) => {
const trimmedLine = line.trim()
if (trimmedLine.match(/\[x\]/i)) {
return line.replace(/\[x\]/i, '[ ]')
} else {
return line
}
}).join('\n')
note.content = clearTodoContent
this.refs.content.setValue(note.content)
this.updateNote(note)
}
renderEditor () { renderEditor () {
const { config, ignorePreviewPointerEvents } = this.props const { config, ignorePreviewPointerEvents } = this.props
const { note } = this.state const { note } = this.state
if (this.state.editorType === 'EDITOR_PREVIEW') { if (this.state.editorType === 'EDITOR_PREVIEW') {
return <MarkdownEditor return <MarkdownEditor
ref='content' ref='content'
@@ -321,7 +348,7 @@ class MarkdownNoteDetail extends React.Component {
} }
render () { render () {
const { data, location } = this.props const { data, location, config } = this.props
const { note, editorType } = this.state const { note, editorType } = this.state
const storageKey = note.storage const storageKey = note.storage
const folderKey = note.folder const folderKey = note.folder
@@ -372,10 +399,12 @@ class MarkdownNoteDetail extends React.Component {
<TagSelect <TagSelect
ref='tags' ref='tags'
value={this.state.note.tags} value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data} data={data}
onChange={this.handleUpdateTag.bind(this)} onChange={this.handleUpdateTag.bind(this)}
/> />
<TodoListPercentage percentageOfTodo={getTodoPercentageOfCompleted(note.content)} /> <TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
</div> </div>
<div styleName='info-right'> <div styleName='info-right'>
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} /> <ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />

View File

@@ -76,3 +76,8 @@ body[data-theme="monokai"]
.root .root
border-left 1px solid $ui-monokai-borderColor border-left 1px solid $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor background-color $ui-monokai-noteDetail-backgroundColor
body[data-theme="dracula"]
.root
border-left 1px solid $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor

View File

@@ -103,3 +103,8 @@ body[data-theme="monokai"]
.info .info
border-color $ui-monokai-borderColor border-color $ui-monokai-borderColor
background-color $ui-monokai-noteDetail-backgroundColor background-color $ui-monokai-noteDetail-backgroundColor
body[data-theme="dracula"]
.info
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor

View File

@@ -112,7 +112,7 @@ class SnippetNoteDetail extends React.Component {
if (this.refs.tags) note.tags = this.refs.tags.value if (this.refs.tags) note.tags = this.refs.tags.value
note.description = this.refs.description.value note.description = this.refs.description.value
note.updatedAt = new Date() note.updatedAt = new Date()
note.title = findNoteTitle(note.description) note.title = findNoteTitle(note.description, false)
this.setState({ this.setState({
note note
@@ -354,12 +354,10 @@ class SnippetNoteDetail extends React.Component {
this.refs['code-' + this.state.snippetIndex].reload() this.refs['code-' + this.state.snippetIndex].reload()
if (this.visibleTabs.offsetWidth > this.allTabs.scrollWidth) { if (this.visibleTabs.offsetWidth > this.allTabs.scrollWidth) {
console.log('no need for arrows')
this.moveTabBarBy(0) this.moveTabBarBy(0)
} else { } else {
const lastTab = this.allTabs.lastChild const lastTab = this.allTabs.lastChild
if (lastTab.offsetLeft + lastTab.offsetWidth < this.visibleTabs.offsetWidth) { if (lastTab.offsetLeft + lastTab.offsetWidth < this.visibleTabs.offsetWidth) {
console.log('need to scroll')
const width = this.visibleTabs.offsetWidth const width = this.visibleTabs.offsetWidth
const newLeft = lastTab.offsetLeft + lastTab.offsetWidth - width const newLeft = lastTab.offsetLeft + lastTab.offsetWidth - width
this.moveTabBarBy(newLeft > 0 ? -newLeft : 0) this.moveTabBarBy(newLeft > 0 ? -newLeft : 0)
@@ -627,7 +625,6 @@ class SnippetNoteDetail extends React.Component {
} }
focusEditor () { focusEditor () {
console.log('code-' + this.state.snippetIndex)
this.refs['code-' + this.state.snippetIndex].focus() this.refs['code-' + this.state.snippetIndex].focus()
} }
@@ -759,6 +756,8 @@ class SnippetNoteDetail extends React.Component {
<TagSelect <TagSelect
ref='tags' ref='tags'
value={this.state.note.tags} value={this.state.note.tags}
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
data={data} data={data}
onChange={(e) => this.handleChange(e)} onChange={(e) => this.handleChange(e)}
/> />

View File

@@ -170,3 +170,20 @@ body[data-theme="monokai"]
.tabList .tabList
background-color $ui-monokai-noteDetail-backgroundColor background-color $ui-monokai-noteDetail-backgroundColor
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.root
border-left 1px solid $ui-dracula-borderColor
background-color $ui-dracula-noteDetail-backgroundColor
.body
background-color $ui-dracula-noteDetail-backgroundColor
.body .description textarea
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color
border 1px solid $ui-dracula-borderColor
.tabList
background-color $ui-dracula-noteDetail-backgroundColor
color $ui-dracula-text-color

View File

@@ -179,10 +179,10 @@ class TagSelect extends React.Component {
} }
render () { render () {
const { value, className } = this.props const { value, className, showTagsAlphabetically } = this.props
const tagList = _.isArray(value) const tagList = _.isArray(value)
? value.map((tag) => { ? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
return ( return (
<span styleName='tag' <span styleName='tag'
key={tag} key={tag}

View File

@@ -76,3 +76,14 @@ body[data-theme="monokai"]
.tag-label .tag-label
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.tag
background-color $ui-dracula-tag-backgroundColor
.tag-removeButton
border-color $ui-dracula-button--focus-borderColor
background-color transparent
.tag-label
color $ui-dracula-borderColor

View File

@@ -63,3 +63,10 @@ body[data-theme="monokai"]
.active .active
background-color #f92672 background-color #f92672
box-shadow 2px 0px 7px #222222 box-shadow 2px 0px 7px #222222
body[data-theme="dracula"]
.control-toggleModeButton
background-color #44475a
.active
background-color #bd93f9
box-shadow 2px 0px 7px #222222

View File

@@ -80,7 +80,6 @@ class Main extends React.Component {
} }
}) })
.then(data => { .then(data => {
console.log(data)
store.dispatch({ store.dispatch({
type: 'ADD_STORAGE', type: 'ADD_STORAGE',
storage: data.storage, storage: data.storage,
@@ -141,7 +140,7 @@ class Main extends React.Component {
componentDidMount () { componentDidMount () {
const { dispatch, config } = this.props const { dispatch, config } = this.props
const supportedThemes = ['dark', 'white', 'solarized-dark', 'monokai'] const supportedThemes = ['dark', 'white', 'solarized-dark', 'monokai', 'dracula']
if (supportedThemes.indexOf(config.ui.theme) !== -1) { if (supportedThemes.indexOf(config.ui.theme) !== -1) {
document.body.setAttribute('data-theme', config.ui.theme) document.body.setAttribute('data-theme', config.ui.theme)
@@ -297,7 +296,7 @@ class Main extends React.Component {
onMouseUp={e => this.handleMouseUp(e)} onMouseUp={e => this.handleMouseUp(e)}
> >
<SideNav <SideNav
{..._.pick(this.props, ['dispatch', 'data', 'config', 'location'])} {..._.pick(this.props, ['dispatch', 'data', 'config', 'params', 'location'])}
width={this.state.navWidth} width={this.state.navWidth}
/> />
{!config.isSideNavFolded && {!config.isSideNavFolded &&

View File

@@ -79,3 +79,7 @@ body[data-theme="solarized-dark"]
body[data-theme="monokai"] body[data-theme="monokai"]
.root, .root--expanded .root, .root--expanded
background-color $ui-monokai-noteList-backgroundColor background-color $ui-monokai-noteList-backgroundColor
body[data-theme="dracula"]
.root, .root--expanded
background-color $ui-dracula-noteList-backgroundColor

View File

@@ -138,3 +138,27 @@ body[data-theme="monokai"]
color $ui-monokai-text-color color $ui-monokai-text-color
&:active &:active
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.root
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
.control
background-color $ui-dracula-noteList-backgroundColor
border-color $ui-dracula-borderColor
.control-sortBy-select
&:hover
transition 0.2s
color $ui-dracula-text-color
.control-button
color $ui-dracula-inactive-text-color
&:hover
color $ui-dracula-text-color
.control-button--active
color $ui-dracula-text-color
&:active
color $ui-dracula-text-color

View File

@@ -56,7 +56,6 @@ class NoteList extends React.Component {
super(props) super(props)
this.selectNextNoteHandler = () => { this.selectNextNoteHandler = () => {
console.log('fired next')
this.selectNextNote() this.selectNextNote()
} }
this.selectPriorNoteHandler = () => { this.selectPriorNoteHandler = () => {
@@ -616,7 +615,6 @@ class NoteList extends React.Component {
.catch((err) => { .catch((err) => {
console.error('Cannot Delete note: ' + err) console.error('Cannot Delete note: ' + err)
}) })
console.log('Notes were all deleted')
} else { } else {
if (!confirmDeleteNote(confirmDeletion, false)) return if (!confirmDeleteNote(confirmDeletion, false)) return
@@ -636,7 +634,6 @@ class NoteList extends React.Component {
}) })
}) })
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE') AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE')
console.log('Notes went to trash')
}) })
.catch((err) => { .catch((err) => {
console.error('Notes could not go to trash: ' + err) console.error('Notes could not go to trash: ' + err)
@@ -996,6 +993,7 @@ class NoteList extends React.Component {
folderName={this.getNoteFolder(note).name} folderName={this.getNoteFolder(note).name}
storageName={this.getNoteStorage(note).name} storageName={this.getNoteStorage(note).name}
viewType={viewType} viewType={viewType}
showTagsAlphabetically={config.ui.showTagsAlphabetically}
/> />
) )
} }

View File

@@ -122,3 +122,8 @@ body[data-theme="monokai"]
.root, .root--folded .root, .root--folded
background-color $ui-monokai-backgroundColor background-color $ui-monokai-backgroundColor
border-right 1px solid $ui-monokai-borderColor border-right 1px solid $ui-monokai-borderColor
body[data-theme="dracula"]
.root, .root--folded
background-color $ui-dracula-backgroundColor
border-right 1px solid $ui-dracula-borderColor

View File

@@ -18,6 +18,11 @@ import TagButton from './TagButton'
import {SortableContainer} from 'react-sortable-hoc' import {SortableContainer} from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n' import i18n from 'browser/lib/i18n'
import context from 'browser/lib/context' import context from 'browser/lib/context'
import { remote } from 'electron'
function matchActiveTags (tags, activeTags) {
return _.every(activeTags, v => tags.indexOf(v) >= 0)
}
class SideNav extends React.Component { class SideNav extends React.Component {
// TODO: should not use electron stuff v0.7 // TODO: should not use electron stuff v0.7
@@ -30,6 +35,52 @@ class SideNav extends React.Component {
EventEmitter.off('side:preferences', this.handleMenuButtonClick) EventEmitter.off('side:preferences', this.handleMenuButtonClick)
} }
deleteTag (tag) {
const selectedButton = remote.dialog.showMessageBox(remote.getCurrentWindow(), {
ype: 'warning',
message: i18n.__('Confirm tag deletion'),
detail: i18n.__('This will permanently remove this tag.'),
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
})
if (selectedButton === 0) {
const { data, dispatch, location, params } = this.props
const notes = data.noteMap
.map(note => note)
.filter(note => note.tags.indexOf(tag) !== -1)
.map(note => {
note = Object.assign({}, note)
note.tags = note.tags.slice()
note.tags.splice(note.tags.indexOf(tag), 1)
return note
})
Promise
.all(notes.map(note => dataApi.updateNote(note.storage, note.key, note)))
.then(updatedNotes => {
updatedNotes.forEach(note => {
dispatch({
type: 'UPDATE_NOTE',
note
})
})
if (location.pathname.match('/tags')) {
const tags = params.tagname.split(' ')
const index = tags.indexOf(tag)
if (index !== -1) {
tags.splice(index, 1)
this.context.router.push(`/tags/${tags.map(tag => encodeURIComponent(tag)).join(' ')}`)
}
}
})
}
}
handleMenuButtonClick (e) { handleMenuButtonClick (e) {
openModal(PreferencesModal) openModal(PreferencesModal)
} }
@@ -44,6 +95,17 @@ class SideNav extends React.Component {
router.push('/starred') router.push('/starred')
} }
handleTagContextMenu (e, tag) {
const menu = []
menu.push({
label: i18n.__('Delete Tag'),
click: this.deleteTag.bind(this, tag)
})
context.popup(menu)
}
handleToggleButtonClick (e) { handleToggleButtonClick (e) {
const { dispatch, config } = this.props const { dispatch, config } = this.props
@@ -144,12 +206,20 @@ class SideNav extends React.Component {
tagListComponent () { tagListComponent () {
const { data, location, config } = this.props const { data, location, config } = this.props
const relatedTags = this.getRelatedTags(this.getActiveTags(location.pathname), data.noteMap) const activeTags = this.getActiveTags(location.pathname)
const relatedTags = this.getRelatedTags(activeTags, data.noteMap)
let tagList = _.sortBy(data.tagNoteMap.map( let tagList = _.sortBy(data.tagNoteMap.map(
(tag, name) => ({ name, size: tag.size, related: relatedTags.has(name) }) (tag, name) => ({ name, size: tag.size, related: relatedTags.has(name) })
), ['name']).filter( ).filter(
tag => tag.size > 0 tag => tag.size > 0
) ), ['name'])
if (config.ui.enableLiveNoteCounts && activeTags.length !== 0) {
const notesTags = data.noteMap.map(note => note.tags)
tagList = tagList.map(tag => {
tag.size = notesTags.filter(tags => tags.includes(tag.name) && matchActiveTags(tags, activeTags)).length
return tag
})
}
if (config.sortTagsBy === 'COUNTER') { if (config.sortTagsBy === 'COUNTER') {
tagList = _.sortBy(tagList, item => (0 - item.size)) tagList = _.sortBy(tagList, item => (0 - item.size))
} }
@@ -165,6 +235,7 @@ class SideNav extends React.Component {
name={tag.name} name={tag.name}
handleClickTagListItem={this.handleClickTagListItem.bind(this)} handleClickTagListItem={this.handleClickTagListItem.bind(this)}
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)} handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
handleContextMenu={this.handleTagContextMenu.bind(this)}
isActive={this.getTagActive(location.pathname, tag.name)} isActive={this.getTagActive(location.pathname, tag.name)}
isRelated={tag.related} isRelated={tag.related}
key={tag.name} key={tag.name}
@@ -247,7 +318,6 @@ class SideNav extends React.Component {
.catch((err) => { .catch((err) => {
console.error('Cannot Delete note: ' + err) console.error('Cannot Delete note: ' + err)
}) })
console.log('Trash emptied')
} }
handleFilterButtonContextMenu (event) { handleFilterButtonContextMenu (event) {

View File

@@ -80,3 +80,14 @@ body[data-theme="monokai"]
color $ui-monokai-active-color color $ui-monokai-active-color
&:active &:active
color $ui-monokai-active-color color $ui-monokai-active-color
body[data-theme="dracula"]
navButtonColor()
.zoom
border-color $ui-dark-borderColor
color $ui-dracula-text-color
&:hover
transition 0.15s
color $ui-dracula-active-color
&:active
color $ui-dracula-active-color

View File

@@ -256,3 +256,25 @@ body[data-theme="monokai"]
input input
background-color $ui-monokai-noteList-backgroundColor background-color $ui-monokai-noteList-backgroundColor
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.root, .root--expanded
background-color $ui-dracula-noteList-backgroundColor
.control
border-color $ui-dracula-borderColor
.control-search
background-color $ui-dracula-noteList-backgroundColor
.control-search-icon
absolute top bottom left
line-height 32px
width 35px
color $ui-dracula-inactive-text-color
background-color $ui-dracula-noteList-backgroundColor
.control-search-input
background-color $ui-dracula-noteList-backgroundColor
input
background-color $ui-dracula-noteList-backgroundColor
color $ui-dracula-text-color

View File

@@ -18,6 +18,9 @@ body
::-webkit-scrollbar ::-webkit-scrollbar
width 12px width 12px
::-webkit-scrollbar-corner
background-color: transparent;
::-webkit-scrollbar-thumb ::-webkit-scrollbar-thumb
background-color rgba(0, 0, 0, 0.15) background-color rgba(0, 0, 0, 0.15)
@@ -162,6 +165,15 @@ body[data-theme="monokai"]
.sortableItemHelper .sortableItemHelper
color: $ui-monokai-text-color color: $ui-monokai-text-color
body[data-theme="dracula"]
::-webkit-scrollbar-thumb
background-color rgba(0, 0, 0, 0.3)
.ModalBase
.modalBack
background-color $ui-dracula-backgroundColor
.sortableItemHelper
color: $ui-dracula-text-color
body[data-theme="default"] body[data-theme="default"]
.SideNav ::-webkit-scrollbar-thumb .SideNav ::-webkit-scrollbar-thumb
background-color rgba(255, 255, 255, 0.3) background-color rgba(255, 255, 255, 0.3)

View File

@@ -45,7 +45,6 @@ function initAwsMobileAnalytics () {
if (getSendEventCond()) return if (getSendEventCond()) return
AWS.config.credentials.get((err) => { AWS.config.credentials.get((err) => {
if (!err) { if (!err) {
console.log('Cognito Identity ID: ' + AWS.config.credentials.identityId)
recordDynamicCustomEvent('APP_STARTED') recordDynamicCustomEvent('APP_STARTED')
recordStaticCustomEvent() recordStaticCustomEvent()
} }
@@ -58,7 +57,7 @@ function recordDynamicCustomEvent (type, options = {}) {
mobileAnalyticsClient.recordEvent(type, options) mobileAnalyticsClient.recordEvent(type, options)
} catch (analyticsError) { } catch (analyticsError) {
if (analyticsError instanceof ReferenceError) { if (analyticsError instanceof ReferenceError) {
console.log(analyticsError.name + ': ' + analyticsError.message) console.error(analyticsError.name + ': ' + analyticsError.message)
} }
} }
} }
@@ -71,7 +70,7 @@ function recordStaticCustomEvent () {
}) })
} catch (analyticsError) { } catch (analyticsError) {
if (analyticsError instanceof ReferenceError) { if (analyticsError instanceof ReferenceError) {
console.log(analyticsError.name + ': ' + analyticsError.message) console.error(analyticsError.name + ': ' + analyticsError.message)
} }
} }
} }

View File

@@ -24,7 +24,8 @@ export const DEFAULT_CONFIG = {
amaEnabled: true, amaEnabled: true,
hotkey: { hotkey: {
toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E', toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E',
toggleMode: OSX ? 'Command + Option + M' : 'Ctrl + M' toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M',
deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace'
}, },
ui: { ui: {
language: 'en', language: 'en',
@@ -43,11 +44,14 @@ export const DEFAULT_CONFIG = {
enableRulers: false, enableRulers: false,
rulers: [80, 120], rulers: [80, 120],
displayLineNumbers: true, displayLineNumbers: true,
switchPreview: 'BLUR', // Available value: RIGHTCLICK, BLUR switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK'
delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE'
scrollPastEnd: false, scrollPastEnd: false,
type: 'SPLIT', type: 'SPLIT', // 'SPLIT', 'EDITOR_PREVIEW'
fetchUrlTitle: true, fetchUrlTitle: true,
enableTableEditor: false enableTableEditor: false,
enableFrontMatterTitle: true,
frontMatterTitleField: 'title'
}, },
preview: { preview: {
fontSize: '14', fontSize: '14',
@@ -60,12 +64,14 @@ export const DEFAULT_CONFIG = {
latexBlockClose: '$$', latexBlockClose: '$$',
plantUMLServerAddress: 'http://www.plantuml.com/plantuml', plantUMLServerAddress: 'http://www.plantuml.com/plantuml',
scrollPastEnd: false, scrollPastEnd: false,
scrollSync: true,
smartQuotes: true, smartQuotes: true,
breaks: true, breaks: true,
smartArrows: false, smartArrows: false,
allowCustomCSS: false, allowCustomCSS: false,
customCSS: '', customCSS: '',
sanitize: 'STRICT' // 'STRICT', 'ALLOW_STYLES', 'NONE' sanitize: 'STRICT', // 'STRICT', 'ALLOW_STYLES', 'NONE'
lineThroughCheckbox: true
}, },
blog: { blog: {
type: 'wordpress', // Available value: wordpress, add more types in the future plz type: 'wordpress', // Available value: wordpress, add more types in the future plz
@@ -147,6 +153,8 @@ function set (updates) {
document.body.setAttribute('data-theme', 'solarized-dark') document.body.setAttribute('data-theme', 'solarized-dark')
} else if (newConfig.ui.theme === 'monokai') { } else if (newConfig.ui.theme === 'monokai') {
document.body.setAttribute('data-theme', 'monokai') document.body.setAttribute('data-theme', 'monokai')
} else if (newConfig.ui.theme === 'dracula') {
document.body.setAttribute('data-theme', 'dracula')
} else { } else {
document.body.setAttribute('data-theme', 'default') document.body.setAttribute('data-theme', 'default')
} }

View File

@@ -529,7 +529,6 @@ function handleAttachmentLinkPaste (storageKey, noteKey, linkText) {
return modifiedLinkText return modifiedLinkText
}) })
} else { } else {
console.log('One if the parameters was null -> Do nothing..')
return Promise.resolve(linkText) return Promise.resolve(linkText)
} }
} }

View File

@@ -14,7 +14,6 @@ function renameStorage (key, name) {
cachedStorageList = JSON.parse(localStorage.getItem('storages')) cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('invalid storages') if (!_.isArray(cachedStorageList)) throw new Error('invalid storages')
} catch (err) { } catch (err) {
console.log('error got')
console.error(err) console.error(err)
return Promise.reject(err) return Promise.reject(err)
} }

View File

@@ -31,13 +31,9 @@ function resolveStorageData (storageCache) {
const version = parseInt(storage.version, 10) const version = parseInt(storage.version, 10)
if (version >= 1) { if (version >= 1) {
if (version > 1) {
console.log('The repository version is newer than one of current app.')
}
return Promise.resolve(storage) return Promise.resolve(storage)
} }
console.log('Transform Legacy storage', storage.path)
return migrateFromV6Storage(storage.path) return migrateFromV6Storage(storage.path)
.then(() => storage) .then(() => storage)
} }

View File

@@ -9,7 +9,7 @@ function resolveStorageNotes (storage) {
notePathList = sander.readdirSync(notesDirPath) notePathList = sander.readdirSync(notesDirPath)
} catch (err) { } catch (err) {
if (err.code === 'ENOENT') { if (err.code === 'ENOENT') {
console.log(notesDirPath, ' doesn\'t exist.') console.error(notesDirPath, ' doesn\'t exist.')
sander.mkdirSync(notesDirPath) sander.mkdirSync(notesDirPath)
} else { } else {
console.warn('Failed to find note dir', notesDirPath, err) console.warn('Failed to find note dir', notesDirPath, err)

View File

@@ -12,7 +12,6 @@ function toggleStorage (key, isOpen) {
cachedStorageList = JSON.parse(localStorage.getItem('storages')) cachedStorageList = JSON.parse(localStorage.getItem('storages'))
if (!_.isArray(cachedStorageList)) throw new Error('invalid storages') if (!_.isArray(cachedStorageList)) throw new Error('invalid storages')
} catch (err) { } catch (err) {
console.log('error got')
console.error(err) console.error(err)
return Promise.reject(err) return Promise.reject(err)
} }

View File

@@ -14,7 +14,6 @@ function once (name, listener) {
} }
function emit (name, ...args) { function emit (name, ...args) {
console.log(name)
remote.getCurrentWindow().webContents.send(name, ...args) remote.getCurrentWindow().webContents.send(name, ...args)
} }

View File

@@ -14,14 +14,13 @@ nodeIpc.connectTo(
path.join(app.getPath('userData'), 'boostnote.service'), path.join(app.getPath('userData'), 'boostnote.service'),
function () { function () {
nodeIpc.of.node.on('error', function (err) { nodeIpc.of.node.on('error', function (err) {
console.log(err) console.error(err)
}) })
nodeIpc.of.node.on('connect', function () { nodeIpc.of.node.on('connect', function () {
console.log('Connected successfully')
ipcRenderer.send('config-renew', {config: ConfigManager.get()}) ipcRenderer.send('config-renew', {config: ConfigManager.get()})
}) })
nodeIpc.of.node.on('disconnect', function () { nodeIpc.of.node.on('disconnect', function () {
console.log('disconnected') return
}) })
} }
) )

View File

@@ -3,5 +3,8 @@ import ee from 'browser/main/lib/eventEmitter'
module.exports = { module.exports = {
'toggleMode': () => { 'toggleMode': () => {
ee.emit('topbar:togglemodebutton') ee.emit('topbar:togglemodebutton')
},
'deleteNote': () => {
ee.emit('hotkey:deletenote')
} }
} }

View File

@@ -128,3 +128,29 @@ body[data-theme="monokai"]
.control-confirmButton .control-confirmButton
colorMonokaiPrimaryButton() colorMonokaiPrimaryButton()
body[data-theme="dracula"]
.root
modalDracula()
width 500px
height 270px
overflow hidden
position relative
.header
background-color transparent
border-color $ui-dark-borderColor
color $ui-dracula-text-color
.control-folder-label
color $ui-dracula-text-color
.control-folder-input
border 1px solid $ui-input--create-folder-modal
color white
.description
color $ui-inactive-text-color
.control-confirmButton
colorDraculaPrimaryButton()

View File

@@ -97,3 +97,20 @@ body[data-theme="monokai"]
.description .description
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.root
background-color transparent
.header
color $ui-dracula-text-color
.control-button
border-color $ui-dracula-borderColor
color $ui-dracula-text-color
background-color transparent
&:focus
colorDraculaPrimaryButton()
.description
color $ui-dracula-text-color

View File

@@ -138,6 +138,10 @@ colorMonokaiControl()
background-color $ui-monokai-button-backgroundColor background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color color $ui-monokai-text-color
colorDraculaControl()
border none
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
body[data-theme="dark"] body[data-theme="dark"]
.root .root
@@ -220,3 +224,30 @@ body[data-theme="monokai"]
.group-section-control .group-section-control
select, .group-section-control-input select, .group-section-control-input
colorMonokaiControl() colorMonokaiControl()
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
.group-header
color $ui-dracula-text-color
border-color $ui-dracula-borderColor
.group-header2
color $ui-dracula-text-color
.group-section-control-input
border-color $ui-dracula-borderColor
.group-control
border-color $ui-dracula-borderColor
.group-control-leftButton
colorDarkDefaultButton()
border-color $ui-dracula-borderColor
.group-control-rightButton
colorDraculaPrimaryButton()
.group-hint
colorDraculaControl()
.group-section-control
select, .group-section-control-input
colorDraculaControl()

View File

@@ -23,21 +23,29 @@ class Crowdfunding extends React.Component {
return ( return (
<div styleName='root'> <div styleName='root'>
<div styleName='header'>{i18n.__('Crowdfunding')}</div> <div styleName='header'>{i18n.__('Crowdfunding')}</div>
<p>{i18n.__('Dear Boostnote users,')}</p>
<br />
<p>{i18n.__('Thank you for using Boostnote!')}</p> <p>{i18n.__('Thank you for using Boostnote!')}</p>
<p>{i18n.__('Boostnote is used in about 200 different countries and regions by an awesome community of developers.')}</p>
<br /> <br />
<p>{i18n.__('To support our growing userbase, and satisfy community expectations,')}</p> <p>{i18n.__('We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.')}</p>
<p>{i18n.__('we would like to invest more time and resources in this project.')}</p> <p>{i18n.__('Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.')}</p>
<br /> <br />
<p>{i18n.__('If you use Boostnote and see its potential, help us out by supporting the project on OpenCollective!')}</p> <p>{i18n.__('### Sustainable Open Source Ecosystem')}</p>
<p>{i18n.__('We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.')}</p>
<p>{i18n.__('The original reason why we made IssueHunt was to reward our contributors of Boostnote project. Weve got tons of Github stars and hundred of contributors in two years.')}</p>
<p>{i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}</p>
<br /> <br />
<p>{i18n.__('Thanks,')}</p> <p>{i18n.__('### We believe Meritocracy')}</p>
<p>{i18n.__('We think developers who has skill and did great things must be rewarded properly.')}</p>
<p>{i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}</p>
<p>{i18n.__('It sometimes looks like exploitation.')}</p>
<p>{i18n.__('Weve realized IssueHunt could enhance sustainability of open-source ecosystem.')}</p>
<br />
<p>{i18n.__('As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.')}</p>
<br />
<p>{i18n.__('Thank you,')}</p>
<p>{i18n.__('The Boostnote Team')}</p> <p>{i18n.__('The Boostnote Team')}</p>
<br /> <br />
<button styleName='cf-link'> <button styleName='cf-link'>
<a href='https://opencollective.com/boostnoteio' onClick={(e) => this.handleLinkClick(e)}>{i18n.__('Support via OpenCollective')}</a> <a href='http://bit.ly/issuehunt-from-boostnote-app' onClick={(e) => this.handleLinkClick(e)}>{i18n.__('See IssueHunt')}</a>
</button> </button>
</div> </div>
) )

View File

@@ -41,3 +41,9 @@ body[data-theme="monokai"]
color $ui-monokai-text-color color $ui-monokai-text-color
p p
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
p
color $ui-dracula-text-color

View File

@@ -154,3 +154,26 @@ body[data-theme="monokai"]
.folderItem-right-dangerButton .folderItem-right-dangerButton
colorMonokaiPrimaryButton() colorMonokaiPrimaryButton()
body[data-theme="dracula"]
.folderItem
&:hover
background-color $ui-dracula-button-backgroundColor
.folderItem-left-danger
color $danger-color
.folderItem-left-key
color $ui-dark-inactive-text-color
.folderItem-left-colorButton
colorDraculaPrimaryButton()
.folderItem-right-button
colorDraculaPrimaryButton()
.folderItem-right-confirmButton
colorDraculaPrimaryButton()
.folderItem-right-dangerButton
colorDraculaPrimaryButton()

View File

@@ -28,11 +28,21 @@ class HotkeyTab extends React.Component {
}}) }})
} }
this.handleSettingError = (err) => { this.handleSettingError = (err) => {
if (
this.state.config.hotkey.toggleMain === '' ||
this.state.config.hotkey.toggleMode === ''
) {
this.setState({keymapAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}})
} else {
this.setState({keymapAlert: { this.setState({keymapAlert: {
type: 'error', type: 'error',
message: err.message != null ? err.message : i18n.__('An error occurred!') message: err.message != null ? err.message : i18n.__('An error occurred!')
}}) }})
} }
}
this.oldHotkey = this.state.config.hotkey this.oldHotkey = this.state.config.hotkey
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
@@ -68,7 +78,8 @@ class HotkeyTab extends React.Component {
const { config } = this.state const { config } = this.state
config.hotkey = { config.hotkey = {
toggleMain: this.refs.toggleMain.value, toggleMain: this.refs.toggleMain.value,
toggleMode: this.refs.toggleMode.value toggleMode: this.refs.toggleMode.value,
deleteNote: this.refs.deleteNote.value
} }
this.setState({ this.setState({
config config
@@ -127,6 +138,17 @@ class HotkeyTab extends React.Component {
/> />
</div> </div>
</div> </div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Delete Note')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
ref='deleteNote'
value={config.hotkey.deleteNote}
type='text'
/>
</div>
</div>
<div styleName='group-control'> <div styleName='group-control'>
<button styleName='group-control-leftButton' <button styleName='group-control-leftButton'
onClick={(e) => this.handleHintToggleButtonClick(e)} onClick={(e) => this.handleHintToggleButtonClick(e)}

View File

@@ -84,7 +84,7 @@ class InfoTab extends React.Component {
>{i18n.__('GitHub')}</a> >{i18n.__('GitHub')}</a>
</li> </li>
<li> <li>
<a href='https://boostlog.io/@junp1234' <a href='https://medium.com/boostnote'
onClick={(e) => this.handleLinkClick(e)} onClick={(e) => this.handleLinkClick(e)}
>{i18n.__('Blog')}</a> >{i18n.__('Blog')}</a>
</li> </li>

View File

@@ -75,3 +75,10 @@ body[data-theme="monokai"]
.list .list
a a
color $ui-monokai-active-color color $ui-monokai-active-color
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
.list
a
color $ui-dracula-active-color

View File

@@ -139,3 +139,27 @@ body[data-theme="monokai"]
background-color $ui-monokai-button--active-backgroundColor background-color $ui-monokai-button--active-backgroundColor
&:hover &:hover
color white color white
body[data-theme="dracula"]
.root
background-color transparent
.top-bar
background-color transparent
border-color $ui-dracula-borderColor
p
color $ui-dracula-text-color
.nav
background-color transparent
border-color $ui-dracula-borderColor
.nav-button
background-color transparent
color $ui-dracula-text-color
&:hover
color $ui-dracula-text-color
.nav-button--active
@extend .nav-button
color $ui-dracula-button--active-color
background-color $ui-dracula-button--active-backgroundColor
&:hover
color #f8f8f2

View File

@@ -55,7 +55,11 @@ class SnippetList extends React.Component {
defineSnippetStyleName (snippet) { defineSnippetStyleName (snippet) {
const { currentSnippet } = this.props const { currentSnippet } = this.props
if (currentSnippet == null) return
if (currentSnippet == null) {
return 'snippet-item'
}
if (currentSnippet.id === snippet.id) { if (currentSnippet.id === snippet.id) {
return 'snippet-item-selected' return 'snippet-item-selected'
} else { } else {

View File

@@ -6,6 +6,9 @@ import i18n from 'browser/lib/i18n'
import dataApi from 'browser/main/lib/dataApi' import dataApi from 'browser/main/lib/dataApi'
import SnippetList from './SnippetList' import SnippetList from './SnippetList'
import eventEmitter from 'browser/main/lib/eventEmitter' import eventEmitter from 'browser/main/lib/eventEmitter'
import copy from 'copy-to-clipboard'
const path = require('path')
class SnippetTab extends React.Component { class SnippetTab extends React.Component {
constructor (props) { constructor (props) {
@@ -16,6 +19,17 @@ class SnippetTab extends React.Component {
this.changeDelay = null this.changeDelay = null
} }
notify (title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join(
'file://',
global.__dirname,
'../../resources/app.png'
)
}
return new window.Notification(title, options)
}
handleSnippetNameOrPrefixChange () { handleSnippetNameOrPrefixChange () {
clearTimeout(this.changeDelay) clearTimeout(this.changeDelay)
this.changeDelay = setTimeout(() => { this.changeDelay = setTimeout(() => {
@@ -27,6 +41,7 @@ class SnippetTab extends React.Component {
handleSnippetSelect (snippet) { handleSnippetSelect (snippet) {
const { currentSnippet } = this.state const { currentSnippet } = this.state
if (snippet !== null) {
if (currentSnippet === null || currentSnippet.id !== snippet.id) { if (currentSnippet === null || currentSnippet.id !== snippet.id) {
dataApi.fetchSnippet(snippet.id).then(changedSnippet => { dataApi.fetchSnippet(snippet.id).then(changedSnippet => {
// notify the snippet editor to load the content of the new snippet // notify the snippet editor to load the content of the new snippet
@@ -35,6 +50,7 @@ class SnippetTab extends React.Component {
}) })
} }
} }
}
onSnippetNameOrPrefixChanged (e, type) { onSnippetNameOrPrefixChanged (e, type) {
const newSnippet = Object.assign({}, this.state.currentSnippet) const newSnippet = Object.assign({}, this.state.currentSnippet)
@@ -54,6 +70,17 @@ class SnippetTab extends React.Component {
} }
} }
handleCopySnippet (e) {
const showCopyNotification = this.props.config.ui.showCopyNotification
copy(this.state.currentSnippet.content)
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
}
}
render () { render () {
const { config, storageKey } = this.props const { config, storageKey } = this.props
const { currentSnippet } = this.state const { currentSnippet } = this.state
@@ -70,6 +97,13 @@ class SnippetTab extends React.Component {
onSnippetDeleted={this.handleDeleteSnippet.bind(this)} onSnippetDeleted={this.handleDeleteSnippet.bind(this)}
currentSnippet={currentSnippet} /> currentSnippet={currentSnippet} />
<div styleName='snippet-detail' style={{visibility: currentSnippet ? 'visible' : 'hidden'}}> <div styleName='snippet-detail' style={{visibility: currentSnippet ? 'visible' : 'hidden'}}>
<div styleName='group-section'>
<div styleName='group-section-control'>
<button styleName='group-control-rightButton'
onClick={e => this.handleCopySnippet(e)}>{i18n.__('Copy')}
</button>
</div>
</div>
<div styleName='group-section'> <div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Snippet name')}</div> <div styleName='group-section-label'>{i18n.__('Snippet name')}</div>
<div styleName='group-section-control'> <div styleName='group-section-control'>

View File

@@ -196,3 +196,19 @@ body[data-theme="monokai"]
color white color white
.group-control-button .group-control-button
colorMonokaiPrimaryButton() colorMonokaiPrimaryButton()
body[data-theme="dracula"]
.snippets
background $ui-dracula-backgroundColor
.snippet-item
color #f8f8f2
&::after
background $ui-dracula-borderColor
&:hover
background darken($ui-dracula-backgroundColor, 5)
.snippet-item-selected
background darken($ui-dracula-backgroundColor, 5)
.snippet-detail
color #f8f8f2
.group-control-button
colorDraculaPrimaryButton()

View File

@@ -236,3 +236,41 @@ body[data-theme="monokai"]
.addStorage-body-control-cancelButton .addStorage-body-control-cancelButton
colorDarkDefaultButton() colorDarkDefaultButton()
border-color $ui-monokai-borderColor border-color $ui-monokai-borderColor
body[data-theme="dracula"]
.root
color $ui-dracula-text-color
.folderList-item
border-bottom $ui-dracula-borderColor
.folderList-empty
color $ui-dracula-text-color
.list-empty
color $ui-dracula-text-color
.list-control-addStorageButton
border-color $ui-dracula-button-backgroundColor
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
.addStorage-header
color $ui-dracula-text-color
border-color $ui-dracula-borderColor
.addStorage-body-section-name-input
border-color $$ui-dracula-borderColor
.addStorage-body-section-type-description
color $ui-dracula-text-color
.addStorage-body-section-path-button
colorPrimaryButton()
.addStorage-body-control
border-color $ui-dracula-borderColor
.addStorage-body-control-createButton
colorDarkPrimaryButton()
.addStorage-body-control-cancelButton
colorDarkDefaultButton()
border-color $ui-dracula-borderColor

View File

@@ -71,6 +71,9 @@ class UiTab extends React.Component {
showCopyNotification: this.refs.showCopyNotification.checked, showCopyNotification: this.refs.showCopyNotification.checked,
confirmDeletion: this.refs.confirmDeletion.checked, confirmDeletion: this.refs.confirmDeletion.checked,
showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked, showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked,
showTagsAlphabetically: this.refs.showTagsAlphabetically.checked,
saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked,
enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked,
disableDirectWrite: this.refs.uiD2w != null disableDirectWrite: this.refs.uiD2w != null
? this.refs.uiD2w.checked ? this.refs.uiD2w.checked
: false : false
@@ -89,7 +92,9 @@ class UiTab extends React.Component {
snippetDefaultLanguage: this.refs.editorSnippetDefaultLanguage.value, snippetDefaultLanguage: this.refs.editorSnippetDefaultLanguage.value,
scrollPastEnd: this.refs.scrollPastEnd.checked, scrollPastEnd: this.refs.scrollPastEnd.checked,
fetchUrlTitle: this.refs.editorFetchUrlTitle.checked, fetchUrlTitle: this.refs.editorFetchUrlTitle.checked,
enableTableEditor: this.refs.enableTableEditor.checked enableTableEditor: this.refs.enableTableEditor.checked,
enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked,
frontMatterTitleField: this.refs.frontMatterTitleField.value
}, },
preview: { preview: {
fontSize: this.refs.previewFontSize.value, fontSize: this.refs.previewFontSize.value,
@@ -102,11 +107,13 @@ class UiTab extends React.Component {
latexBlockClose: this.refs.previewLatexBlockClose.value, latexBlockClose: this.refs.previewLatexBlockClose.value,
plantUMLServerAddress: this.refs.previewPlantUMLServerAddress.value, plantUMLServerAddress: this.refs.previewPlantUMLServerAddress.value,
scrollPastEnd: this.refs.previewScrollPastEnd.checked, scrollPastEnd: this.refs.previewScrollPastEnd.checked,
scrollSync: this.refs.previewScrollSync.checked,
smartQuotes: this.refs.previewSmartQuotes.checked, smartQuotes: this.refs.previewSmartQuotes.checked,
breaks: this.refs.previewBreaks.checked, breaks: this.refs.previewBreaks.checked,
smartArrows: this.refs.previewSmartArrows.checked, smartArrows: this.refs.previewSmartArrows.checked,
sanitize: this.refs.previewSanitize.value, sanitize: this.refs.previewSanitize.value,
allowCustomCSS: this.refs.previewAllowCustomCSS.checked, allowCustomCSS: this.refs.previewAllowCustomCSS.checked,
lineThroughCheckbox: this.refs.lineThroughCheckbox.checked,
customCSS: this.customCSSCM.getCodeMirror().getValue() customCSS: this.customCSSCM.getCodeMirror().getValue()
} }
} }
@@ -187,6 +194,7 @@ class UiTab extends React.Component {
<option value='white'>{i18n.__('White')}</option> <option value='white'>{i18n.__('White')}</option>
<option value='solarized-dark'>{i18n.__('Solarized Dark')}</option> <option value='solarized-dark'>{i18n.__('Solarized Dark')}</option>
<option value='monokai'>{i18n.__('Monokai')}</option> <option value='monokai'>{i18n.__('Monokai')}</option>
<option value='dracula'>{i18n.__('Dracula')}</option>
<option value='dark'>{i18n.__('Dark')}</option> <option value='dark'>{i18n.__('Dark')}</option>
</select> </select>
</div> </div>
@@ -244,16 +252,6 @@ class UiTab extends React.Component {
{i18n.__('Show a confirmation dialog when deleting notes')} {i18n.__('Show a confirmation dialog when deleting notes')}
</label> </label>
</div> </div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.showOnlyRelatedTags}
ref='showOnlyRelatedTags'
type='checkbox'
/>&nbsp;
{i18n.__('Show only related tags')}
</label>
</div>
{ {
global.process.platform === 'win32' global.process.platform === 'win32'
? <div styleName='group-checkBoxSection'> ? <div styleName='group-checkBoxSection'>
@@ -269,6 +267,52 @@ class UiTab extends React.Component {
</div> </div>
: null : null
} }
<div styleName='group-header2'>Tags</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.saveTagsAlphabetically}
ref='saveTagsAlphabetically'
type='checkbox'
/>&nbsp;
{i18n.__('Save tags of a note in alphabetical order')}
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.showTagsAlphabetically}
ref='showTagsAlphabetically'
type='checkbox'
/>&nbsp;
{i18n.__('Show tags of a note in alphabetical order')}
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.showOnlyRelatedTags}
ref='showOnlyRelatedTags'
type='checkbox'
/>&nbsp;
{i18n.__('Show only related tags')}
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.ui.enableLiveNoteCounts}
ref='enableLiveNoteCounts'
type='checkbox'
/>&nbsp;
{i18n.__('Enable live count of notes')}
</label>
</div>
<div styleName='group-header2'>Editor</div> <div styleName='group-header2'>Editor</div>
<div styleName='group-section'> <div styleName='group-section'>
@@ -426,6 +470,31 @@ class UiTab extends React.Component {
</div> </div>
</div> </div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Front matter title field')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
ref='frontMatterTitleField'
value={config.editor.frontMatterTitleField}
onChange={(e) => this.handleUIChange(e)}
type='text'
/>
</div>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.editor.enableFrontMatterTitle}
ref='enableFrontMatterTitle'
type='checkbox'
/>&nbsp;
{i18n.__('Extract title from front matter')}
</label>
</div>
<div styleName='group-checkBoxSection'> <div styleName='group-checkBoxSection'>
<label> <label>
<input onChange={(e) => this.handleUIChange(e)} <input onChange={(e) => this.handleUIChange(e)}
@@ -512,6 +581,16 @@ class UiTab extends React.Component {
</select> </select>
</div> </div>
</div> </div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.preview.lineThroughCheckbox}
ref='lineThroughCheckbox'
type='checkbox'
/>&nbsp;
{i18n.__('Allow line through checkbox')}
</label>
</div>
<div styleName='group-checkBoxSection'> <div styleName='group-checkBoxSection'>
<label> <label>
<input onChange={(e) => this.handleUIChange(e)} <input onChange={(e) => this.handleUIChange(e)}
@@ -522,6 +601,16 @@ class UiTab extends React.Component {
{i18n.__('Allow preview to scroll past the last line')} {i18n.__('Allow preview to scroll past the last line')}
</label> </label>
</div> </div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.preview.scrollSync}
ref='previewScrollSync'
type='checkbox'
/>&nbsp;
{i18n.__('When scrolling, synchronize preview with editor')}
</label>
</div>
<div styleName='group-checkBoxSection'> <div styleName='group-checkBoxSection'>
<label> <label>
<input onChange={(e) => this.handleUIChange(e)} <input onChange={(e) => this.handleUIChange(e)}

View File

@@ -113,7 +113,6 @@ function data (state = defaultDataMap(), action) {
// If storage chanced, origin key must be discarded // If storage chanced, origin key must be discarded
if (originKey !== uniqueKey) { if (originKey !== uniqueKey) {
console.log('diffrent storage')
// From isStarred // From isStarred
if (originNote.isStarred) { if (originNote.isStarred) {
state.starredSet = new Set(state.starredSet) state.starredSet = new Set(state.starredSet)

View File

@@ -16,7 +16,7 @@
margin 0 margin 0
border-radius 4px border-radius 4px
margin .2em 0 0 margin .5rem 0 0
background-color $ui-noteList-backgroundColor background-color $ui-noteList-backgroundColor
border 1px solid rgba(0,0,0,.3) border 1px solid rgba(0,0,0,.3)
box-shadow .05em .2em .6em rgba(0,0,0,.2) box-shadow .05em .2em .6em rgba(0,0,0,.2)
@@ -83,6 +83,23 @@ body[data-theme="monokai"]
background-color $ui-monokai-button-backgroundColor background-color $ui-monokai-button-backgroundColor
color $ui-monokai-text-color color $ui-monokai-text-color
body[data-theme="dracula"]
.TagSelect
.react-autosuggest__input
color $ui-dracula-text-color
ul
border-color $ui-dracula-borderColor
background-color $ui-dracula-noteList-backgroundColor
color $ui-dracula-text-color
&:before
background-color $ui-dark-noteList-backgroundColor
li[aria-selected="true"]
background-color $ui-dracula-button-backgroundColor
color $ui-dracula-text-color
body[data-theme="solarized-dark"] body[data-theme="solarized-dark"]
.TagSelect .TagSelect
.react-autosuggest__input .react-autosuggest__input

View File

@@ -128,6 +128,16 @@ colorMonokaiPrimaryButton()
&:active:hover &:active:hover
background-color $dark-primary-button-background--active background-color $dark-primary-button-background--active
colorDraculaPrimaryButton()
color $ui-dracula-text-color
background-color $ui-dracula-button-backgroundColor
border none
&:hover
background-color $ui-dracula-button--active-backgroundColor
&:active
&:active:hover
background-color $ui-dracula-button--active-backgroundColor
// Danger button(Brand color) // Danger button(Brand color)
$danger-button-background = #c9302c $danger-button-background = #c9302c
@@ -384,3 +394,29 @@ modalMonokai()
background-color $ui-monokai-backgroundColor background-color $ui-monokai-backgroundColor
overflow hidden overflow hidden
border-radius $modal-border-radius border-radius $modal-border-radius
/******* Dracula theme ********/
$ui-dracula-backgroundColor = #282a36
$ui-dracula-noteList-backgroundColor = #282a36
$ui-dracula-noteDetail-backgroundColor = #282a36
$ui-dracula-text-color = #f8f8f2
$ui-dracula-active-color = #bd93f9
$ui-dracula-borderColor = #44475a
$ui-dracula-tag-backgroundColor = #8be9fd
$ui-dracula-button-backgroundColor = #44475a
$ui-dracula-button--active-color = #f8f8f2
$ui-dracula-button--active-backgroundColor = #bd93f9
$ui-dracula-button--hover-backgroundColor = lighten($ui-dracula-backgroundColor, 10%)
$ui-dracula-button--focus-borderColor = lighten(#44475a, 25%)
modalDracula()
position relative
z-index $modal-z-index
width 100%
background-color $ui-dracula-backgroundColor
overflow hidden
border-radius $modal-border-radius

View File

@@ -49,7 +49,7 @@ function startServer () {
} }
function startElectron () { function startElectron () {
spawn(electron, ['--hot', './index.js']) spawn(electron, ['--hot', './index.js'], { stdio: 'inherit' })
.on('close', () => { .on('close', () => {
server.close() server.close()
}) })

View File

@@ -52,7 +52,10 @@
if (className.indexOf('cm-url') !== -1) { if (className.indexOf('cm-url') !== -1) {
const match = /^\((.*)\)|\[(.*)\]|(.*)$/.exec(el.textContent) const match = /^\((.*)\)|\[(.*)\]|(.*)$/.exec(el.textContent)
return match[1] || match[2] || match[3] const url = match[1] || match[2] || match[3]
// `:storage` is the value of the variable `STORAGE_FOLDER_PLACEHOLDER` defined in `browser/main/lib/dataApi/attachmentManagement`
return /^:storage(?:\/|%5C)/.test(url) ? null : url
} }
return null return null

View File

@@ -0,0 +1,45 @@
.cm-table-row-even { background-color: rgb(242, 242, 242); }
.cm-s-3024-day.CodeMirror .cm-table-row-even { background-color: rgb(238, 237, 237); }
.cm-s-3024-night.CodeMirror .cm-table-row-even { background-color: rgb(30, 24, 21); }
.cm-s-abcdef.CodeMirror .cm-table-row-even { background-color: rgb(36, 39, 37); }
.cm-s-ambiance.CodeMirror .cm-table-row-even { background-color: rgb(242, 242, 242); }
.cm-s-base16-dark.CodeMirror .cm-table-row-even { background-color: rgb(41, 41, 41); }
.cm-s-base16-light.CodeMirror .cm-table-row-even { background-color: rgb(234, 234, 234); }
.cm-s-bespin.CodeMirror .cm-table-row-even { background-color: rgb(52, 45, 40); }
.cm-s-blackboard.CodeMirror .cm-table-row-even { background-color: rgb(36, 39, 55); }
.cm-s-cobalt.CodeMirror .cm-table-row-even { background-color: rgb(26, 56, 83); }
.cm-s-colorforth.CodeMirror .cm-table-row-even { background-color: rgb(25, 25, 25); }
.cm-s-darcula.CodeMirror .cm-table-row-even { background-color: rgb(56, 57, 59); }
.cm-s-dracula.CodeMirror .cm-table-row-even { background-color: rgb(61, 63, 73); }
.cm-s-duotone-dark.CodeMirror .cm-table-row-even { background-color: rgb(49, 45, 60); }
.cm-s-duotone-light.CodeMirror .cm-table-row-even { background-color: rgb(246, 243, 238); }
.cm-s-erlang-dark.CodeMirror .cm-table-row-even { background-color: rgb(26, 56, 83); }
.cm-s-gruvbox-dark.CodeMirror .cm-table-row-even { background-color: rgb(55, 53, 51); }
.cm-s-hopscotch.CodeMirror .cm-table-row-even { background-color: rgb(66, 58, 65); }
.cm-s-isotope.CodeMirror .cm-table-row-even { background-color: rgb(22, 22, 22); }
.cm-s-lesser-dark.CodeMirror .cm-table-row-even { background-color: rgb(58, 58, 57); }
.cm-s-liquibyte.CodeMirror .cm-table-row-even { background-color: rgb(26, 26, 26); }
.cm-s-lucario.CodeMirror .cm-table-row-even { background-color: rgb(64, 81, 96); }
.cm-s-material.CodeMirror .cm-table-row-even { background-color: rgb(58, 69, 74); }
.cm-s-mbo.CodeMirror .cm-table-row-even { background-color: rgb(65, 65, 63); }
.cm-s-midnight.CodeMirror .cm-table-row-even { background-color: rgb(34, 46, 63); }
.cm-s-monokai.CodeMirror .cm-table-row-even { background-color: rgb(60, 61, 55); }
.cm-s-neo.CodeMirror .cm-table-row-even { background-color: rgb(245, 245, 245); }
.cm-s-night.CodeMirror .cm-table-row-even { background-color: rgb(34, 25, 53); }
.cm-s-oceanic-next.CodeMirror .cm-table-row-even { background-color: rgb(68, 83, 89); }
.cm-s-paraiso-dark.CodeMirror .cm-table-row-even { background-color: rgb(61, 45, 59); }
.cm-s-paraiso-light.CodeMirror .cm-table-row-even { background-color: rgb(223, 224, 211); }
.cm-s-pastel-on-dark.CodeMirror .cm-table-row-even { background-color: rgb(54, 51, 49); }
.cm-s-railscasts.CodeMirror .cm-table-row-even { background-color: rgb(63, 63, 62); }
.cm-s-rubyblue.CodeMirror .cm-table-row-even { background-color: rgb(41, 58, 73); }
.cm-s-seti.CodeMirror .cm-table-row-even { background-color: rgb(40, 42, 43); }
.cm-s-shadowfox.CodeMirror .cm-table-row-even { background-color: rgb(56, 56, 59); }
.cm-s-the-matrix.CodeMirror .cm-table-row-even { background-color: rgb(0, 26, 0); }
.cm-s-tomorrow-night-bright.CodeMirror .cm-table-row-even { background-color: rgb(23, 23, 23); }
.cm-s-tomorrow-night-eighties.CodeMirror .cm-table-row-even { background-color: rgb(20, 20, 20); }
.cm-s-twilight.CodeMirror .cm-table-row-even { background-color: rgb(43, 43, 43); }
.cm-s-vibrant-ink.CodeMirror .cm-table-row-even { background-color: rgb(26, 26, 26); }
.cm-s-xq-dark.CodeMirror .cm-table-row-even { background-color: rgb(34, 25, 53); }
.cm-s-yeti.CodeMirror .cm-table-row-even { background-color: rgb(235, 232, 230); }
.cm-s-solarized.cm-s-dark .cm-table-row-even { background-color: rgb(13, 54, 64); }
.cm-s-solarized.cm-s-light .cm-table-row-even { background-color: rgb(245, 240, 222); }

View File

@@ -0,0 +1,74 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../codemirror/lib/codemirror"), require("../codemirror/mode/gfm/gfm"))
else if (typeof define == "function" && define.amd) // AMD
define(["../codemirror/lib/codemirror", "../codemirror/mode/gfm/gfm"], mod)
else // Plain browser env
mod(CodeMirror)
})(function(CodeMirror) {
'use strict'
CodeMirror.defineMode('bfm', function(config, gfmConfig) {
const bfmOverlay = {
startState() {
return {
inTable: false,
rowIndex: 0
}
},
copyState(s) {
return {
inTable: s.inTable,
rowIndex: s.rowIndex
}
},
token(stream, state) {
state.combineTokens = true
if (state.inTable) {
if (stream.match(/^\|/)) {
++state.rowIndex
stream.skipToEnd()
if (state.rowIndex === 1) {
return 'table table-separator'
} else if (state.rowIndex % 2 === 0) {
return 'table table-row table-row-even'
} else {
return 'table table-row table-row-odd'
}
} else {
state.inTable = false
stream.skipToEnd()
return null
}
} else if (stream.match(/^\|/)) {
state.inTable = true
state.rowIndex = 0
stream.skipToEnd()
return 'table table-header'
}
stream.skipToEnd()
return null
},
blankLine(state) {
state.inTable = false
}
}
gfmConfig.name = 'gfm'
return CodeMirror.overlayMode(CodeMirror.getMode(config, gfmConfig), bfmOverlay)
})
CodeMirror.defineMIME('text/x-bfm', 'bfm')
CodeMirror.modeInfo.push({
name: "Boost Flavored Markdown",
mime: "text/x-bfm",
mode: "bfm"
})
})

View File

@@ -1,3 +1,4 @@
const fs = require('fs')
const path = require('path') const path = require('path')
const ChildProcess = require('child_process') const ChildProcess = require('child_process')
const packager = require('electron-packager') const packager = require('electron-packager')
@@ -284,5 +285,61 @@ module.exports = function (grunt) {
} }
}) })
grunt.registerTask('bfm', function () {
const Color = require('color')
const parseCSS = require('css').parse
function generateRule (selector, bgColor, fgColor) {
if (bgColor.isLight()) {
bgColor = bgColor.mix(fgColor, 0.05)
} else {
bgColor = bgColor.mix(fgColor, 0.1)
}
if (selector && selector.length > 0) {
return `${selector} .cm-table-row-even { background-color: ${bgColor.rgb().string()}; }`
} else {
return `.cm-table-row-even { background-color: ${bgColor.rgb().string()}; }`
}
}
const root = path.join(__dirname, 'node_modules/codemirror/theme/')
const colors = fs.readdirSync(root).filter(file => file !== 'solarized.css').map(file => {
const css = parseCSS(fs.readFileSync(path.join(root, file), 'utf8'))
const rules = css.stylesheet.rules.filter(rule => rule.selectors && /\b\.CodeMirror$/.test(rule.selectors[0]))
if (rules.length === 1) {
let bgColor = Color('white')
let fgColor = Color('black')
rules[0].declarations.forEach(declaration => {
if (declaration.property === 'background-color' || declaration.property === 'background') {
bgColor = Color(declaration.value.split(' ')[0])
} else if (declaration.property === 'color') {
const value = /^(.*?)(?:\s*!important)?$/.exec(declaration.value)[1]
const match = /^rgba\((.*?),\s*1\)$/.exec(value)
if (match) {
fgColor = Color(`rgb(${match[1]})`)
} else {
fgColor = Color(value)
}
}
})
return generateRule(rules[0].selectors[0], bgColor, fgColor)
}
}).filter(value => !!value)
// default
colors.unshift(generateRule(null, Color('white'), Color('black')))
// solarized dark
colors.push(generateRule('.cm-s-solarized.cm-s-dark', Color('#002b36'), Color('#839496')))
// solarized light
colors.push(generateRule('.cm-s-solarized.cm-s-light', Color('#fdf6e3'), Color('#657b83')))
fs.writeFileSync(path.join(__dirname, 'extra_scripts/codemirror/mode/bfm/bfm.css'), colors.join('\n'), 'utf8')
})
grunt.registerTask('default', ['build']) grunt.registerTask('default', ['build'])
} }

View File

@@ -59,7 +59,7 @@ updater.on('update-downloaded', (info) => {
}) })
updater.autoUpdater.on('error', (err) => { updater.autoUpdater.on('error', (err) => {
console.log(err) console.error(err)
}) })
ipc.on('update-app-confirm', function (event, msg) { ipc.on('update-app-confirm', function (event, msg) {

View File

@@ -7,13 +7,19 @@ const config = new Config()
const _ = require('lodash') const _ = require('lodash')
var showMenu = process.platform !== 'win32' var showMenu = process.platform !== 'win32'
const windowSize = config.get('windowsize') || { x: null, y: null, width: 1080, height: 720 } const windowSize = config.get('windowsize') || {
x: null,
y: null,
width: 1080,
height: 720
}
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
x: windowSize.x, x: windowSize.x,
y: windowSize.y, y: windowSize.y,
width: windowSize.width, width: windowSize.width,
height: windowSize.height, height: windowSize.height,
useContentSize: true,
minWidth: 500, minWidth: 500,
minHeight: 320, minHeight: 320,
autoHideMenuBar: showMenu, autoHideMenuBar: showMenu,

View File

@@ -10,6 +10,7 @@
<link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css"> <link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="../node_modules/katex/dist/katex.min.css"> <link rel="stylesheet" href="../node_modules/katex/dist/katex.min.css">
<link rel="stylesheet" href="../node_modules/codemirror/addon/dialog/dialog.css"> <link rel="stylesheet" href="../node_modules/codemirror/addon/dialog/dialog.css">
<link rel="stylesheet" href="../extra_scripts/codemirror/mode/bfm/bfm.css">
<title>Boostnote</title> <title>Boostnote</title>
@@ -94,9 +95,13 @@
<script src="../node_modules/codemirror/keymap/vim.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/keymap/emacs.js"></script>
<script src="../node_modules/codemirror/addon/runmode/runmode.js"></script> <script src="../node_modules/codemirror/addon/runmode/runmode.js"></script>
<script src="../node_modules/codemirror/mode/xml/xml.js"></script>
<script src="../node_modules/codemirror/mode/markdown/markdown.js"></script>
<script src="../node_modules/codemirror/mode/gfm/gfm.js"></script>
<script src="../extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js"></script> <script src="../extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js"></script>
<script src="../extra_scripts/codemirror/addon/hyperlink/hyperlink.js"></script> <script src="../extra_scripts/codemirror/addon/hyperlink/hyperlink.js"></script>
<script src="../extra_scripts/codemirror/mode/bfm/bfm.js"></script>
<script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script> <script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
<script src="../node_modules/codemirror/addon/edit/matchbrackets.js"></script> <script src="../node_modules/codemirror/addon/edit/matchbrackets.js"></script>

View File

@@ -145,6 +145,7 @@
"UserName": "Benutzername", "UserName": "Benutzername",
"Password": "Passwort", "Password": "Passwort",
"Russian": "Russisch", "Russian": "Russisch",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Befehlstaste(⌘)", "Command(⌘)": "Befehlstaste(⌘)",
"Editor Rulers": "Editor Trennline", "Editor Rulers": "Editor Trennline",
"Enable": "Aktiviert", "Enable": "Aktiviert",

View File

@@ -136,6 +136,7 @@
"Hotkeys": "Hotkeys", "Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote", "Show/Hide Boostnote": "Show/Hide Boostnote",
"Toggle Editor Mode": "Toggle Editor Mode", "Toggle Editor Mode": "Toggle Editor Mode",
"Delete Note": "Delete Note",
"Restore": "Restore", "Restore": "Restore",
"Permanent Delete": "Permanent Delete", "Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion", "Confirm note deletion": "Confirm note deletion",
@@ -156,6 +157,7 @@
"Password": "Password", "Password": "Password",
"Russian": "Russian", "Russian": "Russian",
"Hungarian": "Hungarian", "Hungarian": "Hungarian",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Command(⌘)", "Command(⌘)": "Command(⌘)",
"Add Storage": "Add Storage", "Add Storage": "Add Storage",
"Name": "Name", "Name": "Name",
@@ -177,6 +179,9 @@
"Allow dangerous html tags": "Allow dangerous html tags", "Allow dangerous html tags": "Allow dangerous html tags",
"Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.", "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.",
"⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠",
"Save tags of a note in alphabetical order": "Save tags of a note in alphabetical order",
"Show tags of a note in alphabetical order": "Show tags of a note in alphabetical order",
"Enable live count of notes": "Enable live count of notes",
"Enable smart table editor": "Enable smart table editor", "Enable smart table editor": "Enable smart table editor",
"Snippet Default Language": "Snippet Default Language" "Snippet Default Language": "Snippet Default Language"
} }

View File

@@ -143,6 +143,7 @@
"Spanish": "Español", "Spanish": "Español",
"Unsaved Changes!": "¡Tienes que guardar!", "Unsaved Changes!": "¡Tienes que guardar!",
"Russian": "Ruso", "Russian": "Ruso",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Comando(⌘)", "Command(⌘)": "Comando(⌘)",
"Editor Rulers": "Reglas del editor", "Editor Rulers": "Reglas del editor",
"Enable": "Activar", "Enable": "Activar",

View File

@@ -146,6 +146,7 @@
"UserName": "نام کاربری", "UserName": "نام کاربری",
"Password": "رمز عبور", "Password": "رمز عبور",
"Russian": "روسی", "Russian": "روسی",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Command(⌘)", "Command(⌘)": "Command(⌘)",
"Editor Rulers": "Editor Rulers", "Editor Rulers": "Editor Rulers",
"Enable": "فعال", "Enable": "فعال",

View File

@@ -146,6 +146,7 @@
"Spanish": "Espagnol", "Spanish": "Espagnol",
"Unsaved Changes!": "Il faut sauvegarder !", "Unsaved Changes!": "Il faut sauvegarder !",
"Russian": "Russe", "Russian": "Russe",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Command(⌘)", "Command(⌘)": "Command(⌘)",
"Editor Rulers": "Règles dans l'éditeur", "Editor Rulers": "Règles dans l'éditeur",
"Enable": "Activer", "Enable": "Activer",
@@ -157,10 +158,14 @@
"Allow dangerous html tags": "Accepter les tags html dangereux", "Allow dangerous html tags": "Accepter les tags html dangereux",
"Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convertir des flèches textuelles en jolis signes. ⚠ Cela va interferérer avec les éventuels commentaires HTML dans votre Markdown.", "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convertir des flèches textuelles en jolis signes. ⚠ Cela va interferérer avec les éventuels commentaires HTML dans votre Markdown.",
"⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ Vous avez collé un lien qui référence une pièce-jointe qui n'a pas pu être récupéré dans le dossier de stockage de la note. Coller des liens qui font référence à des pièces-jointes ne fonctionne que si la source et la destination et la même. Veuillez plutôt utiliser du Drag & Drop ! ⚠", "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ Vous avez collé un lien qui référence une pièce-jointe qui n'a pas pu être récupéré dans le dossier de stockage de la note. Coller des liens qui font référence à des pièces-jointes ne fonctionne que si la source et la destination et la même. Veuillez plutôt utiliser du Drag & Drop ! ⚠",
"Save tags of a note in alphabetical order": "Sauvegarder les tags d'une note en ordre alphabétique",
"Show tags of a note in alphabetical order": "Afficher les tags d'une note par ordre alphabétique",
"Enable live count of notes": "Activer le comptage live des notes",
"Enable smart table editor": "Activer l'intelligent éditeur de tableaux", "Enable smart table editor": "Activer l'intelligent éditeur de tableaux",
"Snippet Default Language": "Langage par défaut d'un snippet", "Snippet Default Language": "Langage par défaut d'un snippet",
"New Snippet": "Nouveau snippet", "New Snippet": "Nouveau snippet",
"Custom CSS": "CSS personnalisé", "Custom CSS": "CSS personnalisé",
"Snippet name": "Nom du snippet", "Snippet name": "Nom du snippet",
"Snippet prefix": "Préfixe du snippet" "Snippet prefix": "Préfixe du snippet"
"Delete Note": "Supprimer la note"
} }

View File

@@ -155,6 +155,7 @@
"Password": "Jelszo", "Password": "Jelszo",
"Russian": "Russian", "Russian": "Russian",
"Hungarian": "Hungarian", "Hungarian": "Hungarian",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Command(⌘)", "Command(⌘)": "Command(⌘)",
"Add Storage": "Tároló hozzáadása", "Add Storage": "Tároló hozzáadása",
"Name": "Név", "Name": "Név",

View File

@@ -146,6 +146,7 @@
"UserName": "UserName", "UserName": "UserName",
"Password": "Password", "Password": "Password",
"Russian": "Russo", "Russian": "Russo",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Comando(⌘)", "Command(⌘)": "Comando(⌘)",
"Editor Rulers": "Regole dell'editor", "Editor Rulers": "Regole dell'editor",
"Enable": "Abilita", "Enable": "Abilita",

View File

@@ -155,6 +155,7 @@
"Password": "パスワード", "Password": "パスワード",
"Russian": "ロシア語", "Russian": "ロシア語",
"Hungarian": "ハンガリー語", "Hungarian": "ハンガリー語",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "コマンド(⌘)", "Command(⌘)": "コマンド(⌘)",
"Add Storage": "ストレージを追加", "Add Storage": "ストレージを追加",
"Name": "名前", "Name": "名前",

View File

@@ -143,6 +143,7 @@
"Spanish": "Spanish", "Spanish": "Spanish",
"Unsaved Changes!": "저장해주세요!", "Unsaved Changes!": "저장해주세요!",
"Russian": "Russian", "Russian": "Russian",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Command(⌘)", "Command(⌘)": "Command(⌘)",
"Delete Folder": "폴더 삭제", "Delete Folder": "폴더 삭제",
"This will delete all notes in the folder and can not be undone.": "폴더의 모든 노트를 지우게 되고, 되돌릴 수 없습니다.", "This will delete all notes in the folder and can not be undone.": "폴더의 모든 노트를 지우게 되고, 되돌릴 수 없습니다.",

View File

@@ -143,6 +143,7 @@
"Spanish": "Spanish", "Spanish": "Spanish",
"Unsaved Changes!": "Unsaved Changes!", "Unsaved Changes!": "Unsaved Changes!",
"Russian": "Russian", "Russian": "Russian",
"Thai": "Thai (ภาษาไทย)",
"Editor Rulers": "Editor Rulers", "Editor Rulers": "Editor Rulers",
"Enable": "Enable", "Enable": "Enable",
"Disable": "Disable", "Disable": "Disable",

View File

@@ -149,6 +149,7 @@
"Spanish": "Hiszpański", "Spanish": "Hiszpański",
"Unsaved Changes!": "Musisz zapisać!", "Unsaved Changes!": "Musisz zapisać!",
"Russian": "Rosyjski", "Russian": "Rosyjski",
"Thai": "Thai (ภาษาไทย)",
"Editor Rulers": "Margines", "Editor Rulers": "Margines",
"Enable": "Włącz", "Enable": "Włącz",
"Disable": "Wyłącz", "Disable": "Wyłącz",

View File

@@ -143,6 +143,7 @@
"Spanish": "Espanhol", "Spanish": "Espanhol",
"Unsaved Changes!": "Você precisa salvar!", "Unsaved Changes!": "Você precisa salvar!",
"Russian": "Russo", "Russian": "Russo",
"Thai": "Thai (ภาษาไทย)",
"Editor Rulers": "Réguas do Editor", "Editor Rulers": "Réguas do Editor",
"Enable": "Habilitado", "Enable": "Habilitado",
"Disable": "Desabilitado", "Disable": "Desabilitado",

View File

@@ -143,6 +143,7 @@
"Spanish": "Spanish", "Spanish": "Spanish",
"Unsaved Changes!": "Unsaved Changes!", "Unsaved Changes!": "Unsaved Changes!",
"Russian": "Russian", "Russian": "Russian",
"Thai": "Thai (ภาษาไทย)",
"Editor Rulers": "Editor Rulers", "Editor Rulers": "Editor Rulers",
"Enable": "Enable", "Enable": "Enable",
"Disable": "Disable", "Disable": "Disable",

View File

@@ -144,6 +144,7 @@
"UserName": "Имя пользователя", "UserName": "Имя пользователя",
"Password": "Пароль", "Password": "Пароль",
"Russian": "Русский", "Russian": "Русский",
"Thai": "Thai (ภาษาไทย)",
"Editor Rulers": "Editor Rulers", "Editor Rulers": "Editor Rulers",
"Enable": "Enable", "Enable": "Enable",
"Disable": "Disable", "Disable": "Disable",

View File

@@ -142,6 +142,7 @@
"Spanish": "Spanish", "Spanish": "Spanish",
"Unsaved Changes!": "Unsaved Changes!", "Unsaved Changes!": "Unsaved Changes!",
"Russian": "Russian", "Russian": "Russian",
"Thai": "Thai (ภาษาไทย)",
"Editor Rulers": "Editor Rulers", "Editor Rulers": "Editor Rulers",
"Enable": "Enable", "Enable": "Enable",
"Disable": "Disable", "Disable": "Disable",

182
locales/th.json Normal file
View File

@@ -0,0 +1,182 @@
{
"Notes": "โน๊ต",
"Tags": "แท็ก",
"Preferences": "ตั้งค่า",
"Make a note": "สร้างโน๊ต",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl(^)",
"to create a new note": "เพื่อสร้างโน๊ต",
"Toggle Mode": "Toggle Mode",
"Add tag...": "เพิ่มแท็ก...",
"Trash": "ถังขยะ",
"MODIFICATION DATE": "แก้ไขเมื่อ",
"Words": "คำ",
"Letters": "ตัวอักษร",
"STORAGE": "แหล่งจัดเก็บ",
"FOLDER": "โฟลเดอร์",
"CREATION DATE": "สร้างเมื่อ",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "พิมพ์",
"Your preferences for Boostnote": "การตั้งค่าของคุณสำหรับ Boostnote",
"Help": "ช่วยเหลือ",
"Hide Help": "ซ่อนการช่วยเหลือ",
"Storages": "แหล่งจัดเก็บ",
"Add Storage Location": "เพิ่มแหล่งจัดเก็บ",
"Add Folder": "เพิ่มโฟลเดอร์",
"Select Folder": "เลือกโฟลเดอร์",
"Open Storage folder": "เปิดโฟลเดอร์แหล่งจัดเก็บ",
"Unlink": "ยกเลิกการลิงค์",
"Edit": "แก้ไข",
"Delete": "ลบ",
"Interface": "หน้าตาโปรแกรม",
"Interface Theme": "ธีมของโปรแกรม",
"Default": "ค่าเริ่มต้น",
"White": "โทนสว่าง",
"Solarized Dark": "Solarized Dark",
"Dark": "โทนมืด",
"Show a confirmation dialog when deleting notes": "แสดงหน้าต่างยืนยันเมื่อทำการลบโน๊ต",
"Disable Direct Write (It will be applied after restarting)": "ปิด Direct Write (It will be applied after restarting)",
"Show only related tags": "แสดงเฉพาะแท็กที่เกี่ยวข้อง",
"Editor Theme": "ธีมของ Editor",
"Editor Font Size": "ขนาดอักษรของ Editor",
"Editor Font Family": "แบบอักษรของ Editor",
"Editor Indent Style": "รูปแบบการย่อหน้าของ Editor",
"Spaces": "ช่องว่าง",
"Tabs": "แท็บ",
"Switch to Preview": "Switch to Preview",
"When Editor Blurred": "When Editor Blurred",
"When Editor Blurred, Edit On Double Click": "When Editor Blurred, Edit On Double Click",
"On Right Click": "On Right Click",
"Editor Keymap": "รูปแบบคีย์ลัดของ Editor",
"default": "ค่าเริ่มต้น",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ กรุณาปิดและเปิดโปรแกรมใหม่ หลังจากคุณเปลี่ยนคีย์ลัด",
"Show line numbers in the editor": "แสดงหมายเลขบรรทัด",
"Allow editor to scroll past the last line": "อนุญาตให้เลื่อน Scroll เลยบรรทัดสุดท้ายได้",
"Enable smart quotes": "เปิด Smart quotes",
"Bring in web page title when pasting URL on editor": "แสดงชื่อ Title ของเว็บไซต์เมื่อวางลิงค์ใน Editor",
"Preview": "พรีวิว",
"Preview Font Size": "ขนาดอักษร",
"Preview Font Family": "แบบอักษร",
"Code block Theme": "ธีมของ Code block",
"Allow preview to scroll past the last line": "อนุญาตให้เลื่อน Scroll เลยบรรทัดสุดท้ายได้",
"Show line numbers for preview code blocks": "แสดงหมายเลขบรรทัดใน Code block",
"LaTeX Inline Open Delimiter": "LaTeX Inline Open Delimiter",
"LaTeX Inline Close Delimiter": "LaTeX Inline Close Delimiter",
"LaTeX Block Open Delimiter": "LaTeX Block Open Delimiter",
"LaTeX Block Close Delimiter": "LaTeX Block Close Delimiter",
"PlantUML Server": "เซิฟเวอร์ของ PlantUML",
"Community": "ชุมชนผู้ใช้",
"Subscribe to Newsletter": "สมัครรับข่าวสาร",
"GitHub": "GitHub",
"Blog": "บล็อก",
"Facebook Group": "กลุ่ม Facebook",
"Twitter": "Twitter",
"About": "เกี่ยวกับ",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "เป็นแอพพลิเคชันจดบันทึก ที่ออกแบบมาเพื่อโปรแกรมเมอร์อย่างคุณ.",
"Website": "เว็บไซต์",
"Development": "การพัฒนา",
" : Development configurations for Boostnote.": " : การตั้งค่าต่างๆสำหรับการพัฒนา Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "สงวนลิขสิทธิ์ (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "การวิเคราะห์",
"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.": "Boostnote จะเก็บข้อมูลแบบไม่ระบุตัวตนเพื่อนำไปใช้ในการปรับปรุงแอพพลิเคชันเท่านั้น, และจะไม่มีการเก็บข้อมูลส่วนตัวใดๆของคุณ เช่น ข้อมูลในโน๊ตของคุณอย่างเด็ดขาด.",
"You can see how it works on ": "คุณสามารถดูรายละเอียดเพิ่มเติมได้ที่ ",
"You can choose to enable or disable this option.": "คุณสามารถเลือกที่จะเปิดหรือปิดตัวเลือกนี้ได้.",
"Enable analytics to help improve Boostnote": "เปิดการวิเคราะห์ สำหรับการนำไปปรับปรุงพัฒนา Boostnote",
"Crowdfunding": "การระดมทุนสาธารณะ",
"Dear everyone,": "สวัสดีทุกคน,",
"Thank you for using Boostnote!": "ขอขอบคุณที่เลือกใช้ Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "มีการใช้งาน Boostnote จากสังคมผู้ใช้ที่เป็น Developer มากกว่า 200 ประเทศทั่วโลกจากหลากหลายภูมิภาค.",
"To continue supporting this growth, and to satisfy community expectations,": "เพื่อให้เกิดการสนับสนุนให้เกิดการเติบโตอย่างต่อเนื่อง, และเพื่อพัฒนาให้ตรงตามความต้องการของชุมชนผู้ใช้,",
"we would like to invest more time and resources in this project.": "เราต้องใช้เวลา และการลงทุนด้านทรัพยากรสำหรับโครงการนี้.",
"If you like this project and see its potential, you can help by supporting us on OpenCollective!": "ถ้าคุณชอบและมองเห็นความเป็นไปได้ในอนาคต, คุณสามารถช่วยเหลือด้วยการสนับสนุนเราผ่าน OpenCollective!",
"Thanks,": "ขอขอบคุณ,",
"Boostnote maintainers": "กลุ่มผู้พัฒนา Boostnote",
"Support via OpenCollective": "สนับสนุนผ่าน OpenCollective",
"Language": "ภาษา",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "แสดงการแจ้งเตือน \"บันทึกไปยังคลิปบอร์ด\" เมื่อทำการคัดลอก",
"All Notes": "โน๊ตทั้งหมด",
"Starred": "รายการโปรด",
"Are you sure to ": "คุณแน่ใจหรือไม่ที่จะ ",
" delete": " ลบ",
"this folder?": "โฟลเดอร์นี้?",
"Confirm": "ยืนยัน",
"Cancel": "ยกเลิก",
"Markdown Note": "โน๊ต Markdown",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "รูปแบบนี้ใช้สำหรับสร้างเอกสารทั่วไป. รองรับการเขียนเช็คลิสต์, แทรกโค้ด และการเขียนโดยใช้ Latex.",
"Snippet Note": "โน๊ต Snippet",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "รูปแบบนี้ใช้สำหรับสร้าง Code snippets. สามารถรวมหลาย Snippets เป็นโน๊ตเดียวกันได้.",
"Tab to switch format": "กด Tab เพื่อเปลี่ยนรูปแบบที่เลือก",
"Updated": "เรียงตามอัพเดท",
"Created": "เรียงตามเวลาที่สร้างโน๊ต",
"Alphabetically": "เรียงตามอักษร",
"Counter": "Counter",
"Default View": "มุมมองปกติ",
"Compressed View": "มุมมองหนาแน่น",
"Search": "ค้นหา",
"Blog Type": "ประเภทของบล็อก",
"Blog Address": "ที่อยู่ของบล็อก",
"Save": "บันทึก",
"Auth": "การยืนยันตัวตน",
"Authentication Method": "รูปแบบการยืนยันตัวตน",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "แหล่งจัดเก็บ",
"Hotkeys": "คีย์ลัด",
"Show/Hide Boostnote": "แสดง/ซ่อน Boostnote",
"Toggle editor mode": "เปิด/ปิด Editor mode",
"Restore": "กู้คืน",
"Permanent Delete": "ลบถาวร",
"Confirm note deletion": "ยืนยันการลบโน๊ต",
"This will permanently remove this note.": "โน๊ตของคุณจะถูกลบอย่างถาวร.",
"Successfully applied!": "สำเร็จ!",
"Albanian": "Albanian",
"Chinese (zh-CN)": "Chinese (zh-CN)",
"Chinese (zh-TW)": "Chinese (zh-TW)",
"Danish": "Danish",
"Japanese": "Japanese",
"Korean": "Korean",
"Norwegian": "Norwegian",
"Polish": "Polish",
"Portuguese": "Portuguese",
"Spanish": "Spanish",
"You have to save!": "คุณจำเป็นต้องบันทึก!",
"UserName": "UserName",
"Password": "Password",
"Russian": "Russian",
"Hungarian": "Hungarian",
"Thai": "Thai (ภาษาไทย)",
"Command(⌘)": "Command(⌘)",
"Add Storage": "เพิ่มแหล่งจัดเก็บ",
"Name": "ชื่อ",
"Type": "ชนิด",
"File System": "ระบบไฟล์",
"Setting up 3rd-party cloud storage integration:": "ดูวิธีการตั้งค่า หากต้องการใช้งานแบบลิงค์ไฟล์ร่วมกับผู้ให้บริการเก็บข้อมูลบนคลาวด์",
"Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup",
"Location": "ที่อยู่",
"Add": "เพิ่ม",
"Unlink Storage": "ยกเลิกการลิงค์ Storage",
"Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "การยกเลิกการลิงค์ จะเป็นการลบการลิงค์แหล่งจัดเก็บออกไปจาก Boostnote. แต่ไฟล์ข้อมูลจะไม่ถูกลบ, หากต้องการลบข้อมูล กรุณาลบโพลเดอร์ของข้อมูลในเครื่องของท่านด้วยตัวเอง.",
"Editor Rulers": "ไม้บรรทัด Editor",
"Enable": "เปิด",
"Disable": "ปิด",
"Sanitization": "Sanitization",
"Only allow secure html tags (recommended)": "อนุญาตเฉพาะ HTML tag ที่มีความปลอดภัย (แนะนำ)",
"Render newlines in Markdown paragraphs as <br>": "ใช้ <br> แทนอักขระขึ้นบรรทัดใหม่ในข้อความ Markdown",
"Allow styles": "อนุญาตการใช้ styles",
"Allow dangerous html tags": "อนุญาตให้ใช้ html tags ที่ไม่ปลอดภัย",
"Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "แปลงลูกศรจากรูปแบบข้อความให้เป็นสัญลักษณ์. ⚠ สิ่งนี้จะเป็นการแทรกโดยใช้ HTML comment ลงไปใน Markdown ที่คุณเขียน.",
"⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ ไม่พบไฟล์แนบในโน๊ตนี้ จากลิงค์ที่คุณได้วาง. คุณสามารถวางลิงค์ที่อ้างอิงไปยังไฟล์แนบ เฉพาะกรณีที่ต้นทาง และปลายทางที่อ้างถึงนั้นอยู่ใน 'แหล่งจัดเก็บ เดียวกัน. กรุณาใช้การลากและวางเพื่อใส่ไฟล์แนบแทน! ⚠",
"Enable smart table editor": "เปิดการใช้ Smart table editor",
"Snippet Default Language": "ทำการ Snippet ภาษาที่เป็นค่าเริ่มต้น"
}

View File

@@ -1,7 +1,7 @@
{ {
"name": "boost", "name": "boost",
"productName": "Boostnote", "productName": "Boostnote",
"version": "0.11.9", "version": "0.11.10",
"main": "index.js", "main": "index.js",
"description": "Boostnote", "description": "Boostnote",
"license": "GPL-3.0", "license": "GPL-3.0",
@@ -15,7 +15,7 @@
"dev": "node dev-scripts/dev.js" "dev": "node dev-scripts/dev.js"
}, },
"config": { "config": {
"electron-version": "2.0.7" "electron-version": "3.0.3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -53,10 +53,10 @@
"aws-sdk": "^2.48.0", "aws-sdk": "^2.48.0",
"aws-sdk-mobile-analytics": "^0.9.2", "aws-sdk-mobile-analytics": "^0.9.2",
"chart.js": "^2.7.2", "chart.js": "^2.7.2",
"codemirror": "^5.39.0", "codemirror": "^5.40.2",
"codemirror-mode-elixir": "^1.1.1", "codemirror-mode-elixir": "^1.1.1",
"electron-config": "^1.0.0", "electron-config": "^1.0.0",
"electron-gh-releases": "^2.0.2", "electron-gh-releases": "^2.0.4",
"escape-string-regexp": "^1.0.5", "escape-string-regexp": "^1.0.5",
"file-uri-to-path": "^1.0.0", "file-uri-to-path": "^1.0.0",
"file-url": "^2.0.2", "file-url": "^2.0.2",
@@ -101,7 +101,9 @@
"sanitize-html": "^1.18.2", "sanitize-html": "^1.18.2",
"striptags": "^2.2.1", "striptags": "^2.2.1",
"unique-slug": "2.0.0", "unique-slug": "2.0.0",
"uuid": "^3.2.1" "uuid": "^3.2.1",
"turndown":"^4.0.2",
"turndown-plugin-gfm":"^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"ava": "^0.25.0", "ava": "^0.25.0",
@@ -116,12 +118,14 @@
"babel-preset-react-hmre": "^1.0.1", "babel-preset-react-hmre": "^1.0.1",
"babel-register": "^6.11.6", "babel-register": "^6.11.6",
"browser-env": "^3.2.5", "browser-env": "^3.2.5",
"color": "^3.0.0",
"concurrently": "^3.4.0", "concurrently": "^3.4.0",
"copy-to-clipboard": "^3.0.6", "copy-to-clipboard": "^3.0.6",
"css": "^2.2.4",
"css-loader": "^0.19.0", "css-loader": "^0.19.0",
"devtron": "^1.1.0", "devtron": "^1.1.0",
"dom-storage": "^2.0.2", "dom-storage": "^2.0.2",
"electron": "2.0.7", "electron": "3.0.3",
"electron-packager": "^12.0.0", "electron-packager": "^12.0.0",
"eslint": "^3.13.1", "eslint": "^3.13.1",
"eslint-config-standard": "^6.2.1", "eslint-config-standard": "^6.2.1",

View File

@@ -30,7 +30,7 @@ Issues on Boostnote can be funded by anyone and the money will be distributed to
- [Facebook Group](https://www.facebook.com/groups/boostnote/) - [Facebook Group](https://www.facebook.com/groups/boostnote/)
- [Twitter](https://twitter.com/boostnoteapp) - [Twitter](https://twitter.com/boostnoteapp)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzkxOTk4ODkyNzc0LThkNmMzY2VlZjVhYTNiYjE5YjQyZGVjNTJlYTY1OGMyZTFjNGU5YTUyYjUzOWZhYTU4OTVlNDYyNDFjYWMzNDM) - [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzkxOTk4ODkyNzc0LThkNmMzY2VlZjVhYTNiYjE5YjQyZGVjNTJlYTY1OGMyZTFjNGU5YTUyYjUzOWZhYTU4OTVlNDYyNDFjYWMzNDM)
- [Blog](https://boostlog.io/tags/boostnote) - [Blog](https://medium.com/boostnote)
- [Reddit](https://www.reddit.com/r/Boostnote/) - [Reddit](https://www.reddit.com/r/Boostnote/)

View File

@@ -3,6 +3,7 @@
exports[`TagListItem renders correctly 1`] = ` exports[`TagListItem renders correctly 1`] = `
<div <div
className="tagList-itemContainer" className="tagList-itemContainer"
onContextMenu={[Function]}
> >
<div <div
className="tagList-itemNarrow" className="tagList-itemNarrow"

View File

@@ -50,11 +50,14 @@ const smartQuotes = 'This is a "QUOTE".'
const breaks = 'This is the first line.\nThis is the second line.' const breaks = 'This is the first line.\nThis is the second line.'
const shortcuts = '<kbd>Ctrl</kbd>\n\n[[Ctrl]]'
export default { export default {
basic, basic,
codeblock, codeblock,
katex, katex,
checkboxes, checkboxes,
smartQuotes, smartQuotes,
breaks breaks,
shortcuts
} }

Some files were not shown because too many files have changed in this diff Show More