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

Merge branch 'master' into new-snippet-tabbar

# Conflicts:
#	browser/main/Detail/SnippetNoteDetail.js
This commit is contained in:
Nikolay Lopin
2018-03-18 21:26:12 +03:00
86 changed files with 5089 additions and 1004 deletions

View File

@@ -8,6 +8,8 @@ import copyImage from 'browser/main/lib/dataApi/copyImage'
import { findStorage } from 'browser/lib/findStorage'
import fs from 'fs'
import eventEmitter from 'browser/main/lib/eventEmitter'
import iconv from 'iconv-lite'
const { ipcRenderer } = require('electron')
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -32,8 +34,13 @@ export default class CodeEditor extends React.Component {
constructor (props) {
super(props)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
this.changeHandler = (e) => this.handleChange(e)
this.focusHandler = () => {
ipcRenderer.send('editor:focused', true)
}
this.blurHandler = (editor, e) => {
ipcRenderer.send('editor:focused', false)
if (e == null) return null
let el = e.relatedTarget
while (el != null) {
@@ -81,7 +88,6 @@ export default class CodeEditor extends React.Component {
}
}
})
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
}
componentDidMount () {
@@ -139,6 +145,7 @@ export default class CodeEditor extends React.Component {
this.setMode(this.props.mode)
this.editor.on('focus', this.focusHandler)
this.editor.on('blur', this.blurHandler)
this.editor.on('change', this.changeHandler)
this.editor.on('paste', this.pasteHandler)
@@ -162,6 +169,7 @@ export default class CodeEditor extends React.Component {
}
componentWillUnmount () {
this.editor.off('focus', this.focusHandler)
this.editor.off('blur', this.blurHandler)
this.editor.off('change', this.changeHandler)
this.editor.off('paste', this.pasteHandler)
@@ -317,7 +325,7 @@ export default class CodeEditor extends React.Component {
fetch(pastedTxt, {
method: 'get'
}).then((response) => {
return (response.text())
return this.decodeResponse(response)
}).then((response) => {
const parsedResponse = (new window.DOMParser()).parseFromString(response, 'text/html')
const value = editor.getValue()
@@ -335,6 +343,31 @@ export default class CodeEditor extends React.Component {
})
}
decodeResponse (response) {
const headers = response.headers
const _charset = headers.has('content-type')
? this.extractContentTypeCharset(headers.get('content-type'))
: undefined
return response.arrayBuffer().then((buff) => {
return new Promise((resolve, reject) => {
try {
const charset = _charset !== undefined && iconv.encodingExists(_charset) ? _charset : 'utf-8'
resolve(iconv.decode(new Buffer(buff), charset).toString())
} catch (e) {
reject(e)
}
})
})
}
extractContentTypeCharset (contentType) {
return contentType.split(';').filter((str) => {
return str.trim().toLowerCase().startsWith('charset')
}).map((str) => {
return str.replace(/['"]/g, '').split('=')[1]
})[0]
}
render () {
const { className, fontSize } = this.props
let fontFamily = this.props.fontFamily

View File

@@ -279,6 +279,7 @@ class MarkdownEditor extends React.Component {
lineNumber={config.preview.lineNumber}
indentSize={editorIndentSize}
scrollPastEnd={config.preview.scrollPastEnd}
smartQuotes={config.preview.smartQuotes}
ref='preview'
onContextMenu={(e) => this.handleContextMenu(e)}
onDoubleClick={(e) => this.handleDoubleClick(e)}

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types'
import React from 'react'
import markdown from 'browser/lib/markdown'
import Markdown from 'browser/lib/markdown'
import _ from 'lodash'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
@@ -130,6 +130,13 @@ export default class MarkdownPreview extends React.Component {
this.printHandler = () => this.handlePrint()
this.linkClickHandler = this.handlelinkClick.bind(this)
this.initMarkdown = this.initMarkdown.bind(this)
this.initMarkdown()
}
initMarkdown () {
const { smartQuotes } = this.props
this.markdown = new Markdown({ typographer: smartQuotes })
}
handlePreviewAnchorClick (e) {
@@ -198,7 +205,7 @@ export default class MarkdownPreview extends React.Component {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams()
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber)
const body = markdown.render(noteContent)
const body = this.markdown.render(noteContent)
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
files.forEach((file) => {
@@ -216,6 +223,8 @@ export default class MarkdownPreview extends React.Component {
return `<html>
<head>
<meta charset="UTF-8">
<meta name = "viewport" content = "width = device-width, initial-scale = 1, maximum-scale = 1">
<style id="style">${inlineStyles}</style>
${styles}
</head>
@@ -309,6 +318,10 @@ export default class MarkdownPreview extends React.Component {
componentDidUpdate (prevProps) {
if (prevProps.value !== this.props.value) this.rewriteIframe()
if (prevProps.smartQuotes !== this.props.smartQuotes) {
this.initMarkdown()
this.rewriteIframe()
}
if (prevProps.fontFamily !== this.props.fontFamily ||
prevProps.fontSize !== this.props.fontSize ||
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
@@ -374,7 +387,7 @@ export default class MarkdownPreview extends React.Component {
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
})
}
this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value)
this.refs.root.contentWindow.document.body.innerHTML = this.markdown.render(value)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
@@ -390,9 +403,9 @@ export default class MarkdownPreview extends React.Component {
})
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
el.src = markdown.normalizeLinkText(el.src)
el.src = this.markdown.normalizeLinkText(el.src)
if (!/\/:storage/.test(el.src)) return
el.src = `file:///${markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
el.src = `file:///${this.markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
})
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
@@ -419,9 +432,9 @@ export default class MarkdownPreview extends React.Component {
el.innerHTML = ''
if (codeBlockTheme.indexOf('solarized') === 0) {
const [refThema, color] = codeBlockTheme.split(' ')
el.parentNode.className += ` cm-s-${refThema} cm-s-${color} CodeMirror`
el.parentNode.className += ` cm-s-${refThema} cm-s-${color}`
} else {
el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror`
el.parentNode.className += ` cm-s-${codeBlockTheme}`
}
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
@@ -504,9 +517,20 @@ export default class MarkdownPreview extends React.Component {
handlelinkClick (e) {
const noteHash = e.target.href.split('/').pop()
const regexIsNoteLink = /^(.{20})-(.{20})$/
// this will match the new uuid v4 hash and the old hash
// e.g.
// :note:1c211eb7dcb463de6490 and
// :note:7dd23275-f2b4-49cb-9e93-3454daf1af9c
const regexIsNoteLink = /^:note:([a-zA-Z0-9-]{20,36})$/
if (regexIsNoteLink.test(noteHash)) {
eventEmitter.emit('list:jump', noteHash)
eventEmitter.emit('list:jump', noteHash.replace(':note:', ''))
}
// this will match the old link format storage.key-note.key
// e.g.
// 877f99c3268608328037-1c211eb7dcb463de6490
const regexIsLegacyNoteLink = /^(.{20})-(.{20})$/
if (regexIsLegacyNoteLink.test(noteHash)) {
eventEmitter.emit('list:jump', noteHash.split('-')[1])
}
}
@@ -533,5 +557,6 @@ MarkdownPreview.propTypes = {
className: PropTypes.string,
value: PropTypes.string,
showCopyNotification: PropTypes.bool,
storagePath: PropTypes.string
storagePath: PropTypes.string,
smartQuotes: PropTypes.bool
}

View File

@@ -127,6 +127,7 @@ class MarkdownSplitEditor extends React.Component {
codeBlockFontFamily={config.editor.fontFamily}
lineNumber={config.preview.lineNumber}
scrollPastEnd={config.preview.scrollPastEnd}
smartQuotes={config.preview.smartQuotes}
ref='preview'
tabInde='0'
value={value}

View File

@@ -62,9 +62,9 @@ const NoteItem = ({
? 'item--active'
: 'item'
}
key={`${note.storage}-${note.key}`}
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)}
key={note.key}
onClick={e => handleNoteClick(e, note.key)}
onContextMenu={e => handleNoteContextMenu(e, note.key)}
onDragStart={e => handleDragStart(e, note)}
draggable='true'
>
@@ -100,7 +100,7 @@ const NoteItem = ({
{note.isStarred
? <img styleName='item-star' src='../resources/icon/icon-starred.svg' /> : ''
}
{note.isPinned && !pathname.match(/\/home|\/starred|\/trash/)
{note.isPinned && !pathname.match(/\/starred|\/trash/)
? <i styleName='item-pin' className='fa fa-thumb-tack' /> : ''
}
{note.type === 'MARKDOWN_NOTE'
@@ -123,7 +123,11 @@ NoteItem.propTypes = {
title: PropTypes.string.isrequired,
tags: PropTypes.array,
isStarred: PropTypes.bool.isRequired,
isTrashed: PropTypes.bool.isRequired
isTrashed: PropTypes.bool.isRequired,
blog: {
blogLink: PropTypes.string,
blogId: PropTypes.number
}
}),
handleNoteClick: PropTypes.func.isRequired,
handleNoteContextMenu: PropTypes.func.isRequired,

View File

@@ -117,7 +117,7 @@ $control-height = 30px
font-size 12px
line-height 20px
overflow ellipsis
display flex
display block
.item-bottom-tagList
flex 1
@@ -144,18 +144,18 @@ $control-height = 30px
padding-bottom 2px
.item-star
position relative
width 16px
height 16px
position absolute
right 2px
top 5px
color alpha($ui-favorite-star-button-color, 60%)
font-size 12px
padding 0
border-radius 17px
.item-pin
position relative
width 34px
height 34px
position absolute
right 25px
top 7px
color #E54D42
font-size 14px
padding 0

View File

@@ -28,9 +28,9 @@ const NoteItemSimple = ({
? 'item-simple--active'
: 'item-simple'
}
key={`${note.storage}-${note.key}`}
onClick={e => handleNoteClick(e, `${note.storage}-${note.key}`)}
onContextMenu={e => handleNoteContextMenu(e, `${note.storage}-${note.key}`)}
key={note.key}
onClick={e => handleNoteClick(e, note.key)}
onContextMenu={e => handleNoteContextMenu(e, note.key)}
onDragStart={e => handleDragStart(e, note)}
draggable='true'
>

View File

@@ -5,6 +5,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SideNavFilter.styl'
import i18n from 'browser/lib/i18n'
/**
* @param {boolean} isFolded
@@ -31,7 +32,7 @@ const SideNavFilter = ({
}
/>
</div>
<span styleName='menu-button-label'>All Notes</span>
<span styleName='menu-button-label'>{i18n.__('All Notes')}</span>
<span styleName='counters'>{counterTotalNote}</span>
</button>
@@ -45,7 +46,7 @@ const SideNavFilter = ({
}
/>
</div>
<span styleName='menu-button-label'>Starred</span>
<span styleName='menu-button-label'>{i18n.__('Starred')}</span>
<span styleName='counters'>{counterStarredNote}</span>
</button>
@@ -59,7 +60,7 @@ const SideNavFilter = ({
}
/>
</div>
<span onContextMenu={handleFilterButtonContextMenu} styleName='menu-button-label'>Trash</span>
<span onContextMenu={handleFilterButtonContextMenu} styleName='menu-button-label'>{i18n.__('Trash')}</span>
<span styleName='counters'>{counterDelNote}</span>
</button>

View File

@@ -6,6 +6,18 @@ import React from 'react'
import styles from './StorageItem.styl'
import CSSModules from 'browser/lib/CSSModules'
import _ from 'lodash'
import { SortableHandle } from 'react-sortable-hoc'
const DraggableIcon = SortableHandle(({ className }) => (
<i className={`fa ${className}`} />
))
const FolderIcon = ({ className, color, isActive }) => {
const iconStyle = isActive ? 'fa-folder-open-o' : 'fa-folder-o'
return (
<i className={`fa ${iconStyle} ${className}`} style={{ color: color }} />
)
}
/**
* @param {boolean} isActive
@@ -21,34 +33,54 @@ import _ from 'lodash'
* @return {React.Component}
*/
const StorageItem = ({
isActive, handleButtonClick, handleContextMenu, folderName,
folderColor, isFolded, noteCount, handleDrop, handleDragEnter, handleDragLeave
}) => (
<button styleName={isActive
? 'folderList-item--active'
: 'folderList-item'
}
onClick={handleButtonClick}
onContextMenu={handleContextMenu}
onDrop={handleDrop}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
<span styleName={isFolded
? 'folderList-item-name--folded' : 'folderList-item-name'
}>
<text style={{color: folderColor, paddingRight: '10px'}}>{isActive ? <i className='fa fa-folder-open-o' /> : <i className='fa fa-folder-o' />}</text>{isFolded ? _.truncate(folderName, {length: 1, omission: ''}) : folderName}
</span>
{(!isFolded && _.isNumber(noteCount)) &&
<span styleName='folderList-item-noteCount'>{noteCount}</span>
}
{isFolded &&
<span styleName='folderList-item-tooltip'>
{folderName}
styles,
isActive,
handleButtonClick,
handleContextMenu,
folderName,
folderColor,
isFolded,
noteCount,
handleDrop,
handleDragEnter,
handleDragLeave
}) => {
return (
<button
styleName={isActive ? 'folderList-item--active' : 'folderList-item'}
onClick={handleButtonClick}
onContextMenu={handleContextMenu}
onDrop={handleDrop}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
>
{!isFolded && (
<DraggableIcon className={styles['folderList-item-reorder']} />
)}
<span
styleName={
isFolded ? 'folderList-item-name--folded' : 'folderList-item-name'
}
>
<FolderIcon
styleName='folderList-item-icon'
color={folderColor}
isActive={isActive}
/>
{isFolded
? _.truncate(folderName, { length: 1, omission: '' })
: folderName}
</span>
}
</button>
)
{!isFolded &&
_.isNumber(noteCount) && (
<span styleName='folderList-item-noteCount'>{noteCount}</span>
)}
{isFolded && (
<span styleName='folderList-item-tooltip'>{folderName}</span>
)}
</button>
)
}
StorageItem.propTypes = {
isActive: PropTypes.bool.isRequired,

View File

@@ -13,6 +13,7 @@
border none
overflow ellipsis
font-size 14px
align-items: center
&:first-child
margin-top 0
&:hover
@@ -22,7 +23,7 @@
&:active
color $$ui-button-default-color
background-color alpha($ui-button-default--active-backgroundColor, 20%)
.folderList-item--active
@extend .folderList-item
color #1EC38B
@@ -34,9 +35,7 @@
.folderList-item-name
display block
flex 1
padding 0 12px
height 26px
line-height 26px
padding-right: 10px
border-width 0 0 0 2px
border-style solid
border-color transparent
@@ -69,9 +68,20 @@
.folderList-item-name--folded
@extend .folderList-item-name
padding-left 7px
text
.folderList-item-icon
font-size 9px
.folderList-item-icon
padding-right: 10px
.folderList-item-reorder
font-size: 9px
padding: 10px 8px 10px 9px;
color: rgba(147, 147, 149, 0.3)
cursor: ns-resize
&:before
content: "\f142 \f142"
body[data-theme="white"]
.folderList-item
color $ui-inactive-text-color
@@ -127,4 +137,4 @@ body[data-theme="solarized-dark"]
background-color $ui-solarized-dark-button-backgroundColor
&:hover
color $ui-solarized-dark-text-color
background-color $ui-solarized-dark-button-backgroundColor
background-color $ui-solarized-dark-button-backgroundColor

8
browser/lib/i18n.js Normal file
View File

@@ -0,0 +1,8 @@
// load package for localization
const i18n = new (require('i18n-2'))({
// setup some locales - other locales default to the first locale
locales: ['en', 'sq', 'zh-CN', 'zh-TW', 'da', 'fr', 'de', 'ja', 'ko', 'no', 'pl', 'pt', 'es'],
extension: '.json'
})
export default i18n

View File

@@ -1,7 +1,11 @@
const crypto = require('crypto')
const _ = require('lodash')
const uuidv4 = require('uuid/v4')
module.exports = function (length) {
if (!_.isFinite(length)) length = 10
module.exports = function (uuid) {
if (typeof uuid === typeof true && uuid) {
return uuidv4()
}
const length = 10
return crypto.randomBytes(length).toString('hex')
}

View File

@@ -0,0 +1,23 @@
'use strict'
import sanitizeHtml from 'sanitize-html'
module.exports = function sanitizePlugin (md, options) {
options = options || {}
md.core.ruler.after('linkify', 'sanitize_inline', state => {
for (let tokenIdx = 0; tokenIdx < state.tokens.length; tokenIdx++) {
if (state.tokens[tokenIdx].type === 'html_block') {
state.tokens[tokenIdx].content = sanitizeHtml(state.tokens[tokenIdx].content, options)
}
if (state.tokens[tokenIdx].type === 'inline') {
const inlineTokens = state.tokens[tokenIdx].children
for (let childIdx = 0; childIdx < inlineTokens.length; childIdx++) {
if (inlineTokens[childIdx].type === 'html_inline') {
inlineTokens[childIdx].content = sanitizeHtml(inlineTokens[childIdx].content, options)
}
}
}
}
})
}

View File

@@ -1,14 +1,12 @@
import markdownit from 'markdown-it'
import sanitize from './markdown-it-sanitize-html'
import emoji from 'markdown-it-emoji'
import math from '@rokt33r/markdown-it-math'
import _ from 'lodash'
import ConfigManager from 'browser/main/lib/ConfigManager'
import katex from 'katex'
import {lastFindInArray} from './utils'
// FIXME We should not depend on global variable.
const katex = window.katex
const config = ConfigManager.get()
function createGutter (str, firstLineNumber) {
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
const lastLineNumber = (str.match(/\n/g) || []).length + firstLineNumber - 1
@@ -19,171 +17,218 @@ function createGutter (str, firstLineNumber) {
return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
}
var md = markdownit({
typographer: true,
linkify: true,
html: true,
xhtmlOut: true,
breaks: true,
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)
class Markdown {
constructor (options = {}) {
const config = ConfigManager.get()
const defaultOptions = {
typographer: config.preview.smartQuotes,
linkify: true,
html: true,
xhtmlOut: true,
breaks: true,
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>`
}
return '<pre class="code">' +
'<span class="filename">' + fileName + '</span>' +
createGutter(str, firstLineNumber) +
'<code class="' + langType + '">' +
str +
'</code></pre>'
}
})
md.use(emoji, {
shortcuts: {}
})
md.use(math, {
inlineOpen: config.preview.latexInlineOpen,
inlineClose: config.preview.latexInlineClose,
blockOpen: config.preview.latexBlockOpen,
blockClose: config.preview.latexBlockClose,
inlineRenderer: function (str) {
let output = ''
try {
output = katex.renderToString(str.trim())
} catch (err) {
output = `<span class="katex-error">${err.message}</span>`
}
return output
},
blockRenderer: function (str) {
let output = ''
try {
output = katex.renderToString(str.trim(), { displayMode: true })
} catch (err) {
output = `<div class="katex-error">${err.message}</div>`
}
return output
}
})
md.use(require('markdown-it-imsize'))
md.use(require('markdown-it-footnote'))
md.use(require('markdown-it-multimd-table'))
md.use(require('markdown-it-named-headers'), {
slugify: (header) => {
return encodeURI(header.trim()
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
.replace(/\s+/g, '-'))
.replace(/\-+$/, '')
}
})
md.use(require('markdown-it-kbd'))
const deflate = require('markdown-it-plantuml/lib/deflate')
md.use(require('markdown-it-plantuml'), '', {
generateSource: function (umlCode) {
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`@startuml\n${s}\n@enduml`, 9)
)
return `http://www.plantuml.com/plantuml/svg/${zippedCode}`
}
})
// Override task item
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
let content, terminate, i, l, token
let nextLine = startLine + 1
const terminatorRules = state.md.block.ruler.getRules('paragraph')
const endLine = state.lineMax
// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { continue }
// Some tags can terminate paragraph without empty line.
terminate = false
for (i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) { break }
}
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
state.line = nextLine
token = state.push('paragraph_open', 'p', 1)
token.map = [startLine, state.line]
if (state.parentType === 'list') {
const match = content.match(/^\[( |x)\] ?(.+)/i)
if (match) {
const liToken = lastFindInArray(state.tokens, token => token.type === 'list_item_open')
if (liToken) {
if (!liToken.attrs) {
liToken.attrs = []
if (langType === 'flowchart') {
return `<pre class="flowchart">${str}</pre>`
}
liToken.attrs.push(['class', 'taskListItem'])
if (langType === 'sequence') {
return `<pre class="sequence">${str}</pre>`
}
return '<pre class="code CodeMirror">' +
'<span class="filename">' + fileName + '</span>' +
createGutter(str, firstLineNumber) +
'<code class="' + langType + '">' +
str +
'</code></pre>'
}
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>`
}
const updatedOptions = Object.assign(defaultOptions, options)
this.md = markdownit(updatedOptions)
// Sanitize use rinput before other plugins
this.md.use(sanitize, {
allowedTags: ['iframe', 'input', 'b',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7', 'h8', 'br', 'b', 'i', 'strong', 'em', 'a', 'pre', 'code', 'img', 'tt',
'div', 'ins', 'del', 'sup', 'sub', 'p', 'ol', 'ul', 'table', 'thead', 'tbody', 'tfoot', 'blockquote',
'dl', 'dt', 'dd', 'kbd', 'q', 'samp', 'var', 'hr', 'ruby', 'rt', 'rp', 'li', 'tr', 'td', 'th', 's', 'strike', 'summary', 'details'
],
allowedAttributes: {
'*': [
'style',
'abbr', 'accept', 'accept-charset',
'accesskey', 'action', 'align', 'alt', 'axis',
'border', 'cellpadding', 'cellspacing', 'char',
'charoff', 'charset', 'checked',
'clear', 'cols', 'colspan', 'color',
'compact', 'coords', 'datetime', 'dir',
'disabled', 'enctype', 'for', 'frame',
'headers', 'height', 'hreflang',
'hspace', 'ismap', 'label', 'lang',
'maxlength', 'media', 'method',
'multiple', 'name', 'nohref', 'noshade',
'nowrap', 'open', 'prompt', 'readonly', 'rel', 'rev',
'rows', 'rowspan', 'rules', 'scope',
'selected', 'shape', 'size', 'span',
'start', 'summary', 'tabindex', 'target',
'title', 'type', 'usemap', 'valign', 'value',
'vspace', 'width', 'itemprop'
],
'a': ['href'],
'div': ['itemscope', 'itemtype'],
'blockquote': ['cite'],
'del': ['cite'],
'ins': ['cite'],
'q': ['cite'],
'img': ['src', 'width', 'height'],
'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
'input': ['type', 'id', 'checked']
},
allowedIframeHostnames: ['www.youtube.com']
})
this.md.use(emoji, {
shortcuts: {}
})
this.md.use(math, {
inlineOpen: config.preview.latexInlineOpen,
inlineClose: config.preview.latexInlineClose,
blockOpen: config.preview.latexBlockOpen,
blockClose: config.preview.latexBlockClose,
inlineRenderer: function (str) {
let output = ''
try {
output = katex.renderToString(str.trim())
} catch (err) {
output = `<span class="katex-error">${err.message}</span>`
}
return output
},
blockRenderer: function (str) {
let output = ''
try {
output = katex.renderToString(str.trim(), { displayMode: true })
} catch (err) {
output = `<div class="katex-error">${err.message}</div>`
}
return output
}
})
this.md.use(require('markdown-it-imsize'))
this.md.use(require('markdown-it-footnote'))
this.md.use(require('markdown-it-multimd-table'))
this.md.use(require('markdown-it-named-headers'), {
slugify: (header) => {
return encodeURI(header.trim()
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
.replace(/\s+/g, '-'))
.replace(/\-+$/, '')
}
})
this.md.use(require('markdown-it-kbd'))
const deflate = require('markdown-it-plantuml/lib/deflate')
this.md.use(require('markdown-it-plantuml'), '', {
generateSource: function (umlCode) {
const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64(
deflate.zip_deflate(`@startuml\n${s}\n@enduml`, 9)
)
return `http://www.plantuml.com/plantuml/svg/${zippedCode}`
}
})
// Override task item
this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
let content, terminate, i, l, token
let nextLine = startLine + 1
const terminatorRules = state.md.block.ruler.getRules('paragraph')
const endLine = state.lineMax
// jump line-by-line until empty one or EOF
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
// this would be a code block normally, but after paragraph
// it's considered a lazy continuation regardless of what's there
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
// quirk for blockquotes, this line should already be checked by that rule
if (state.sCount[nextLine] < 0) { continue }
// Some tags can terminate paragraph without empty line.
terminate = false
for (i = 0, l = terminatorRules.length; i < l; i++) {
if (terminatorRules[i](state, nextLine, endLine, true)) {
terminate = true
break
}
}
if (terminate) { break }
}
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
state.line = nextLine
token = state.push('paragraph_open', 'p', 1)
token.map = [startLine, state.line]
if (state.parentType === 'list') {
const match = content.match(/^\[( |x)\] ?(.+)/i)
if (match) {
const liToken = lastFindInArray(state.tokens, token => token.type === 'list_item_open')
if (liToken) {
if (!liToken.attrs) {
liToken.attrs = []
}
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>`
}
}
token = state.push('inline', '', 0)
token.content = content
token.map = [startLine, state.line]
token.children = []
token = state.push('paragraph_close', 'p', -1)
return true
})
// Add line number attribute for scrolling
const originalRender = this.md.renderer.render
this.md.renderer.render = (tokens, options, env) => {
tokens.forEach((token) => {
switch (token.type) {
case 'heading_open':
case 'paragraph_open':
case 'blockquote_open':
case 'table_open':
token.attrPush(['data-line', token.map[0]])
}
})
const result = originalRender.call(this.md.renderer, tokens, options, env)
return result
}
// FIXME We should not depend on global variable.
window.md = this.md
}
token = state.push('inline', '', 0)
token.content = content
token.map = [startLine, state.line]
token.children = []
token = state.push('paragraph_close', 'p', -1)
return true
})
// Add line number attribute for scrolling
const originalRender = md.renderer.render
md.renderer.render = function render (tokens, options, env) {
tokens.forEach((token) => {
switch (token.type) {
case 'heading_open':
case 'paragraph_open':
case 'blockquote_open':
case 'table_open':
token.attrPush(['data-line', token.map[0]])
}
})
const result = originalRender.call(md.renderer, tokens, options, env)
return result
}
// FIXME We should not depend on global variable.
window.md = md
function normalizeLinkText (linkText) {
return md.normalizeLinkText(linkText)
}
const markdown = {
render: function markdown (content) {
render (content) {
if (!_.isString(content)) content = ''
const renderedContent = md.render(content)
return renderedContent
},
normalizeLinkText
return this.md.render(content)
}
normalizeLinkText (linkText) {
return this.md.normalizeLinkText(linkText)
}
}
export default markdown
export default Markdown

View File

@@ -3,6 +3,7 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
import copy from 'copy-to-clipboard'
import i18n from 'browser/lib/i18n'
class InfoPanel extends React.Component {
copyNoteLink () {
@@ -19,7 +20,7 @@ class InfoPanel extends React.Component {
<div className='infoPanel' styleName='control-infoButton-panel' style={{display: 'none'}}>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>MODIFICATION DATE</p>
<p styleName='modification-date-desc'>{i18n.__('MODIFICATION DATE')}</p>
</div>
<hr />
@@ -29,11 +30,11 @@ class InfoPanel extends React.Component {
: <div styleName='count-wrap'>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{wordCount}</p>
<p styleName='infoPanel-sub-count'>Words</p>
<p styleName='infoPanel-sub-count'>{i18n.__('Words')}</p>
</div>
<div styleName='count-number'>
<p styleName='infoPanel-defaul-count'>{letterCount}</p>
<p styleName='infoPanel-sub-count'>Letters</p>
<p styleName='infoPanel-sub-count'>{i18n.__('Letters')}</p>
</div>
</div>
}
@@ -45,17 +46,17 @@ class InfoPanel extends React.Component {
<div>
<p styleName='infoPanel-default'>{storageName}</p>
<p styleName='infoPanel-sub'>STORAGE</p>
<p styleName='infoPanel-sub'>{i18n.__('STORAGE')}</p>
</div>
<div>
<p styleName='infoPanel-default'>{folderName}</p>
<p styleName='infoPanel-sub'>FOLDER</p>
<p styleName='infoPanel-sub'>{i18n.__('FOLDER')}</p>
</div>
<div>
<p styleName='infoPanel-default'>{createdAt}</p>
<p styleName='infoPanel-sub'>CREATION DATE</p>
<p styleName='infoPanel-sub'>{i18n.__('CREATION DATE')}</p>
</div>
<div>
@@ -63,7 +64,7 @@ class InfoPanel extends React.Component {
<button onClick={() => this.copyNoteLink()} styleName='infoPanel-copyButton'>
<i className='fa fa-clipboard' />
</button>
<p styleName='infoPanel-sub'>NOTE LINK</p>
<p styleName='infoPanel-sub'>{i18n.__('NOTE LINK')}</p>
</div>
<hr />
@@ -71,22 +72,22 @@ class InfoPanel extends React.Component {
<div id='export-wrap'>
<button styleName='export--enable' onClick={(e) => exportAsMd(e)}>
<i className='fa fa-file-code-o' />
<p>.md</p>
<p>{i18n.__('.md')}</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsTxt(e)}>
<i className='fa fa-file-text-o' />
<p>.txt</p>
<p>{i18n.__('.txt')}</p>
</button>
<button styleName='export--enable' onClick={(e) => exportAsHtml(e)}>
<i className='fa fa-html5' />
<p>.html</p>
<p>{i18n.__('.html')}</p>
</button>
<button styleName='export--enable' onClick={(e) => print(e)}>
<i className='fa fa-print' />
<p>Print</p>
<p>{i18n.__('Print')}</p>
</button>
</div>
</div>

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './InfoPanel.styl'
import i18n from 'browser/lib/i18n'
const InfoPanelTrashed = ({
storageName, folderName, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml
@@ -9,24 +10,24 @@ const InfoPanelTrashed = ({
<div className='infoPanel' styleName='control-infoButton-panel-trash' style={{display: 'none'}}>
<div>
<p styleName='modification-date'>{updatedAt}</p>
<p styleName='modification-date-desc'>MODIFICATION DATE</p>
<p styleName='modification-date-desc'>{i18n.__('MODIFICATION DATE')}</p>
</div>
<hr />
<div>
<p styleName='infoPanel-default'>{storageName}</p>
<p styleName='infoPanel-sub'>STORAGE</p>
<p styleName='infoPanel-sub'>{i18n.__('STORAGE')}</p>
</div>
<div>
<p styleName='infoPanel-default'><text styleName='infoPanel-trash'>Trash</text>{folderName}</p>
<p styleName='infoPanel-sub'>FOLDER</p>
<p styleName='infoPanel-sub'>{i18n.__('FOLDER')}</p>
</div>
<div>
<p styleName='infoPanel-default'>{createdAt}</p>
<p styleName='infoPanel-sub'>CREATION DATE</p>
<p styleName='infoPanel-sub'>{i18n.__('CREATION DATE')}</p>
</div>
<div id='export-wrap'>

View File

@@ -139,7 +139,7 @@ class MarkdownNoteDetail extends React.Component {
hashHistory.replace({
pathname: location.pathname,
query: {
key: newNote.storage + '-' + newNote.key
key: newNote.key
}
})
this.setState({
@@ -393,7 +393,7 @@ class MarkdownNoteDetail extends React.Component {
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](${location.query.key})`}
noteLink={`[${note.title}](:note:${location.query.key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.exportAsMd}

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TrashButton.styl'
import i18n from 'browser/lib/i18n'
const PermanentDeleteButton = ({
onClick
@@ -10,7 +11,7 @@ const PermanentDeleteButton = ({
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
<span styleName='tooltip'>Permanent Delete</span>
<span styleName='tooltip'>{i18n.__('Permanent Delete')}</span>
</button>
)

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './RestoreButton.styl'
import i18n from 'browser/lib/i18n'
const RestoreButton = ({
onClick
@@ -10,7 +11,7 @@ const RestoreButton = ({
onClick={onClick}
>
<i className='fa fa-undo fa-fw' styleName='iconRestore' />
<span styleName='tooltip'>Restore</span>
<span styleName='tooltip'>{i18n.__('Restore')}</span>
</button>
)

View File

@@ -25,7 +25,8 @@ import PermanentDeleteButton from './PermanentDeleteButton'
import InfoButton from './InfoButton'
import InfoPanel from './InfoPanel'
import InfoPanelTrashed from './InfoPanelTrashed'
import {formatDate} from 'browser/lib/date-formatter'
import { formatDate } from 'browser/lib/date-formatter'
import i18n from 'browser/lib/i18n'
function pass (name) {
switch (name) {
@@ -166,7 +167,7 @@ class SnippetNoteDetail extends React.Component {
hashHistory.replace({
pathname: location.pathname,
query: {
key: newNote.storage + '-' + newNote.key
key: newNote.key
}
})
this.setState({
@@ -328,9 +329,9 @@ class SnippetNoteDetail extends React.Component {
if (this.state.note.snippets[index].content.trim().length > 0) {
const dialogIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete a snippet',
detail: 'This work cannot be undone.',
buttons: ['Confirm', 'Cancel']
message: i18n.__('Delete a snippet'),
detail: i18n.__('This work cannot be undone.'),
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
})
if (dialogIndex === 0) {
this.deleteSnippetByIndex(index)
@@ -422,6 +423,7 @@ class SnippetNoteDetail extends React.Component {
handleKeyDown (e) {
switch (e.keyCode) {
// tab key
case 9:
if (e.ctrlKey && !e.shiftKey) {
e.preventDefault()
@@ -434,6 +436,7 @@ class SnippetNoteDetail extends React.Component {
this.focusEditor()
}
break
// L key
case 76:
{
const isSuper = global.process.platform === 'darwin'
@@ -445,6 +448,7 @@ class SnippetNoteDetail extends React.Component {
}
}
break
// T key
case 84:
{
const isSuper = global.process.platform === 'darwin'
@@ -634,8 +638,8 @@ class SnippetNoteDetail extends React.Component {
showWarning () {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Sorry!',
detail: 'md/text import is available only a markdown note.',
message: i18n.__('Sorry!'),
detail: i18n.__('md/text import is available only a markdown note.'),
buttons: ['OK']
})
}
@@ -777,7 +781,7 @@ class SnippetNoteDetail extends React.Component {
<InfoPanel
storageName={currentOption.storage.name}
folderName={currentOption.folder.name}
noteLink={`[${note.title}](${location.query.key})`}
noteLink={`[${note.title}](:note:${location.query.key})`}
updatedAt={formatDate(note.updatedAt)}
createdAt={formatDate(note.createdAt)}
exportAsMd={this.showWarning}

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ToggleModeButton.styl'
import i18n from 'browser/lib/i18n'
const ToggleModeButton = ({
onClick, editorType
@@ -13,7 +14,7 @@ const ToggleModeButton = ({
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
</div>
<span styleName='tooltip'>Toggle Mode</span>
<span styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
</div>
)

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './TrashButton.styl'
import i18n from 'browser/lib/i18n'
const TrashButton = ({
onClick
@@ -10,7 +11,7 @@ const TrashButton = ({
onClick={(e) => onClick(e)}
>
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
<span styleName='tooltip'>Trash</span>
<span styleName='tooltip'>{i18n.__('Trash')}</span>
</button>
)

View File

@@ -7,6 +7,7 @@ import MarkdownNoteDetail from './MarkdownNoteDetail'
import SnippetNoteDetail from './SnippetNoteDetail'
import ee from 'browser/main/lib/eventEmitter'
import StatusBar from '../StatusBar'
import i18n from 'browser/lib/i18n'
const OSX = global.process.platform === 'darwin'
@@ -40,9 +41,9 @@ class Detail extends React.Component {
const alertConfig = {
type: 'warning',
message: 'Confirm note deletion',
detail: 'This will permanently remove this note.',
buttons: ['Confirm', 'Cancel']
message: i18n.__('Confirm note deletion'),
detail: i18n.__('This will permanently remove this note.'),
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
}
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), alertConfig)
@@ -56,11 +57,8 @@ class Detail extends React.Component {
const { location, data, config } = this.props
let note = null
if (location.query.key != null) {
const splitted = location.query.key.split('-')
const storageKey = splitted.shift()
const noteKey = splitted.shift()
note = data.noteMap.get(storageKey + '-' + noteKey)
const noteKey = location.query.key
note = data.noteMap.get(noteKey)
}
if (note == null) {
@@ -70,7 +68,7 @@ class Detail extends React.Component {
tabIndex='0'
>
<div styleName='empty'>
<div styleName='empty-message'>{OSX ? 'Command(⌘)' : 'Ctrl(^)'} + N<br />to create a new note</div>
<div styleName='empty-message'>{OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')} + N<br />{i18n.__('to create a new note')}</div>
</div>
<StatusBar
{..._.pick(this.props, ['config', 'location', 'dispatch'])}

View File

@@ -14,6 +14,7 @@ import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig'
import eventEmitter from 'browser/main/lib/eventEmitter'
import { hashHistory } from 'react-router'
import store from 'browser/main/store'
import i18n from 'browser/lib/i18n'
const path = require('path')
const electron = require('electron')
const { remote } = electron
@@ -148,6 +149,35 @@ class Main extends React.Component {
} else {
document.body.setAttribute('data-theme', 'default')
}
if (config.ui.language === 'sq') {
i18n.setLocale('sq')
} else if (config.ui.language === 'zh-CN') {
i18n.setLocale('zh-CN')
} else if (config.ui.language === 'zh-TW') {
i18n.setLocale('zh-TW')
} else if (config.ui.language === 'da') {
i18n.setLocale('da')
} else if (config.ui.language === 'fr') {
i18n.setLocale('fr')
} else if (config.ui.language === 'de') {
i18n.setLocale('de')
} else if (config.ui.language === 'ja') {
i18n.setLocale('ja')
} else if (config.ui.language === 'ko') {
i18n.setLocale('ko')
} else if (config.ui.language === 'no') {
i18n.setLocale('no')
} else if (config.ui.language === 'pl') {
i18n.setLocale('pl')
} else if (config.ui.language === 'pt') {
i18n.setLocale('pt')
} else if (config.ui.language === 'ru') {
i18n.setLocale('ru')
} else if (config.ui.language === 'es') {
i18n.setLocale('es')
} else {
i18n.setLocale('en')
}
// Reload all data
dataApi.init()

View File

@@ -6,6 +6,7 @@ import _ from 'lodash'
import modal from 'browser/main/lib/modal'
import NewNoteModal from 'browser/main/modals/NewNoteModal'
import eventEmitter from 'browser/main/lib/eventEmitter'
import i18n from 'browser/lib/i18n'
const { remote } = require('electron')
const { dialog } = remote
@@ -86,7 +87,7 @@ class NewNoteButton extends React.Component {
onClick={(e) => this.handleNewNoteButtonClick(e)}>
<img styleName='iconTag' src='../resources/icon/icon-newnote.svg' />
<span styleName='control-newNoteButton-tooltip'>
Make a note {OSX ? '⌘' : 'Ctrl'} + N
{i18n.__('Make a note')} {OSX ? '⌘' : i18n.__('Ctrl')} + N
</span>
</button>
</div>

View File

@@ -1,3 +1,4 @@
/* global electron */
import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
@@ -13,10 +14,14 @@ import searchFromNotes from 'browser/lib/search'
import fs from 'fs'
import path from 'path'
import { hashHistory } from 'react-router'
import copy from 'copy-to-clipboard'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import Markdown from '../../lib/markdown'
import i18n from 'browser/lib/i18n'
const { remote } = require('electron')
const { Menu, MenuItem, dialog } = remote
const WP_POST_PATH = '/wp/v2/posts'
function sortByCreatedAt (a, b) {
return new Date(b.createdAt) - new Date(a.createdAt)
@@ -31,7 +36,7 @@ function sortByUpdatedAt (a, b) {
}
function findNoteByKey (notes, noteKey) {
return notes.find((note) => `${note.storage}-${note.key}` === noteKey)
return notes.find((note) => note.key === noteKey)
}
function findNotesByKeys (notes, noteKeys) {
@@ -39,7 +44,7 @@ function findNotesByKeys (notes, noteKeys) {
}
function getNoteKey (note) {
return `${note.storage}-${note.key}`
return note.key
}
class NoteList extends React.Component {
@@ -70,6 +75,7 @@ class NoteList extends React.Component {
this.getNoteFolder = this.getNoteFolder.bind(this)
this.getViewType = this.getViewType.bind(this)
this.restoreNote = this.restoreNote.bind(this)
this.copyNoteLink = this.copyNoteLink.bind(this)
// TODO: not Selected noteKeys but SelectedNote(for reusing)
this.state = {
@@ -114,10 +120,10 @@ class NoteList extends React.Component {
componentDidUpdate (prevProps) {
const { location } = this.props
const { selectedNoteKeys } = this.state
const visibleNoteKeys = this.notes.map(note => `${note.storage}-${note.key}`)
const visibleNoteKeys = this.notes.map(note => note.key)
const note = this.notes[0]
const prevKey = prevProps.location.query.key
const noteKey = visibleNoteKeys.includes(prevKey) ? prevKey : note && `${note.storage}-${note.key}`
const noteKey = visibleNoteKeys.includes(prevKey) ? prevKey : note && note.key
if (note && location.query.key == null) {
const { router } = this.context
@@ -257,27 +263,38 @@ class NoteList extends React.Component {
handleNoteListKeyDown (e) {
if (e.metaKey || e.ctrlKey) return true
// A key
if (e.keyCode === 65 && !e.shiftKey) {
e.preventDefault()
ee.emit('top:new-note')
}
// D key
if (e.keyCode === 68) {
e.preventDefault()
this.deleteNote()
}
// E key
if (e.keyCode === 69) {
e.preventDefault()
ee.emit('detail:focus')
}
if (e.keyCode === 38) {
// F or S key
if (e.keyCode === 70 || e.keyCode === 83) {
e.preventDefault()
ee.emit('top:focus-search')
}
// UP or K key
if (e.keyCode === 38 || e.keyCode === 75) {
e.preventDefault()
this.selectPriorNote()
}
if (e.keyCode === 40) {
// DOWN or J key
if (e.keyCode === 40 || e.keyCode === 74) {
e.preventDefault()
this.selectNextNote()
}
@@ -458,6 +475,10 @@ class NoteList extends React.Component {
const deleteLabel = 'Delete Note'
const cloneNote = 'Clone Note'
const restoreNote = 'Restore Note'
const copyNoteLink = 'Copy Note Link'
const publishLabel = 'Publish Blog'
const updateLabel = 'Update Blog'
const openBlogLabel = 'Open Blog'
const menu = new Menu()
if (!location.pathname.match(/\/starred|\/trash/)) {
@@ -482,6 +503,28 @@ class NoteList extends React.Component {
label: cloneNote,
click: this.cloneNote.bind(this)
}))
menu.append(new MenuItem({
label: copyNoteLink,
click: this.copyNoteLink(note)
}))
if (note.type === 'MARKDOWN_NOTE') {
if (note.blog && note.blog.blogLink && note.blog.blogId) {
menu.append(new MenuItem({
label: updateLabel,
click: this.publishMarkdown.bind(this)
}))
menu.append(new MenuItem({
label: openBlogLabel,
click: () => this.openBlog.bind(this)(note)
}))
} else {
menu.append(new MenuItem({
label: publishLabel,
click: this.publishMarkdown.bind(this)
}))
}
}
menu.popup()
}
@@ -548,11 +591,9 @@ class NoteList extends React.Component {
})
if (dialogueButtonIndex === 1) return
Promise.all(
selectedNoteKeys.map((uniqueKey) => {
const storageKey = uniqueKey.split('-')[0]
const noteKey = uniqueKey.split('-')[1]
selectedNotes.map((note) => {
return dataApi
.deleteNote(storageKey, noteKey)
.deleteNote(note.storage, note.key)
})
)
.then((data) => {
@@ -613,23 +654,134 @@ class NoteList extends React.Component {
content: firstNote.content
})
.then((note) => {
const uniqueKey = note.storage + '-' + note.key
dispatch({
type: 'UPDATE_NOTE',
note: note
})
this.setState({
selectedNoteKeys: [uniqueKey]
selectedNoteKeys: [note.key]
})
hashHistory.push({
pathname: location.pathname,
query: {key: uniqueKey}
query: {key: note.key}
})
})
}
copyNoteLink (note) {
const noteLink = `[${note.title}](${note.key})`
return copy(noteLink)
}
save (note) {
const { dispatch } = this.props
dataApi
.updateNote(note.storage, note.key, note)
.then((note) => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
}
publishMarkdown () {
if (this.pendingPublish) {
clearTimeout(this.pendingPublish)
}
this.pendingPublish = setTimeout(() => {
this.publishMarkdownNow()
}, 1000)
}
publishMarkdownNow () {
const {selectedNoteKeys} = this.state
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
const firstNote = selectedNotes[0]
const config = ConfigManager.get()
const {address, token, authMethod, username, password} = config.blog
let authToken = ''
if (authMethod === 'USER') {
authToken = `Basic ${window.btoa(`${username}:${password}`)}`
} else {
authToken = `Bearer ${token}`
}
const contentToRender = firstNote.content.replace(`# ${firstNote.title}`, '')
const markdown = new Markdown()
const data = {
title: firstNote.title,
content: markdown.render(contentToRender),
status: 'publish'
}
let url = ''
let method = ''
if (firstNote.blog && firstNote.blog.blogId) {
url = `${address}${WP_POST_PATH}/${firstNote.blog.blogId}`
method = 'PUT'
} else {
url = `${address}${WP_POST_PATH}`
method = 'POST'
}
// eslint-disable-next-line no-undef
fetch(url, {
method: method,
body: JSON.stringify(data),
headers: {
'Authorization': authToken,
'Content-Type': 'application/json'
}
}).then(res => res.json())
.then(response => {
if (_.isNil(response.link) || _.isNil(response.id)) {
return Promise.reject()
}
firstNote.blog = {
blogLink: response.link,
blogId: response.id
}
this.save(firstNote)
this.confirmPublish(firstNote)
})
.catch((error) => {
console.error(error)
this.confirmPublishError()
})
}
confirmPublishError () {
const { remote } = electron
const { dialog } = remote
const alertError = {
type: 'warning',
message: 'Publish Failed',
detail: 'Check and update your blog setting and try again.',
buttons: ['Confirm']
}
dialog.showMessageBox(remote.getCurrentWindow(), alertError)
}
confirmPublish (note) {
const buttonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Publish Succeeded',
detail: `${note.title} is published at ${note.blog.blogLink}`,
buttons: ['Confirm', 'Open Blog']
})
if (buttonIndex === 1) {
this.openBlog(note)
}
}
openBlog (note) {
const { shell } = electron
shell.openExternal(note.blog.blogLink)
}
importFromFile () {
const options = {
filters: [
@@ -844,13 +996,13 @@ class NoteList extends React.Component {
value={config.sortBy}
onChange={(e) => this.handleSortByChange(e)}
>
<option title='Sort by update time' value='UPDATED_AT'>Updated</option>
<option title='Sort by create time' value='CREATED_AT'>Created</option>
<option title='Sort alphabetically' value='ALPHABETICAL'>Alphabetically</option>
<option title='Sort by update time' value='UPDATED_AT'>{i18n.__('Updated')}</option>
<option title='Sort by create time' value='CREATED_AT'>{i18n.__('Created')}</option>
<option title='Sort alphabetically' value='ALPHABETICAL'>{i18n.__('Alphabetically')}</option>
</select>
</div>
<div styleName='control-button-area'>
<button title='Default View' styleName={config.listStyle === 'DEFAULT'
<button title={i18n.__('Default View')} styleName={config.listStyle === 'DEFAULT'
? 'control-button--active'
: 'control-button'
}
@@ -858,7 +1010,7 @@ class NoteList extends React.Component {
>
<img styleName='iconTag' src='../resources/icon/icon-column.svg' />
</button>
<button title='Compressed View' styleName={config.listStyle === 'SMALL'
<button title={i18n.__('Compressed View')} styleName={config.listStyle === 'SMALL'
? 'control-button--active'
: 'control-button'
}

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SwitchButton.styl'
import i18n from 'browser/lib/i18n'
const ListButton = ({
onClick, isTagActive
@@ -12,7 +13,7 @@ const ListButton = ({
: '../resources/icon/icon-list-active.svg'
}
/>
<span styleName='tooltip'>Notes</span>
<span styleName='tooltip'>{i18n.__('Notes')}</span>
</button>
)

View File

@@ -2,13 +2,14 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './PreferenceButton.styl'
import i18n from 'browser/lib/i18n'
const PreferenceButton = ({
onClick
}) => (
<button styleName='top-menu-preference' onClick={(e) => onClick(e)}>
<img styleName='iconTag' src='../resources/icon/icon-setting.svg' />
<span styleName='tooltip'>Preferences</span>
<span styleName='tooltip'>{i18n.__('Preferences')}</span>
</button>
)

View File

@@ -9,6 +9,8 @@ import RenameFolderModal from 'browser/main/modals/RenameFolderModal'
import dataApi from 'browser/main/lib/dataApi'
import StorageItemChild from 'browser/components/StorageItem'
import _ from 'lodash'
import { SortableElement } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
const { remote } = require('electron')
const { Menu, dialog } = remote
@@ -43,9 +45,9 @@ class StorageItem extends React.Component {
handleUnlinkStorageClick (e) {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Unlink Storage',
detail: 'This work will just detatches a storage from Boostnote. (Any data won\'t be deleted.)',
buttons: ['Confirm', 'Cancel']
message: i18n.__('Unlink Storage'),
detail: i18n.__('This work will just detatches a storage from Boostnote. (Any data won\'t be deleted.)'),
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
})
if (index === 0) {
@@ -159,9 +161,9 @@ class StorageItem extends React.Component {
handleFolderDeleteClick (e, folder) {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Delete Folder',
detail: 'This will delete all notes in the folder and can not be undone.',
buttons: ['Confirm', 'Cancel']
message: i18n.__('Delete Folder'),
detail: i18n.__('This will delete all notes in the folder and can not be undone.'),
buttons: [i18n.__('Confirm'), i18n.__('Cancel')]
})
if (index === 0) {
@@ -191,33 +193,16 @@ class StorageItem extends React.Component {
dropNote (storage, folder, dispatch, location, noteData) {
noteData = noteData.filter((note) => folder.key !== note.folder)
if (noteData.length === 0) return
const newNoteData = noteData.map((note) => Object.assign({}, note, {storage: storage, folder: folder.key}))
Promise.all(
newNoteData.map((note) => dataApi.createNote(storage.key, note))
noteData.map((note) => dataApi.moveNote(note.storage, note.key, storage.key, folder.key))
)
.then((createdNoteData) => {
createdNoteData.forEach((note) => {
createdNoteData.forEach((newNote) => {
dispatch({
type: 'UPDATE_NOTE',
note: note
})
})
})
.catch((err) => {
console.error(`error on create notes: ${err}`)
})
.then(() => {
return Promise.all(
noteData.map((note) => dataApi.deleteNote(note.storage, note.key))
)
})
.then((deletedNoteData) => {
deletedNoteData.forEach((note) => {
dispatch({
type: 'DELETE_NOTE',
storageKey: note.storageKey,
noteKey: note.noteKey
type: 'MOVE_NOTE',
originNote: noteData.find((note) => note.content === newNote.content),
note: newNote
})
})
})
@@ -236,7 +221,8 @@ class StorageItem extends React.Component {
render () {
const { storage, location, isFolded, data, dispatch } = this.props
const { folderNoteMap, trashedSet } = data
const folderList = storage.folders.map((folder) => {
const SortableStorageItemChild = SortableElement(StorageItemChild)
const folderList = storage.folders.map((folder, index) => {
const isActive = !!(location.pathname.match(new RegExp('\/storages\/' + storage.key + '\/folders\/' + folder.key)))
const noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
@@ -250,8 +236,9 @@ class StorageItem extends React.Component {
noteCount = noteSet.size - trashedNoteCount
}
return (
<StorageItemChild
<SortableStorageItemChild
key={folder.key}
index={index}
isActive={isActive}
handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)}
handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)}
@@ -273,9 +260,9 @@ class StorageItem extends React.Component {
key={storage.key}
>
<div styleName={isActive
? 'header--active'
: 'header'
}
? 'header--active'
: 'header'
}
onContextMenu={(e) => this.handleHeaderContextMenu(e)}
>
<button styleName='header-toggleButton'
@@ -284,7 +271,7 @@ class StorageItem extends React.Component {
<img src={this.state.isOpen
? '../resources/icon/icon-down.svg'
: '../resources/icon/icon-right.svg'
}
}
/>
</button>

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types'
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './SwitchButton.styl'
import i18n from 'browser/lib/i18n'
const TagButton = ({
onClick, isTagActive
@@ -12,7 +13,7 @@ const TagButton = ({
: '../resources/icon/icon-tag.svg'
}
/>
<span styleName='tooltip'>Tags</span>
<span styleName='tooltip'>{i18n.__('Tags')}</span>
</button>
)

View File

@@ -17,6 +17,7 @@ import EventEmitter from 'browser/main/lib/eventEmitter'
import PreferenceButton from './PreferenceButton'
import ListButton from './ListButton'
import TagButton from './TagButton'
import {SortableContainer} from 'react-sortable-hoc'
class SideNav extends React.Component {
// TODO: should not use electron stuff v0.7
@@ -68,6 +69,17 @@ class SideNav extends React.Component {
router.push('/alltags')
}
onSortEnd (storage) {
return ({oldIndex, newIndex}) => {
const { dispatch } = this.props
dataApi
.reorderFolder(storage.key, oldIndex, newIndex)
.then((data) => {
dispatch({ type: 'REORDER_FOLDER', storage: data.storage })
})
}
}
SideNavComponent (isFolded, storageList) {
const { location, data } = this.props
@@ -117,9 +129,9 @@ class SideNav extends React.Component {
tagListComponent () {
const { data, location } = this.props
const tagList = data.tagNoteMap.map((tag, name) => {
const tagList = _.sortBy(data.tagNoteMap.map((tag, name) => {
return { name, size: tag.size }
})
}), ['name'])
return (
tagList.map(tag => {
return (
@@ -148,10 +160,8 @@ class SideNav extends React.Component {
emptyTrash (entries) {
const { dispatch } = this.props
const deletionPromises = entries.map((storageAndNoteKey) => {
const storageKey = storageAndNoteKey.split('-')[0]
const noteKey = storageAndNoteKey.split('-')[1]
return dataApi.deleteNote(storageKey, noteKey)
const deletionPromises = entries.map((note) => {
return dataApi.deleteNote(note.storage, note.key)
})
Promise.all(deletionPromises)
.then((arrayOfStorageAndNoteKeys) => {
@@ -167,9 +177,9 @@ class SideNav extends React.Component {
handleFilterButtonContextMenu (event) {
const { data } = this.props
const entries = data.trashedSet.toJS()
const trashedNotes = data.trashedSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey))
const menu = Menu.buildFromTemplate([
{ label: 'Empty Trash', click: () => this.emptyTrash(entries) }
{ label: 'Empty Trash', click: () => this.emptyTrash(trashedNotes) }
])
menu.popup()
}
@@ -180,13 +190,16 @@ class SideNav extends React.Component {
const isFolded = config.isSideNavFolded
const storageList = data.storageMap.map((storage, key) => {
return <StorageItem
const SortableStorageItem = SortableContainer(StorageItem)
return <SortableStorageItem
key={storage.key}
storage={storage}
data={data}
location={location}
isFolded={isFolded}
dispatch={dispatch}
onSortEnd={this.onSortEnd.bind(this)(storage)}
useDragHandle
/>
})
const style = {}

View File

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

View File

@@ -3,6 +3,7 @@ import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './StatusBar.styl'
import ZoomManager from 'browser/main/lib/ZoomManager'
import i18n from 'browser/lib/i18n'
const electron = require('electron')
const { remote, ipcRenderer } = electron
@@ -14,9 +15,9 @@ class StatusBar extends React.Component {
updateApp () {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Update Boostnote',
detail: 'New Boostnote is ready to be installed.',
buttons: ['Restart & Install', 'Not Now']
message: i18n.__('Update Boostnote'),
detail: i18n.__('New Boostnote is ready to be installed.'),
buttons: [i18n.__('Restart & Install'), i18n.__('Not Now')]
})
if (index === 0) {
@@ -62,7 +63,7 @@ class StatusBar extends React.Component {
{status.updateReady
? <button onClick={this.updateApp} styleName='update'>
<i styleName='update-icon' className='fa fa-cloud-download' /> Ready to Update!
<i styleName='update-icon' className='fa fa-cloud-download' /> {i18n.__('Ready to Update!')}
</button>
: null
}

View File

@@ -40,6 +40,32 @@ $control-height = 34px
padding-bottom 2px
background-color $ui-noteList-backgroundColor
.control-search-input-clear
height 16px
width 16px
position absolute
right 40px
top 10px
z-index 300
border none
background-color transparent
color #999
&:hover .control-search-input-clear-tooltip
opacity 1
.control-search-input-clear-tooltip
tooltip()
position fixed
pointer-events none
top 50px
left 433px
z-index 200
padding 5px
line-height normal
border-radius 2px
opacity 0
transition 0.1s
.control-search-optionList
position fixed
z-index 200
@@ -207,4 +233,4 @@ body[data-theme="solarized-dark"]
background-color $ui-solarized-dark-noteList-backgroundColor
input
background-color $ui-solarized-dark-noteList-backgroundColor
color $ui-solarized-dark-text-color
color $ui-solarized-dark-text-color

View File

@@ -5,6 +5,7 @@ import styles from './TopBar.styl'
import _ from 'lodash'
import ee from 'browser/main/lib/eventEmitter'
import NewNoteButton from 'browser/main/NewNoteButton'
import i18n from 'browser/lib/i18n'
class TopBar extends React.Component {
constructor (props) {
@@ -36,6 +37,17 @@ class TopBar extends React.Component {
ee.off('code:init', this.codeInitHandler)
}
handleSearchClearButton (e) {
const { router } = this.context
this.setState({
search: '',
isSearching: false
})
this.refs.search.childNodes[0].blur
router.push('/searched')
e.preventDefault()
}
handleKeyDown (e) {
// reset states
this.setState({
@@ -43,6 +55,23 @@ class TopBar extends React.Component {
isIME: false
})
// Clear search on ESC
if (e.keyCode === 27) {
return this.handleSearchClearButton(e)
}
// Next note on DOWN key
if (e.keyCode === 40) {
ee.emit('list:next')
e.preventDefault()
}
// Prev note on UP key
if (e.keyCode === 38) {
ee.emit('list:prior')
e.preventDefault()
}
// When the key is an alphabet, del, enter or ctr
if (e.keyCode <= 90 || e.keyCode >= 186 && e.keyCode <= 222) {
this.setState({
@@ -114,10 +143,12 @@ class TopBar extends React.Component {
}
handleOnSearchFocus () {
const el = this.refs.search.childNodes[0]
if (this.state.isSearching) {
this.refs.search.childNodes[0].blur()
el.blur()
} else {
this.refs.search.childNodes[0].focus()
el.focus()
el.setSelectionRange(0, el.value.length)
}
}
@@ -146,19 +177,19 @@ class TopBar extends React.Component {
onChange={(e) => this.handleSearchChange(e)}
onKeyDown={(e) => this.handleKeyDown(e)}
onKeyUp={(e) => this.handleKeyUp(e)}
placeholder='Search'
placeholder={i18n.__('Search')}
type='text'
className='searchInput'
/>
{this.state.search !== '' &&
<button styleName='control-search-input-clear'
onClick={(e) => this.handleSearchClearButton(e)}
>
<i className='fa fa-fw fa-times' />
<span styleName='control-search-input-clear-tooltip'>{i18n.__('Clear Search')}</span>
</button>
}
</div>
{this.state.search > 0 &&
<button styleName='left-search-clearButton'
onClick={(e) => this.handleSearchClearButton(e)}
>
<i className='fa fa-times' />
</button>
}
</div>
</div>
{location.pathname === '/trashed' ? ''

View File

@@ -8,6 +8,7 @@ import { Router, Route, IndexRoute, IndexRedirect, hashHistory } from 'react-rou
import { syncHistoryWithStore } from 'react-router-redux'
require('./lib/ipcClient')
require('../lib/customMeta')
import i18n from 'browser/lib/i18n'
const electron = require('electron')
@@ -46,9 +47,9 @@ function notify (...args) {
function updateApp () {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Update Boostnote',
detail: 'New Boostnote is ready to be installed.',
buttons: ['Restart & Install', 'Not Now']
message: i18n.__('Update Boostnote'),
detail: i18n.__('New Boostnote is ready to be installed.'),
buttons: [i18n.__('Restart & Install'), i18n.__('Not Now')]
})
if (index === 0) {

View File

@@ -1,5 +1,6 @@
import _ from 'lodash'
import RcParser from 'browser/lib/RcParser'
import i18n from 'browser/lib/i18n'
const OSX = global.process.platform === 'darwin'
const win = global.process.platform === 'win32'
@@ -21,6 +22,7 @@ export const DEFAULT_CONFIG = {
toggleMain: OSX ? 'Cmd + Alt + L' : 'Super + Alt + E'
},
ui: {
language: 'en',
theme: 'default',
showCopyNotification: true,
disableDirectWrite: false,
@@ -48,7 +50,16 @@ export const DEFAULT_CONFIG = {
latexInlineClose: '$',
latexBlockOpen: '$$',
latexBlockClose: '$$',
scrollPastEnd: false
scrollPastEnd: false,
smartQuotes: true
},
blog: {
type: 'wordpress', // Available value: wordpress, add more types in the future plz
address: 'http://wordpress.com/wp-json',
authMethod: 'JWT', // Available value: JWT, USER
token: '',
username: '',
password: ''
}
}
@@ -124,6 +135,36 @@ function set (updates) {
document.body.setAttribute('data-theme', 'default')
}
if (newConfig.ui.language === 'sq') {
i18n.setLocale('sq')
} else if (newConfig.ui.language === 'zh-CN') {
i18n.setLocale('zh-CN')
} else if (newConfig.ui.language === 'zh-TW') {
i18n.setLocale('zh-TW')
} else if (newConfig.ui.language === 'da') {
i18n.setLocale('da')
} else if (newConfig.ui.language === 'fr') {
i18n.setLocale('fr')
} else if (newConfig.ui.language === 'de') {
i18n.setLocale('de')
} else if (newConfig.ui.language === 'ja') {
i18n.setLocale('ja')
} else if (newConfig.ui.language === 'ko') {
i18n.setLocale('ko')
} else if (newConfig.ui.language === 'no') {
i18n.setLocale('no')
} else if (newConfig.ui.language === 'pl') {
i18n.setLocale('pl')
} else if (newConfig.ui.language === 'pt') {
i18n.setLocale('pt')
} else if (newConfig.ui.language === 'ru') {
i18n.setLocale('ru')
} else if (newConfig.ui.language === 'es') {
i18n.setLocale('es')
} else {
i18n.setLocale('en')
}
let editorTheme = document.getElementById('editorTheme')
if (editorTheme == null) {
editorTheme = document.createElement('link')
@@ -151,6 +192,7 @@ function set (updates) {
function assignConfigValues (originalConfig, rcConfig) {
const config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey)
config.blog = Object.assign({}, DEFAULT_CONFIG.blog, originalConfig.blog, rcConfig.blog)
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui)
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor)
config.preview = Object.assign({}, DEFAULT_CONFIG.preview, originalConfig.preview, rcConfig.preview)

View File

@@ -3,19 +3,20 @@ const path = require('path')
const { findStorage } = require('browser/lib/findStorage')
/**
* @description To copy an image and return the path.
* @description Copy an image and return the path.
* @param {String} filePath
* @param {String} storageKey
* @return {String} an image path
* @param {Boolean} rename create new filename or leave the old one
* @return {Promise<any>} an image path
*/
function copyImage (filePath, storageKey) {
function copyImage (filePath, storageKey, rename = true) {
return new Promise((resolve, reject) => {
try {
const targetStorage = findStorage(storageKey)
const inputImage = fs.createReadStream(filePath)
const imageExt = path.extname(filePath)
const imageName = Math.random().toString(36).slice(-16)
const imageName = rename ? Math.random().toString(36).slice(-16) : path.basename(filePath, imageExt)
const basename = `${imageName}${imageExt}`
const imageDir = path.join(targetStorage.path, 'images')
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)

View File

@@ -52,12 +52,12 @@ function createNote (storageKey, input) {
return storage
})
.then(function saveNote (storage) {
let key = keygen()
let key = keygen(true)
let isUnique = false
while (!isUnique) {
try {
sander.statSync(path.join(storage.path, 'notes', key + '.cson'))
key = keygen()
key = keygen(true)
} catch (err) {
if (err.code === 'ENOENT') {
isUnique = true

View File

@@ -4,7 +4,7 @@ import {findStorage} from 'browser/lib/findStorage'
const fs = require('fs')
const path = require('path')
const LOCAL_STORED_REGEX = /!\[(.*?)\]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
const LOCAL_STORED_REGEX = /!\[(.*?)]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
const IMAGES_FOLDER_NAME = 'images'
/**

View File

@@ -1,10 +1,12 @@
const resolveStorageData = require('./resolveStorageData')
const _ = require('lodash')
const path = require('path')
const fs = require('fs')
const CSON = require('@rokt33r/season')
const keygen = require('browser/lib/keygen')
const sander = require('sander')
const { findStorage } = require('browser/lib/findStorage')
const copyImage = require('./copyImage')
function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
let oldStorage, newStorage
@@ -37,12 +39,12 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
return resolveStorageData(newStorage)
.then(function findNewNoteKey (_newStorage) {
newStorage = _newStorage
newNoteKey = keygen()
newNoteKey = keygen(true)
let isUnique = false
while (!isUnique) {
try {
sander.statSync(path.join(newStorage.path, 'notes', newNoteKey + '.cson'))
newNoteKey = keygen()
newNoteKey = keygen(true)
} catch (err) {
if (err.code === 'ENOENT') {
isUnique = true
@@ -65,6 +67,27 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
return noteData
})
.then(function moveImages (noteData) {
const searchImagesRegex = /!\[.*?]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
let match = searchImagesRegex.exec(noteData.content)
const moveTasks = []
while (match != null) {
const [, filename] = match
const oldPath = path.join(oldStorage.path, 'images', filename)
moveTasks.push(
copyImage(oldPath, noteData.storage, false)
.then(() => {
fs.unlinkSync(oldPath)
})
)
// find next occurence
match = searchImagesRegex.exec(noteData.content)
}
return Promise.all(moveTasks).then(() => noteData)
})
.then(function writeAndReturn (noteData) {
CSON.writeFileSync(path.join(newStorage.path, 'notes', noteData.key + '.cson'), _.omit(noteData, ['key', 'storage']))
return noteData

View File

@@ -30,6 +30,9 @@ function validateInput (input) {
validatedInput.isPinned = !!input.isPinned
}
if (!_.isNil(input.blog)) {
validatedInput.blog = input.blog
}
validatedInput.type = input.type
switch (input.type) {
case 'MARKDOWN_NOTE':

View File

@@ -6,6 +6,7 @@ import { hashHistory } from 'react-router'
import ee from 'browser/main/lib/eventEmitter'
import ModalEscButton from 'browser/components/ModalEscButton'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import i18n from 'browser/lib/i18n'
class NewNoteModal extends React.Component {
constructor (props) {
@@ -35,7 +36,7 @@ class NewNoteModal extends React.Component {
content: ''
})
.then((note) => {
const noteHash = `${note.storage}-${note.key}`
const noteHash = note.key
dispatch({
type: 'UPDATE_NOTE',
note: note
@@ -75,7 +76,7 @@ class NewNoteModal extends React.Component {
}]
})
.then((note) => {
const noteHash = `${note.storage}-${note.key}`
const noteHash = note.key
dispatch({
type: 'UPDATE_NOTE',
note: note
@@ -122,8 +123,8 @@ class NewNoteModal extends React.Component {
<i styleName='control-button-icon'
className='fa fa-file-text-o'
/><br />
<span styleName='control-button-label'>Markdown Note</span><br />
<span styleName='control-button-description'>This format is for creating text documents. Checklists, code blocks and Latex blocks are available.</span>
<span styleName='control-button-label'>{i18n.__('Markdown Note')}</span><br />
<span styleName='control-button-description'>{i18n.__('This format is for creating text documents. Checklists, code blocks and Latex blocks are available.')}</span>
</button>
<button styleName='control-button'
@@ -134,13 +135,13 @@ class NewNoteModal extends React.Component {
<i styleName='control-button-icon'
className='fa fa-code'
/><br />
<span styleName='control-button-label'>Snippet Note</span><br />
<span styleName='control-button-description'>This format is for creating code snippets. Multiple snippets can be grouped into a single note.
<span styleName='control-button-label'>{i18n.__('Snippet Note')}</span><br />
<span styleName='control-button-description'>{i18n.__('This format is for creating code snippets. Multiple snippets can be grouped into a single note.')}
</span>
</button>
</div>
<div styleName='description'><i className='fa fa-arrows-h' /> Tab to switch format</div>
<div styleName='description'><i className='fa fa-arrows-h' />{i18n.__('Tab to switch format')}</div>
</div>
)

View File

@@ -0,0 +1,199 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './ConfigTab.styl'
import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
import PropTypes from 'prop-types'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
const electron = require('electron')
const { shell } = electron
const ipc = electron.ipcRenderer
class Blog extends React.Component {
constructor (props) {
super(props)
this.state = {
config: props.config,
BlogAlert: null
}
}
handleLinkClick (e) {
shell.openExternal(e.currentTarget.href)
e.preventDefault()
}
clearMessage () {
_.debounce(() => {
this.setState({
BlogAlert: null
})
}, 2000)()
}
componentDidMount () {
this.handleSettingDone = () => {
this.setState({BlogAlert: {
type: 'success',
message: i18n.__('Successfully applied!')
}})
}
this.handleSettingError = (err) => {
this.setState({BlogAlert: {
type: 'error',
message: err.message != null ? err.message : 'Error occurs!'
}})
}
this.oldBlog = this.state.config.blog
ipc.addListener('APP_SETTING_DONE', this.handleSettingDone)
ipc.addListener('APP_SETTING_ERROR', this.handleSettingError)
}
handleBlogChange (e) {
const { config } = this.state
config.blog = {
password: !_.isNil(this.refs.passwordInput) ? this.refs.passwordInput.value : config.blog.password,
username: !_.isNil(this.refs.usernameInput) ? this.refs.usernameInput.value : config.blog.username,
token: !_.isNil(this.refs.tokenInput) ? this.refs.tokenInput.value : config.blog.token,
authMethod: this.refs.authMethodDropdown.value,
address: this.refs.addressInput.value,
type: this.refs.typeDropdown.value
}
this.setState({
config
})
if (_.isEqual(this.oldBlog, config.blog)) {
this.props.haveToSave()
} else {
this.props.haveToSave({
tab: 'Blog',
type: 'warning',
message: 'You have to save!'
})
}
}
handleSaveButtonClick (e) {
const newConfig = {
blog: this.state.config.blog
}
ConfigManager.set(newConfig)
store.dispatch({
type: 'SET_UI',
config: newConfig
})
this.clearMessage()
this.props.haveToSave()
}
render () {
const {config, BlogAlert} = this.state
const blogAlertElement = BlogAlert != null
? <p className={`alert ${BlogAlert.type}`}>
{BlogAlert.message}
</p>
: null
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>{i18n.__('Blog')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Blog Type')}
</div>
<div styleName='group-section-control'>
<select
value={config.blog.type}
ref='typeDropdown'
onChange={(e) => this.handleBlogChange(e)}
>
<option value='wordpress' key='wordpress'>wordpress</option>
</select>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Blog Address')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
ref='addressInput'
value={config.blog.address}
type='text'
/>
</div>
</div>
<div styleName='group-control'>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}
</button>
{blogAlertElement}
</div>
</div>
<div styleName='group-header2'>{i18n.__('Auth')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
{i18n.__('Authentication Method')}
</div>
<div styleName='group-section-control'>
<select
value={config.blog.authMethod}
ref='authMethodDropdown'
onChange={(e) => this.handleBlogChange(e)}
>
<option value='JWT' key='JWT'>{i18n.__('JWT')}</option>
<option value='USER' key='USER'>{i18n.__('USER')}</option>
</select>
</div>
</div>
{ config.blog.authMethod === 'JWT' &&
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Token')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
ref='tokenInput'
value={config.blog.token}
type='text' />
</div>
</div>
}
{ config.blog.authMethod === 'USER' &&
<div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('UserName')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
ref='usernameInput'
value={config.blog.username}
type='text' />
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>{i18n.__('Password')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleBlogChange(e)}
ref='passwordInput'
value={config.blog.password}
type='password' />
</div>
</div>
</div>
}
</div>
)
}
}
Blog.propTypes = {
dispatch: PropTypes.func,
haveToSave: PropTypes.func
}
export default CSSModules(Blog, styles)

View File

@@ -1,6 +1,7 @@
import React from 'react'
import CSSModules from 'browser/lib/CSSModules'
import styles from './Crowdfunding.styl'
import i18n from 'browser/lib/i18n'
const electron = require('electron')
const { shell } = electron
@@ -21,22 +22,22 @@ class Crowdfunding extends React.Component {
render () {
return (
<div styleName='root'>
<div styleName='header'>Crowdfunding</div>
<p>Dear everyone,</p>
<div styleName='header'>{i18n.__('Crowdfunding')}</div>
<p>{i18n.__('Dear everyone,')}</p>
<br />
<p>Thank you for using Boostnote!</p>
<p>Boostnote is used in about 200 different countries and regions by an awesome community of developers.</p>
<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 />
<p>To continue supporting this growth, and to satisfy community expectations,</p>
<p>we would like to invest more time and resources in this project.</p>
<p>{i18n.__('To continue supporting this growth, and to satisfy community expectations,')}</p>
<p>{i18n.__('we would like to invest more time and resources in this project.')}</p>
<br />
<p>If you like this project and see its potential, you can help by supporting us on OpenCollective!</p>
<p>{i18n.__('If you like this project and see its potential, you can help by supporting us on OpenCollective!')}</p>
<br />
<p>Thanks,</p>
<p>Boostnote maintainers</p>
<p>{i18n.__('Thanks,')}</p>
<p>{i18n.__('Boostnote maintainers')}</p>
<br />
<button styleName='cf-link'>
<a href='https://opencollective.com/boostnoteio' onClick={(e) => this.handleLinkClick(e)}>Support via OpenCollective</a>
<a href='https://opencollective.com/boostnoteio' onClick={(e) => this.handleLinkClick(e)}>{i18n.__('Support via OpenCollective')}</a>
</button>
</div>
)

View File

@@ -7,6 +7,7 @@ import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import { SketchPicker } from 'react-color'
import { SortableElement, SortableHandle } from 'react-sortable-hoc'
import i18n from 'browser/lib/i18n'
class FolderItem extends React.Component {
constructor (props) {
@@ -179,18 +180,18 @@ class FolderItem extends React.Component {
return (
<div styleName='folderItem'>
<div styleName='folderItem-left'>
Are you sure to <span styleName='folderItem-left-danger'>delete</span> this folder?
{i18n.__('Are you sure to ')} <span styleName='folderItem-left-danger'>{i18n.__(' delete')}</span> {i18n.__('this folder?')}
</div>
<div styleName='folderItem-right'>
<button styleName='folderItem-right-dangerButton'
onClick={(e) => this.handleDeleteConfirmButtonClick(e)}
>
Confirm
{i18n.__('Confirm')}
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleCancelButtonClick(e)}
>
Cancel
{i18n.__('Cancel')}
</button>
</div>
</div>
@@ -231,12 +232,12 @@ class FolderItem extends React.Component {
<button styleName='folderItem-right-button'
onClick={(e) => this.handleEditButtonClick(e)}
>
Edit
{i18n.__('Edit')}
</button>
<button styleName='folderItem-right-button'
onClick={(e) => this.handleDeleteButtonClick(e)}
>
Delete
{i18n.__('Delete')}
</button>
</div>
</div>

View File

@@ -5,6 +5,7 @@ import styles from './ConfigTab.styl'
import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
const electron = require('electron')
const ipc = electron.ipcRenderer
@@ -23,7 +24,7 @@ class HotkeyTab extends React.Component {
this.handleSettingDone = () => {
this.setState({keymapAlert: {
type: 'success',
message: 'Successfully applied!'
message: i18n.__('Successfully applied!')
}})
}
this.handleSettingError = (err) => {
@@ -77,7 +78,7 @@ class HotkeyTab extends React.Component {
this.props.haveToSave({
tab: 'Hotkey',
type: 'warning',
message: 'You have to save!'
message: i18n.__('You have to save!')
})
}
}
@@ -102,9 +103,9 @@ class HotkeyTab extends React.Component {
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>Hotkeys</div>
<div styleName='group-header'>{i18n.__('Hotkeys')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Show/Hide Boostnote</div>
<div styleName='group-section-label'>{i18n.__('Show/Hide Boostnote')}</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
onChange={(e) => this.handleHotkeyChange(e)}
@@ -124,7 +125,7 @@ class HotkeyTab extends React.Component {
}
</button>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveButtonClick(e)}>Save
onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}
</button>
{keymapAlertElement}
</div>

View File

@@ -5,6 +5,7 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
import store from 'browser/main/store'
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
const electron = require('electron')
const { shell, remote } = electron
@@ -38,11 +39,11 @@ class InfoTab extends React.Component {
if (!newConfig.amaEnabled) {
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('DISABLE_AMA')
this.setState({
amaMessage: 'We hope we will gain your trust'
amaMessage: i18n.__('We hope we will gain your trust')
})
} else {
this.setState({
amaMessage: 'Thank\'s for trust us'
amaMessage: i18n.__('Thank\'s for trusting us')
})
}
@@ -69,48 +70,48 @@ class InfoTab extends React.Component {
return (
<div styleName='root'>
<div styleName='header--sub'>Community</div>
<div styleName='header--sub'>{i18n.__('Community')}</div>
<div styleName='top'>
<ul styleName='list'>
<li>
<a href='https://boostnote.io/#subscribe'
onClick={(e) => this.handleLinkClick(e)}
>Subscribe to Newsletter</a>
>{i18n.__('Subscribe to Newsletter')}</a>
</li>
<li>
<a href='https://github.com/BoostIO/Boostnote/issues'
onClick={(e) => this.handleLinkClick(e)}
>GitHub</a>
>{i18n.__('GitHub')}</a>
</li>
<li>
<a href='https://boostlog.io/@junp1234'
onClick={(e) => this.handleLinkClick(e)}
>Blog</a>
>{i18n.__('Blog')}</a>
</li>
<li>
<a href='https://www.facebook.com/groups/boostnote'
onClick={(e) => this.handleLinkClick(e)}
>Facebook Group</a>
>{i18n.__('Facebook Group')}</a>
</li>
<li>
<a href='https://twitter.com/boostnoteapp'
onClick={(e) => this.handleLinkClick(e)}
>Twitter</a>
>{i18n.__('Twitter')}</a>
</li>
</ul>
</div>
<hr />
<div styleName='header--sub'>About</div>
<div styleName='header--sub'>{i18n.__('About')}</div>
<div styleName='top'>
<div styleName='icon-space'>
<img styleName='icon' src='../resources/app.png' width='92' height='92' />
<div styleName='icon-right'>
<div styleName='appId'>Boostnote {appVersion}</div>
<div styleName='appId'>{i18n.__('Boostnote')} {appVersion}</div>
<div styleName='description'>
An open source note-taking app made for programmers just like you.
{i18n.__('An open source note-taking app made for programmers just like you.')}
</div>
</div>
</div>
@@ -120,35 +121,35 @@ class InfoTab extends React.Component {
<li>
<a href='https://boostnote.io'
onClick={(e) => this.handleLinkClick(e)}
>Website</a>
>{i18n.__('Website')}</a>
</li>
<li>
<a href='https://github.com/BoostIO/Boostnote/blob/master/docs/build.md'
onClick={(e) => this.handleLinkClick(e)}
>Development</a> : Development configurations for Boostnote.
>{i18n.__('Development')}</a>{i18n.__(' : Development configurations for Boostnote.')}
</li>
<li styleName='cc'>
Copyright (C) 2017 - 2018 BoostIO
{i18n.__('Copyright (C) 2017 - 2018 BoostIO')}
</li>
<li styleName='cc'>
License: GPL v3
{i18n.__('License: GPL v3')}
</li>
</ul>
<hr styleName='separate-line' />
<div styleName='policy'>Analytics</div>
<div>Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.</div>
<div>You can see how it works on <a href='https://github.com/BoostIO/Boostnote' onClick={(e) => this.handleLinkClick(e)}>GitHub</a>.</div>
<div styleName='policy'>{i18n.__('Analytics')}</div>
<div>{i18n.__('Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.')}</div>
<div>{i18n.__('You can see how it works on ')}<a href='https://github.com/BoostIO/Boostnote' onClick={(e) => this.handleLinkClick(e)}>GitHub</a>.</div>
<br />
<div>You can choose to enable or disable this option.</div>
<div>{i18n.__('You can choose to enable or disable this option.')}</div>
<input onChange={(e) => this.handleConfigChange(e)}
checked={this.state.config.amaEnabled}
ref='amaEnabled'
type='checkbox'
/>
Enable analytics to help improve Boostnote<br />
<button styleName='policy-submit' onClick={(e) => this.handleSaveButtonClick(e)}>Save</button>
{i18n.__('Enable analytics to help improve Boostnote')}<br />
<button styleName='policy-submit' onClick={(e) => this.handleSaveButtonClick(e)}>{i18n.__('Save')}</button>
<br />
{this.infoMessage()}
</div>

View File

@@ -6,6 +6,7 @@ import consts from 'browser/lib/consts'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import FolderList from './FolderList'
import i18n from 'browser/lib/i18n'
const { shell, remote } = require('electron')
const { dialog } = remote
@@ -46,9 +47,9 @@ class StorageItem extends React.Component {
handleUnlinkButtonClick (e) {
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'warning',
message: 'Unlink Storage',
detail: 'Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.',
buttons: ['Unlink', 'Cancel']
message: i18n.__('Unlink Storage'),
detail: i18n.__('Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.'),
buttons: [i18n.__('Unlink'), i18n.__('Cancel')]
})
if (index === 0) {
@@ -127,7 +128,7 @@ class StorageItem extends React.Component {
<i className='fa fa-plus' />
<span styleName='header-control-button-tooltip'
style={{left: -20}}
>Add Folder</span>
>{i18n.__('Add Folder')}</span>
</button>
<button styleName='header-control-button'
onClick={(e) => this.handleExternalButtonClick(e)}
@@ -135,7 +136,7 @@ class StorageItem extends React.Component {
<i className='fa fa-external-link' />
<span styleName='header-control-button-tooltip'
style={{left: -50}}
>Open Storage folder</span>
>{i18n.__('Open Storage folder')}</span>
</button>
<button styleName='header-control-button'
onClick={(e) => this.handleUnlinkButtonClick(e)}
@@ -143,7 +144,7 @@ class StorageItem extends React.Component {
<i className='fa fa-unlink' />
<span styleName='header-control-button-tooltip'
style={{left: -10}}
>Unlink</span>
>{i18n.__('Unlink')}</span>
</button>
</div>
</div>

View File

@@ -4,6 +4,7 @@ import CSSModules from 'browser/lib/CSSModules'
import styles from './StoragesTab.styl'
import dataApi from 'browser/main/lib/dataApi'
import StorageItem from './StorageItem'
import i18n from 'browser/lib/i18n'
const electron = require('electron')
const { shell, remote } = electron
@@ -69,16 +70,16 @@ class StoragesTab extends React.Component {
})
return (
<div styleName='list'>
<div styleName='header'>Storages</div>
<div styleName='header'>{i18n.__('Storages')}</div>
{storageList.length > 0
? storageList
: <div styleName='list-empty'>No storage found.</div>
: <div styleName='list-empty'>{i18n.__('No storage found.')}</div>
}
<div styleName='list-control'>
<button styleName='list-control-addStorageButton'
onClick={(e) => this.handleAddStorageButton(e)}
>
<i className='fa fa-plus' /> Add Storage Location
<i className='fa fa-plus' /> {i18n.__('Add Storage Location')}
</button>
</div>
</div>
@@ -140,13 +141,13 @@ class StoragesTab extends React.Component {
return (
<div styleName='addStorage'>
<div styleName='addStorage-header'>Add Storage</div>
<div styleName='addStorage-header'>{i18n.__('Add Storage')}</div>
<div styleName='addStorage-body'>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>
Name
{i18n.__('Name')}
</div>
<div styleName='addStorage-body-section-name'>
<input styleName='addStorage-body-section-name-input'
@@ -158,25 +159,25 @@ class StoragesTab extends React.Component {
</div>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>Type</div>
<div styleName='addStorage-body-section-label'>{i18n.__('Type')}</div>
<div styleName='addStorage-body-section-type'>
<select styleName='addStorage-body-section-type-select'
value={this.state.newStorage.type}
readOnly
>
<option value='FILESYSTEM'>File System</option>
<option value='FILESYSTEM'>{i18n.__('File System')}</option>
</select>
<div styleName='addStorage-body-section-type-description'>
Setting up 3rd-party cloud storage integration:{' '}
{i18n.__('Setting up 3rd-party cloud storage integration:')}{' '}
<a href='https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup'
onClick={(e) => this.handleLinkClick(e)}
>Cloud-Syncing-and-Backup</a>
>{i18n.__('Cloud-Syncing-and-Backup')}</a>
</div>
</div>
</div>
<div styleName='addStorage-body-section'>
<div styleName='addStorage-body-section-label'>Location
<div styleName='addStorage-body-section-label'>{i18n.__('Location')}
</div>
<div styleName='addStorage-body-section-path'>
<input styleName='addStorage-body-section-path-input'
@@ -196,10 +197,10 @@ class StoragesTab extends React.Component {
<div styleName='addStorage-body-control'>
<button styleName='addStorage-body-control-createButton'
onClick={(e) => this.handleAddStorageCreateButton(e)}
>Add</button>
>{i18n.__('Add')}</button>
<button styleName='addStorage-body-control-cancelButton'
onClick={(e) => this.handleAddStorageCancelButton(e)}
>Cancel</button>
>{i18n.__('Cancel')}</button>
</div>
</div>

View File

@@ -9,6 +9,7 @@ import ReactCodeMirror from 'react-codemirror'
import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
const OSX = global.process.platform === 'darwin'
@@ -29,7 +30,7 @@ class UiTab extends React.Component {
this.handleSettingDone = () => {
this.setState({UiAlert: {
type: 'success',
message: 'Successfully applied!'
message: i18n.__('Successfully applied!')
}})
}
this.handleSettingError = (err) => {
@@ -61,6 +62,7 @@ class UiTab extends React.Component {
const newConfig = {
ui: {
theme: this.refs.uiTheme.value,
language: this.refs.uiLanguage.value,
showCopyNotification: this.refs.showCopyNotification.checked,
confirmDeletion: this.refs.confirmDeletion.checked,
disableDirectWrite: this.refs.uiD2w != null
@@ -88,7 +90,8 @@ class UiTab extends React.Component {
latexInlineClose: this.refs.previewLatexInlineClose.value,
latexBlockOpen: this.refs.previewLatexBlockOpen.value,
latexBlockClose: this.refs.previewLatexBlockClose.value,
scrollPastEnd: this.refs.previewScrollPastEnd.checked
scrollPastEnd: this.refs.previewScrollPastEnd.checked,
smartQuotes: this.refs.previewSmartQuotes.checked
}
}
@@ -106,7 +109,7 @@ class UiTab extends React.Component {
this.props.haveToSave({
tab: 'UI',
type: 'warning',
message: 'You have to save!'
message: i18n.__('You have to save!')
})
}
})
@@ -151,22 +154,48 @@ class UiTab extends React.Component {
return (
<div styleName='root'>
<div styleName='group'>
<div styleName='group-header'>Interface</div>
<div styleName='group-header'>{i18n.__('Interface')}</div>
<div styleName='group-section'>
Interface Theme
{i18n.__('Interface Theme')}
<div styleName='group-section-control'>
<select value={config.ui.theme}
onChange={(e) => this.handleUIChange(e)}
ref='uiTheme'
>
<option value='default'>Default</option>
<option value='white'>White</option>
<option value='solarized-dark'>Solarized Dark</option>
<option value='dark'>Dark</option>
<option value='default'>{i18n.__('Default')}</option>
<option value='white'>{i18n.__('White')}</option>
<option value='solarized-dark'>{i18n.__('Solarized Dark')}</option>
<option value='dark'>{i18n.__('Dark')}</option>
</select>
</div>
</div>
<div styleName='group-section'>
{i18n.__('Language')}
<div styleName='group-section-control'>
<select value={config.ui.language}
onChange={(e) => this.handleUIChange(e)}
ref='uiLanguage'
>
<option value='sq'>{i18n.__('Albanian')}</option>
<option value='zh-CN'>{i18n.__('Chinese (zh-CN)')}</option>
<option value='zh-TW'>{i18n.__('Chinese (zh-TW)')}</option>
<option value='da'>{i18n.__('Danish')}</option>
<option value='en'>{i18n.__('English')}</option>
<option value='fr'>{i18n.__('French')}</option>
<option value='de'>{i18n.__('German')}</option>
<option value='ja'>{i18n.__('Japanese')}</option>
<option value='ko'>{i18n.__('Korean')}</option>
<option value='no'>{i18n.__('Norwegian')}</option>
<option value='pl'>{i18n.__('Polish')}</option>
<option value='pt'>{i18n.__('Portuguese')}</option>
<option value='ru'>{i18n.__('Russian')}</option>
<option value='es'>{i18n.__('Spanish')}</option>
</select>
</div>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
@@ -174,7 +203,7 @@ class UiTab extends React.Component {
ref='showCopyNotification'
type='checkbox'
/>&nbsp;
Show &quot;Saved to Clipboard&quot; notification when copying
{i18n.__('Show "Saved to Clipboard" notification when copying')}
</label>
</div>
<div styleName='group-checkBoxSection'>
@@ -184,7 +213,7 @@ class UiTab extends React.Component {
ref='confirmDeletion'
type='checkbox'
/>&nbsp;
Show a confirmation dialog when deleting notes
{i18n.__('Show a confirmation dialog when deleting notes')}
</label>
</div>
{
@@ -206,7 +235,7 @@ class UiTab extends React.Component {
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Theme
{i18n.__('Editor Theme')}
</div>
<div styleName='group-section-control'>
<select value={config.editor.theme}
@@ -226,7 +255,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Font Size
{i18n.__('Editor Font Size')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -239,7 +268,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Font Family
{i18n.__('Editor Font Family')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -252,7 +281,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Indent Style
{i18n.__('Editor Indent Style')}
</div>
<div styleName='group-section-control'>
<select value={config.editor.indentSize}
@@ -268,42 +297,42 @@ class UiTab extends React.Component {
ref='editorIndentType'
onChange={(e) => this.handleUIChange(e)}
>
<option value='space'>Spaces</option>
<option value='tab'>Tabs</option>
<option value='space'>{i18n.__('Spaces')}</option>
<option value='tab'>{i18n.__('Tabs')}</option>
</select>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Switch to Preview
{i18n.__('Switch to Preview')}
</div>
<div styleName='group-section-control'>
<select value={config.editor.switchPreview}
ref='editorSwitchPreview'
onChange={(e) => this.handleUIChange(e)}
>
<option value='BLUR'>When Editor Blurred</option>
<option value='DBL_CLICK'>When Editor Blurred, Edit On Double Click</option>
<option value='RIGHTCLICK'>On Right Click</option>
<option value='BLUR'>{i18n.__('When Editor Blurred')}</option>
<option value='DBL_CLICK'>{i18n.__('When Editor Blurred, Edit On Double Click')}</option>
<option value='RIGHTCLICK'>{i18n.__('On Right Click')}</option>
</select>
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Editor Keymap
{i18n.__('Editor Keymap')}
</div>
<div styleName='group-section-control'>
<select value={config.editor.keyMap}
ref='editorKeyMap'
onChange={(e) => this.handleUIChange(e)}
>
<option value='sublime'>default</option>
<option value='vim'>vim</option>
<option value='emacs'>emacs</option>
<option value='sublime'>{i18n.__('default')}</option>
<option value='vim'>{i18n.__('vim')}</option>
<option value='emacs'>{i18n.__('emacs')}</option>
</select>
<p styleName='note-for-keymap'> Please restart boostnote after you change the keymap</p>
<p styleName='note-for-keymap'>{i18n.__('⚠️ Please restart boostnote after you change the keymap')}</p>
</div>
</div>
@@ -314,7 +343,7 @@ class UiTab extends React.Component {
ref='editorDisplayLineNumbers'
type='checkbox'
/>&nbsp;
Show line numbers in the editor
{i18n.__('Show line numbers in the editor')}
</label>
</div>
@@ -325,7 +354,7 @@ class UiTab extends React.Component {
ref='scrollPastEnd'
type='checkbox'
/>&nbsp;
Allow editor to scroll past the last line
{i18n.__('Allow editor to scroll past the last line')}
</label>
</div>
@@ -336,14 +365,14 @@ class UiTab extends React.Component {
ref='editorFetchUrlTitle'
type='checkbox'
/>&nbsp;
Bring in web page title when pasting URL on editor
{i18n.__('Bring in web page title when pasting URL on editor')}
</label>
</div>
<div styleName='group-header2'>Preview</div>
<div styleName='group-header2'>{i18n.__('Preview')}</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Preview Font Size
{i18n.__('Preview Font Size')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -356,7 +385,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
Preview Font Family
{i18n.__('Preview Font Family')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -368,7 +397,7 @@ class UiTab extends React.Component {
</div>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>Code block Theme</div>
<div styleName='group-section-label'>{i18n.__('Code block Theme')}</div>
<div styleName='group-section-control'>
<select value={config.preview.codeBlockTheme}
ref='previewCodeBlockTheme'
@@ -389,7 +418,7 @@ class UiTab extends React.Component {
ref='previewScrollPastEnd'
type='checkbox'
/>&nbsp;
Allow preview to scroll past the last line
{i18n.__('Allow preview to scroll past the last line')}
</label>
</div>
<div styleName='group-checkBoxSection'>
@@ -399,12 +428,22 @@ class UiTab extends React.Component {
ref='previewLineNumber'
type='checkbox'
/>&nbsp;
Show line numbers for preview code blocks
{i18n.__('Show line numbers for preview code blocks')}
</label>
</div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.preview.smartQuotes}
ref='previewSmartQuotes'
type='checkbox'
/>&nbsp;
Enable smart quotes
</label>
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
LaTeX Inline Open Delimiter
{i18n.__('LaTeX Inline Open Delimiter')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -417,7 +456,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
LaTeX Inline Close Delimiter
{i18n.__('LaTeX Inline Close Delimiter')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -430,7 +469,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
LaTeX Block Open Delimiter
{i18n.__('LaTeX Block Open Delimiter')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -443,7 +482,7 @@ class UiTab extends React.Component {
</div>
<div styleName='group-section'>
<div styleName='group-section-label'>
LaTeX Block Close Delimiter
{i18n.__('LaTeX Block Close Delimiter')}
</div>
<div styleName='group-section-control'>
<input styleName='group-section-control-input'
@@ -457,7 +496,7 @@ class UiTab extends React.Component {
<div styleName='group-control'>
<button styleName='group-control-rightButton'
onClick={(e) => this.handleSaveUIClick(e)}>Save
onClick={(e) => this.handleSaveUIClick(e)}>{i18n.__('Save')}
</button>
{UiAlertElement}
</div>

View File

@@ -6,11 +6,13 @@ import UiTab from './UiTab'
import InfoTab from './InfoTab'
import Crowdfunding from './Crowdfunding'
import StoragesTab from './StoragesTab'
import Blog from './Blog'
import ModalEscButton from 'browser/components/ModalEscButton'
import CSSModules from 'browser/lib/CSSModules'
import styles from './PreferencesModal.styl'
import RealtimeNotification from 'browser/components/RealtimeNotification'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
class Preferences extends React.Component {
constructor (props) {
@@ -19,7 +21,8 @@ class Preferences extends React.Component {
this.state = {
currentTab: 'STORAGES',
UIAlert: '',
HotkeyAlert: ''
HotkeyAlert: '',
BlogAlert: ''
}
}
@@ -75,6 +78,14 @@ class Preferences extends React.Component {
return (
<Crowdfunding />
)
case 'BLOG':
return (
<Blog
dispatch={dispatch}
config={config}
haveToSave={alert => this.setState({BlogAlert: alert})}
/>
)
case 'STORAGES':
default:
return (
@@ -107,11 +118,12 @@ class Preferences extends React.Component {
const content = this.renderContent()
const tabs = [
{target: 'STORAGES', label: 'Storage'},
{target: 'HOTKEY', label: 'Hotkeys', Hotkey: this.state.HotkeyAlert},
{target: 'UI', label: 'Interface', UI: this.state.UIAlert},
{target: 'INFO', label: 'About'},
{target: 'CROWDFUNDING', label: 'Crowdfunding'}
{target: 'STORAGES', label: i18n.__('Storage')},
{target: 'HOTKEY', label: i18n.__('Hotkeys'), Hotkey: this.state.HotkeyAlert},
{target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert},
{target: 'INFO', label: i18n.__('About')},
{target: 'CROWDFUNDING', label: i18n.__('Crowdfunding')},
{target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert}
]
const navButtons = tabs.map((tab) => {
@@ -140,7 +152,7 @@ class Preferences extends React.Component {
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='top-bar'>
<p>Your preferences for Boostnote</p>
<p>{i18n.__('Your preferences for Boostnote')}</p>
</div>
<ModalEscButton handleEscButtonClick={(e) => this.handleEscButtonClick(e)} />
<div styleName='nav'>

View File

@@ -5,6 +5,7 @@ import styles from './RenameFolderModal.styl'
import dataApi from 'browser/main/lib/dataApi'
import store from 'browser/main/store'
import ModalEscButton from 'browser/components/ModalEscButton'
import i18n from 'browser/lib/i18n'
class RenameFolderModal extends React.Component {
constructor (props) {
@@ -72,7 +73,7 @@ class RenameFolderModal extends React.Component {
onKeyDown={(e) => this.handleKeyDown(e)}
>
<div styleName='header'>
<div styleName='title'>Rename Folder</div>
<div styleName='title'>{i18n.__('Rename Folder')}</div>
</div>
<ModalEscButton handleEscButtonClick={(e) => this.handleCloseButtonClick(e)} />

View File

@@ -27,7 +27,7 @@ function data (state = defaultDataMap(), action) {
action.notes.some((note) => {
if (note === undefined) return true
const uniqueKey = note.storage + '-' + note.key
const uniqueKey = note.key
const folderKey = note.storage + '-' + note.folder
state.noteMap.set(uniqueKey, note)
@@ -66,7 +66,7 @@ function data (state = defaultDataMap(), action) {
case 'UPDATE_NOTE':
{
const note = action.note
const uniqueKey = note.storage + '-' + note.key
const uniqueKey = note.key
const folderKey = note.storage + '-' + note.folder
const oldNote = state.noteMap.get(uniqueKey)
@@ -162,9 +162,9 @@ function data (state = defaultDataMap(), action) {
case 'MOVE_NOTE':
{
const originNote = action.originNote
const originKey = originNote.storage + '-' + originNote.key
const originKey = originNote.key
const note = action.note
const uniqueKey = note.storage + '-' + note.key
const uniqueKey = note.key
const folderKey = note.storage + '-' + note.folder
const oldNote = state.noteMap.get(uniqueKey)
@@ -297,7 +297,7 @@ function data (state = defaultDataMap(), action) {
}
case 'DELETE_NOTE':
{
const uniqueKey = action.storageKey + '-' + action.noteKey
const uniqueKey = action.noteKey
const targetNote = state.noteMap.get(uniqueKey)
state = Object.assign({}, state)
@@ -423,7 +423,7 @@ function data (state = defaultDataMap(), action) {
state.folderNoteMap = new Map(state.folderNoteMap)
state.tagNoteMap = new Map(state.tagNoteMap)
action.notes.forEach((note) => {
const uniqueKey = note.storage + '-' + note.key
const uniqueKey = note.key
const folderKey = note.storage + '-' + note.folder
state.noteMap.set(uniqueKey, note)
@@ -483,7 +483,7 @@ function data (state = defaultDataMap(), action) {
state.tagNoteMap = new Map(state.tagNoteMap)
state.starredSet = new Set(state.starredSet)
notes.forEach((note) => {
const noteKey = storage.key + '-' + note.key
const noteKey = note.key
state.noteMap.delete(noteKey)
state.starredSet.delete(noteKey)
note.tags.forEach((tag) => {

View File

@@ -5,7 +5,7 @@ $danger-color = #c9302c
$danger-lighten-color = lighten(#c9302c, 5%)
// Layouts
$statusBar-height = 0px
$statusBar-height = 22px
$sideNav-width = 200px
$sideNav--folded-width = 44px
$topBar-height = 60px
@@ -347,4 +347,4 @@ modalSolarizedDark()
width 100%
background-color $ui-solarized-dark-backgroundColor
overflow hidden
border-radius $modal-border-radius
border-radius $modal-border-radius

86
docs/zh_TW/build.md Normal file
View File

@@ -0,0 +1,86 @@
# 編譯
此文件還提供下列的語言 [日文](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [韓文](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [俄文](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [簡體中文](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [法文](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md) and [德文](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md).
## 環境
* npm: 4.x
* node: 7.x
`$ grunt pre-build``npm v5.x` 有問題,所以只能用 `npm v4.x`
## 開發
我們使用 Webpack HMR 來開發 Boostnote。

在專案根目錄底下執行下列指令,將會以原始設置啟動 Boostnote。
**用 yarn 來安裝必要 packages**
```bash
$ yarn
```
**開始開發**
```
$ yarn run dev-start
```
上述指令同時運行了 `yarn run webpack``yarn run hot`,相當於將這兩個指令在不同的 terminal 中運行。
`webpack` 會同時監控修改過的程式碼,並
The `webpack` will watch for code changes and then apply them automatically.
If the following error occurs: `Failed to load resource: net::ERR_CONNECTION_REFUSED`, please reload Boostnote.
![net::ERR_CONNECTION_REFUSED](https://cloud.githubusercontent.com/assets/11307908/24343004/081e66ae-1279-11e7-8d9e-7f478043d835.png)
> ### Notice
> There are some cases where you have to refresh the app manually.
> 1. When editing a constructor method of a component
> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.)
## Deploy
We use Grunt to automate deployment.
You can build the program by using `grunt`. However, we don't recommend this because the default task includes codesign and authenticode.
So, we've prepared a separate script which just makes an executable file.
This build doesn't work on npm v5.3.0. So you need to use v5.2.0 when you build it.
```
grunt pre-build
```
You will find the executable in the `dist` directory. Note, the auto updater won't work because the app isn't signed.
If you find it necessary, you can use codesign or authenticode with this executable.
## Make own distribution packages (deb, rpm)
Distribution packages are created by exec `grunt build` on Linux platform (e.g. Ubuntu, Fedora).
> Note: You can create both `.deb` and `.rpm` in a single environment.
After installing the supported version of `node` and `npm`, install build dependency packages.
Ubuntu/Debian:
```
$ sudo apt-get install -y rpm fakeroot
```
Fedora:
```
$ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot
```
Then execute `grunt build`.
```
$ grunt build
```
You will find `.deb` and `.rpm` in the `dist` directory.

View File

@@ -0,0 +1,52 @@
(function (mod) {
if (typeof exports === 'object' && typeof module === 'object') { // Common JS
mod(require('../codemirror/lib/codemirror'))
} else if (typeof define === 'function' && define.amd) { // AMD
define(['../codemirror/lib/codemirror'], mod)
} else { // Plain browser env
mod(CodeMirror)
}
})(function (CodeMirror) {
'use strict'
var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/
var emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/
var unorderedListRE = /[*+-]\s/
CodeMirror.commands.boostNewLineAndIndentContinueMarkdownList = function (cm) {
if (cm.getOption('disableInput')) return CodeMirror.Pass
var ranges = cm.listSelections()
var replacements = []
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head
var eolState = cm.getStateAfter(pos.line)
var inList = eolState.list !== false
var inQuote = eolState.quote !== 0
var line = cm.getLine(pos.line)
var match = listRE.exec(line)
if (!ranges[i].empty() || (!inList && !inQuote) || !match || pos.ch < match[2].length - 1) {
cm.execCommand('newlineAndIndent')
return
}
if (emptyListRE.test(line)) {
if (!/>\s*$/.test(line)) {
cm.replaceRange('', {
line: pos.line, ch: 0
}, {
line: pos.line, ch: pos.ch + 1
})
}
replacements[i] = '\n'
} else {
var indent = match[1]
var after = match[5]
var bullet = unorderedListRE.test(match[2]) || match[2].indexOf('>') >= 0
? match[2].replace('x', ' ')
: (parseInt(match[3], 10) + 1) + match[4]
replacements[i] = '\n' + indent + bullet + after
}
}
cm.replaceSelections(replacements)
}
})

View File

@@ -1,6 +1,7 @@
const electron = require('electron')
const BrowserWindow = electron.BrowserWindow
const shell = electron.shell
const ipc = electron.ipcMain
const mainWindow = require('./main-window')
const macOS = process.platform === 'darwin'
@@ -259,6 +260,23 @@ const view = {
]
}
let editorFocused
// Define extra shortcut keys
mainWindow.webContents.on('before-input-event', (event, input) => {
// Synonyms for Search (Find)
if (input.control && input.key === 'f' && input.type === 'keyDown') {
if (!editorFocused) {
mainWindow.webContents.send('top:focus-search')
event.preventDefault()
}
}
})
ipc.on('editor:focused', (event, isFocused) => {
editorFocused = isFocused
})
const window = {
label: 'Window',
submenu: [
@@ -267,6 +285,11 @@ const window = {
accelerator: 'Command+M',
selector: 'performMiniaturize:'
},
{
label: 'Toggle Full Screen',
accelerator: 'Command+Control+F',
selector: 'toggleFullScreen:'
},
{
label: 'Close',
accelerator: 'Command+W',

View File

@@ -78,7 +78,7 @@
<script src="../node_modules/codemirror/keymap/emacs.js"></script>
<script src="../node_modules/codemirror/addon/runmode/runmode.js"></script>
<script src="../node_modules/boost/boostNewLineIndentContinueMarkdownList.js"></script>
<script src="../extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js"></script>
<script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
@@ -97,7 +97,6 @@
</script>
<script src="../node_modules/js-sequence-diagrams/fucknpm/sequence-diagram-min.js"></script>
<script src="../node_modules/katex/dist/katex.min.js"></script>
<script src="../node_modules/react/dist/react.min.js"></script>
<script src="../node_modules/react-dom/dist/react-dom.min.js"></script>
<script src="../node_modules/redux/dist/redux.min.js"></script>

145
locales/da.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

147
locales/de.json Normal file
View File

@@ -0,0 +1,147 @@
{
"Notes": "Notizen",
"Tags": "Tags",
"Preferences": "Einstellungen",
"Make a note": "Notiz erstellen",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "um eine neue Notiz zu erstellen",
"Toggle Mode": "Modus umschalten",
"Trash": "Papierkorb",
"MODIFICATION DATE": "ÄNDERUNGSDATUM",
"Words": "Wörter",
"Letters": "Buchstaben",
"STORAGE": "SPEICHERORT",
"FOLDER": "ORDNER",
"CREATION DATE": "ERSTELLUNGSDATUM",
"NOTE LINK": "NOTIZ LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Deine Boostnote Einstellungen",
"Storages": "Speicherorte",
"Add Storage Location": "Speicherort Hinzufügen",
"Add Folder": "Ordner Hinzufügen",
"Open Storage folder": "Speicherort Öffnen",
"Unlink": "Verknüpfung aufheben",
"Edit": "Bearbeiten",
"Delete": "Löschen",
"Interface": "Interface",
"Interface Theme": "Interface-Thema",
"Default": "Standard",
"White": "Weiß",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Bestätigungsdialog beim Löschen von Notizen anzeigen",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Schriftgröße",
"Editor Font Family": "Editor Schriftart",
"Editor Indent Style": "Editor Einrückestil",
"Spaces": "Leerzeichen",
"Tabs": "Tabs",
"Switch to Preview": "Zur Vorschau wechseln",
"When Editor Blurred": "Wenn Editor verschwommen ist",
"When Editor Blurred, Edit On Double Click": "Mit Doppelklick bearbeiten, wenn Editor verschwommen ist",
"On Right Click": "Mit Rechtsklcik",
"Editor Keymap": "Editor Tastenbelegung",
"default": "standard",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Boostnote nach Änderung der Tastenbelegung neu starten",
"Show line numbers in the editor": "Zeilennummern im Editor anzeigen",
"Allow editor to scroll past the last line": "Editor das Scrollen über das Ende hinaus erlauben",
"Bring in web page title when pasting URL on editor": "Websitetitel beim Einfügen in den Editor anzeigen",
"Preview": "Vorschau",
"Preview Font Size": "Vorschau Schriftgröße",
"Preview Font Family": "Vorschau Schriftart",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Vorschau das Scrollen über das Ende hinaus erlauben",
"Show line numbers for preview code blocks": "Zeilennummern in Vorschau-Code-Blöcken anzeigen",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Newsletter abonnieren",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "Eine OpenSource-Notizapp für Programmierer wie du und ich.",
"Website": "Website",
"Development": "Entwicklung",
" : Development configurations for Boostnote.": " : Entwicklungseinstellungen für Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 sammelt anonyme Daten, um die App zu verbessern und nicht persönliche Informationen, wie z.B. der Inhalt deiner Notizen.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "Du kannst wählen, ob du diese Option aktivierst oder daektivierst.",
"Enable analytics to help improve Boostnote": "Datenerhebung zur Verbesserung von Boostnote aktivieren",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote Betreuer",
"Support via OpenCollective": "Support per OpenCollective",
"Language": "Sprache",
"English": "Englisch",
"German": "Deutsch",
"French": "Französisch",
"Show \"Saved to Clipboard\" notification when copying": "\"Auf Clipboard gespeichert\" Benachrichtigungen beim kopieren anzeigen",
"All Notes": "Alle Notizen",
"Starred": "Starred",
"Are you sure to ": "Sind sie sicher ",
" delete": " zu löschen",
"this folder?": "diesen Ordner?",
"Confirm": "Bestätigen",
"Cancel": "Abbrechen",
"Markdown Note": "Markdown Notiz",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "Dieses Format dient der Erstellung von Text Dokumenten. Checklisten, Code-Blöcke und Latex-Blöcke sind verfügbar.",
"Snippet Note": "Snippet Notiz",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "Dieses Format dient der Erstellung von Code-Schnipslen. Mehrere snippets können zu einer Notiz zusammengefasst werden.",
"Tab to switch format": "Tab drücken, um das Format zu wechseln",
"Updated": "Bearbeitet",
"Created": "Erstellt",
"Alphabetically": "Alphabetisch",
"Default View": "Standardansicht",
"Compressed View": "Komprimierte Ansicht",
"Search": "Suchen",
"Blog Type": "Blog-Typ",
"Blog Address": "Blog Adresse",
"Save": "Speichern",
"Auth": "Authentifizierung",
"Authentication Method": "Authentifizierungsmethode",
"JWT": "JWT",
"USER": "BENUTZER",
"Token": "Token",
"Storage": "Speicherorte",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Boostnote anzeigen/verstecken",
"Restore": "Restore",
"Permanent Delete": "Dauerhaft Löschen",
"Confirm note deletion": "Löschen bestätigen",
"This will permanently remove this note.": "Notiz wird dauerhaft gelöscht",
"You have to save!": "Es muss gespeichert werden",
"Albanian": "Albanisch",
"Danish": "Dänisch",
"Japanese": "Japanisch",
"Korean": "Koreanisch",
"Norwegian": "Norwegisch",
"Polish": "Polnish",
"Portuguese": "Portugiesisch",
"Spanish": "Spanisch",
"Chinese (zh-CN)": "Chinesisch (zh-CN)",
"Chinese (zh-TW)": "Chinesisch (zh-TW)",
"Successfully applied!": "Erfolgreich angewendet!",
"UserName": "UserName",
"Password": "Password",
"Russian": "Russian"
}

147
locales/en.json Normal file
View File

@@ -0,0 +1,147 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"UserName": "UserName",
"Password": "Password",
"Russian": "Russian"
}

145
locales/es.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

143
locales/fr.json Normal file
View File

@@ -0,0 +1,143 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Préférences",
"Make a note": "Créer une note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "pour créer une nouvelle note",
"Toggle Mode": "Toggle Mode",
"Trash": "Poubelle",
"MODIFICATION DATE": "DATE DE MODIFICATION",
"Words": "Mots",
"Letters": "Lettres",
"STORAGE": "STOCKAGE",
"FOLDER": "DOSSIER",
"CREATION DATE": "DATE DE CREATION",
"NOTE LINK": "LIEN DE LA NOTE",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Imprimer",
"Your preferences for Boostnote": "Vos préférences pour Boostnote",
"Storages": "Stockages",
"Add Storage Location": "Ajouter un espace de stockage",
"Add Folder": "Ajouter un dossier",
"Open Storage folder": "Ouvrir un dossier de stockage",
"Unlink": "Délier",
"Edit": "Editer",
"Delete": "Supprimer",
"Interface": "Interface",
"Interface Theme": "Thème d'interface",
"Default": "Effacer",
"White": "Blanc",
"Solarized Dark": "Foncé solarisé",
"Dark": "Foncé",
"Show a confirmation dialog when deleting notes": "Montrer une alerte de confirmation lors de la suppression de notes",
"Editor Theme": "Theme d'éditeur",
"Editor Font Size": "Taille de police de l'éditeur",
"Editor Font Family": "Police de l'éditeur",
"Editor Indent Style": "Style d'indentation de l'éditeur",
"Spaces": "Espaces",
"Tabs": "Tabulations",
"Switch to Preview": "Switcher vers l'aperçu",
"When Editor Blurred": "Quand l'éditeur n'est pas sélectionné",
"When Editor Blurred, Edit On Double Click": "Quand l'éditeur n'est pas sélectionné, éditer avec un double clic",
"On Right Click": "Avec un clic droit",
"Editor Keymap": "Keymap de l'éditeur",
"default": "Par défaut",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Redémarrez Boostnote après avoir changé la keymap",
"Show line numbers in the editor": "Montrer les numéros de lignes dans l'éditeur",
"Allow editor to scroll past the last line": "Contrôle si l'éditeur défile au-delà de la dernière ligne",
"Bring in web page title when pasting URL on editor": "Mettre le titre de la page lors d'un collé d'une URL dans l'éditeur",
"Preview": "Aperçu",
"Preview Font Size": "Taille de police de l'aperçu",
"Preview Font Family": "Police de l'aperçu",
"Code block Theme": "Thème des blocs de code",
"Show line numbers for preview code blocks": "Montrer les numéros de lignes dans les blocs de code dans l'aperçu",
"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",
"Community": "Communauté",
"Subscribe to Newsletter": "Souscrire à la newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Groupe Facebook",
"Twitter": "Twitter",
"About": "A propos",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "Une appli de prise de notes open-source faite pour les développeurs comme vous.",
"Website": "Site web",
"Development": "Développement",
" : Development configurations for Boostnote.": " : Configurations de développement pour Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 collecte des données anonymisées dans le seul but d'améliorer l'application, et ne collecte aucune donnée personnelle telle que le contenu de vos notes.",
"You can see how it works on ": "Vous pouvez voir comment ça marche sur",
"You can choose to enable or disable this option.": "Vous pouvez choisir d'activer/désactiver cette option.",
"Enable analytics to help improve Boostnote": "Activer la collecte de données anonymisées pour améliorer Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Cher utilisateur,",
"Thank you for using Boostnote!": "Merci d'utiliser Boostnote !",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote est utilisé dans plus de 200 pays et régions par une impressionnante communauté de développeurs.",
"To continue supporting this growth, and to satisfy community expectations,": "Afin de continuer à grandir, et de satisfaire les attentes de la communauté,",
"we would like to invest more time and resources in this project.": "nous aimerions investir d'avantage de temps et de ressources dans ce proje.",
"If you like this project and see its potential, you can help by supporting us on OpenCollective!": "Si vous aimez ce projet et que vous en voyez tout le potentiel, vous pouvez aider par un support sur OpenCollective !",
"Thanks,": "Merci,",
"Boostnote maintainers": "Les mainteneurs de Boostnote",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Langues",
"English": "Anglais",
"German": "Allemand",
"French": "Français",
"Show \"Saved to Clipboard\" notification when copying": "Montrer la notification \"Sauvegardé dans le presse-papiers\" lors de la copie",
"All Notes": "Toutes les notes",
"Starred": "Favoris",
"Are you sure to ": "Etes-vous sûr de ",
" delete": " supprimer",
"this folder?": "ce dossier ?",
"Confirm": "Confimer",
"Cancel": "Annuler",
"Markdown Note": "Note Markdown",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "Ce format est pour créer des documents texte. Checklists, blocks de code et blocks Latex sont disponibles.",
"Snippet Note": "Note Snippet",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "Ce format est pour créer des snippets de code. Plusieurs snippets peuvent être groupés en une seule note.",
"Tab to switch format": "Tab pour changer de format",
"Updated": "Mis à jour",
"Created": "Créé",
"Alphabetically": "De manière alphabétique",
"Default View": "Vue par défaut",
"Compressed View": "Vue compressée",
"Search": "Chercher",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Sauvegarder",
"Auth": "Auth",
"Authentication Method": "Méthode d'Authentification",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Stockage",
"Hotkeys": "Raccourcis",
"Show/Hide Boostnote": "Montrer/Cacher Boostnote",
"Restore": "Restaurer",
"Permanent Delete": "Supprimer définivitement",
"Confirm note deletion": "Confirmer la suppression de la note",
"This will permanently remove this note.": "Cela va supprimer cette note définitivement.",
"Successfully applied!": " Succès !",
"Albanian": "Albanais",
"Chinese (zh-CN)": "Chinois (zh-CN)",
"Chinese (zh-TW)": "Chinois (zh-TW)",
"Danish": "Danois",
"Japanese": "Japonais",
"Korean": "Coréen",
"Norwegian": "Norvégien",
"Polish": "Polonais",
"Portuguese": "Portugais",
"Spanish": "Espagnol",
"You have to save!": "Il faut sauvegarder !"
}

145
locales/ja.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

145
locales/ko.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

145
locales/no.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

145
locales/pl.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

145
locales/pt.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

147
locales/ru.json Normal file
View File

@@ -0,0 +1,147 @@
{
"Notes": "Записи",
"Tags": "Теги",
"Preferences": "Настройки",
"Make a note": "Добавить запись",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "создать новую запись",
"Toggle Mode": "Переключить режим",
"Trash": "Корзина",
"MODIFICATION DATE": "Дата изменения",
"Words": "Слова",
"Letters": "Буквы",
"STORAGE": "Хранилище",
"FOLDER": "Папка",
"CREATION DATE": "Дата создания",
"NOTE LINK": "Ссылка на запись",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Настройки Boostnote",
"Storages": "Хранилища",
"Add Storage Location": "Добавить хранилище",
"Add Folder": "Добавить папку",
"Open Storage folder": "Открыть хранилище",
"Unlink": "Удалить",
"Edit": "Редактировать",
"Delete": "Удалить",
"Interface": "Интерфейс",
"Interface Theme": "Тема оформления",
"Default": "По умолчанию",
"White": "Светлая",
"Solarized Dark": "Solarized Dark",
"Dark": "Темная",
"Show a confirmation dialog when deleting notes": "Спрашивать подтверждение перед удалением записей?",
"Editor Theme": "Тема",
"Editor Font Size": "Размер шрифта",
"Editor Font Family": "Шрифт",
"Editor Indent Style": "Отступы",
"Spaces": "Пробелы",
"Tabs": "Табуляция",
"Switch to Preview": "Переключать на превью",
"When Editor Blurred": "При снятии фокуса с редактора",
"When Editor Blurred, Edit On Double Click": "При снятии фокуса, редактировать двойным кликом",
"On Right Click": "По правому клику",
"Editor Keymap": "Горячие клавиши",
"default": "по умолчанию",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Перезапустите Boostnote, чтобы применить изменения",
"Show line numbers in the editor": "Показывать номера строк в редакторе",
"Allow editor to scroll past the last line": "Разрешить прокрутку дальше последней строки в редакторе",
"Bring in web page title when pasting URL on editor": "Копировать заголовок страницы при вставке URL-ссылки в редакторе",
"Preview": "Preview",
"Preview Font Size": "Размер шрифта",
"Preview Font Family": "Шрифт",
"Code block Theme": "Тема оформления кода",
"Allow preview to scroll past the last line": "Разрешить прокрутку дальше последней строки в превью",
"Show line numbers for preview code blocks": "Показывать номера строк в блоках кода",
"LaTeX Inline Open Delimiter": "Символ начала inline записи в LaTeX",
"LaTeX Inline Close Delimiter": "Символ окончания inline записи в LaTeX",
"LaTeX Block Open Delimiter": "Символ начала блока LaTeX",
"LaTeX Block Close Delimiter": "Символ окончания блока LaTeX",
"Community": "Сообщество",
"Subscribe to Newsletter": "Подпишитесь на рассылку",
"GitHub": "GitHub",
"Blog": "Блог",
"Facebook Group": "Группа в Фейсбуке",
"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": "Copyright (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 используется в 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!": "Если вам нравится Boostnote и его сообщество, вы можете профинансировать проект на OpenCollective!",
"Thanks,": "Спасибо,",
"Boostnote maintainers": "разработчики Boostnote",
"Support via OpenCollective": "Старница проекта на OpenCollective",
"Language": "Язык",
"English": "Английский",
"German": "Немецкий",
"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.": "Этот формат подходит для хранения кусков кода. Внутри одной записи можно создать несколько сниппетов.",
"Tab to switch format": "Tab для переключения формата",
"Updated": "По дате изменения",
"Created": "По дате создания",
"Alphabetically": "По алфавиту",
"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",
"Restore": "Восстановить",
"Permanent Delete": "Удалить без восстановления",
"Confirm note deletion": "Подтвердите удаление",
"This will permanently remove this note.": "Это действие приведет к полному удалению записи. Восстановить запись будет невозможно.",
"Successfully applied!": "Успешно!",
"Albanian": "Албанский",
"Chinese (zh-CN)": "Китайский (zh-CN)",
"Chinese (zh-TW)": "Китайский (zh-TW)",
"Danish": "Датский",
"Japanese": "Японский",
"Korean": "Корейский",
"Norwegian": "Норвежский",
"Polish": "Польский",
"Portuguese": "Португальский",
"Spanish": "Испанский",
"You have to save!": "Нужно сохранить!",
"UserName": "Имя пользователя",
"Password": "Пароль",
"Russian": "Русский"
}

145
locales/sq.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

145
locales/zh-CN.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

145
locales/zh-TW.json Normal file
View File

@@ -0,0 +1,145 @@
{
"Notes": "Notes",
"Tags": "Tags",
"Preferences": "Preferences",
"Make a note": "Make a note",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
"to create a new note": "to create a new note",
"Toggle Mode": "Toggle Mode",
"Trash": "Trash",
"MODIFICATION DATE": "MODIFICATION DATE",
"Words": "Words",
"Letters": "Letters",
"STORAGE": "STORAGE",
"FOLDER": "FOLDER",
"CREATION DATE": "CREATION DATE",
"NOTE LINK": "NOTE LINK",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "Print",
"Your preferences for Boostnote": "Your preferences for Boostnote",
"Storages": "Storages",
"Add Storage Location": "Add Storage Location",
"Add Folder": "Add Folder",
"Open Storage folder": "Open Storage folder",
"Unlink": "Unlink",
"Edit": "Edit",
"Delete": "Delete",
"Interface": "Interface",
"Interface Theme": "Interface Theme",
"Default": "Delete",
"White": "White",
"Solarized Dark": "Solarized Dark",
"Dark": "Dark",
"Show a confirmation dialog when deleting notes": "Show a confirmation dialog when deleting notes",
"Editor Theme": "Editor Theme",
"Editor Font Size": "Editor Font Size",
"Editor Font Family": "Editor Font Family",
"Editor Indent Style": "Editor Indent Style",
"Spaces": "Spaces",
"Tabs": "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 Keymap",
"default": "default",
"vim": "vim",
"emacs": "emacs",
"⚠️ Please restart boostnote after you change the keymap": "⚠️ Please restart boostnote after you change the keymap",
"Show line numbers in the editor": "Show line numbers in the editor",
"Allow editor to scroll past the last line": "Allow editor to scroll past the last line",
"Bring in web page title when pasting URL on editor": "Bring in web page title when pasting URL on editor",
"Preview": "Preview",
"Preview Font Size": "Preview Font Size",
"Preview Font Family": "Preview Font Family",
"Code block Theme": "Code block Theme",
"Allow preview to scroll past the last line": "Allow preview to scroll past the last line",
"Show line numbers for preview code blocks": "Show line numbers for preview code blocks",
"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",
"Community": "Community",
"Subscribe to Newsletter": "Subscribe to Newsletter",
"GitHub": "GitHub",
"Blog": "Blog",
"Facebook Group": "Facebook Group",
"Twitter": "Twitter",
"About": "About",
"Boostnote": "Boostnote",
"An open source note-taking app made for programmers just like you.": "An open source note-taking app made for programmers just like you.",
"Website": "Website",
"Development": "Development",
" : Development configurations for Boostnote.": " : Development configurations for Boostnote.",
"Copyright (C) 2017 - 2018 BoostIO": "Copyright (C) 2017 - 2018 BoostIO",
"License: GPL v3": "License: GPL v3",
"Analytics": "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 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.",
"You can see how it works on ": "You can see how it works on ",
"You can choose to enable or disable this option.": "You can choose to enable or disable this option.",
"Enable analytics to help improve Boostnote": "Enable analytics to help improve Boostnote",
"Crowdfunding": "Crowdfunding",
"Dear everyone,": "Dear everyone,",
"Thank you for using Boostnote!": "Thank you for using Boostnote!",
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote is used in about 200 different countries and regions by an awesome community of developers.",
"To continue supporting this growth, and to satisfy community expectations,": "To continue supporting this growth, and to satisfy community expectations,",
"we would like to invest more time and resources in this project.": "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!": "If you like this project and see its potential, you can help by supporting us on OpenCollective!",
"Thanks,": "Thanks,",
"Boostnote maintainers": "Boostnote maintainers",
"Support via OpenCollective": "Support via OpenCollective",
"Language": "Language",
"English": "English",
"German": "German",
"French": "French",
"Show \"Saved to Clipboard\" notification when copying": "Show \"Saved to Clipboard\" notification when copying",
"All Notes": "All Notes",
"Starred": "Starred",
"Are you sure to ": "Are you sure to ",
" delete": " delete",
"this folder?": "this folder?",
"Confirm": "Confirm",
"Cancel": "Cancel",
"Markdown Note": "Markdown Note",
"This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.",
"Snippet Note": "Snippet Note",
"This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "This format is for creating code snippets. Multiple snippets can be grouped into a single note.",
"Tab to switch format": "Tab to switch format",
"Updated": "Updated",
"Created": "Created",
"Alphabetically": "Alphabetically",
"Default View": "Default View",
"Compressed View": "Compressed View",
"Search": "Search",
"Blog Type": "Blog Type",
"Blog Address": "Blog Address",
"Save": "Save",
"Auth": "Auth",
"Authentication Method": "Authentication Method",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
"Storage": "Storage",
"Hotkeys": "Hotkeys",
"Show/Hide Boostnote": "Show/Hide Boostnote",
"Restore": "Restore",
"Permanent Delete": "Permanent Delete",
"Confirm note deletion": "Confirm note deletion",
"This will permanently remove this note.": "This will permanently remove this note.",
"Successfully applied!": "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!": "You have to save!",
"Russian": "Russian"
}

View File

@@ -1,46 +0,0 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../codemirror/lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../codemirror/lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
unorderedListRE = /[*+-]\s/;
CodeMirror.commands.boostNewLineAndIndentContinueMarkdownList = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
var ranges = cm.listSelections(), replacements = [];
for (var i = 0; i < ranges.length; i++) {
var pos = ranges[i].head;
var eolState = cm.getStateAfter(pos.line);
var inList = eolState.list !== false;
var inQuote = eolState.quote !== 0;
var line = cm.getLine(pos.line), match = listRE.exec(line);
if (!ranges[i].empty() || (!inList && !inQuote) || !match || pos.ch < match[2].length - 1) {
cm.execCommand("newlineAndIndent");
return;
}
if (emptyListRE.test(line)) {
if (!/>\s*$/.test(line)) cm.replaceRange("", {
line: pos.line, ch: 0
}, {
line: pos.line, ch: pos.ch + 1
});
replacements[i] = "\n";
} else {
var indent = match[1], after = match[5];
var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
? match[2].replace("x", " ")
: (parseInt(match[3], 10) + 1) + match[4];
replacements[i] = "\n" + indent + bullet + after;
}
}
cm.replaceSelections(replacements);
};
});

35
package-lock.json generated Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "boost",
"version": "0.10.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"i18n-2": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/i18n-2/-/i18n-2-0.7.2.tgz",
"integrity": "sha512-Rdh6vfpNhL7q61cNf27x7QGULTi1TcGLVdFb5OJ6dOiJo+EkOTqEg0+3xgyeEMgYhopUBsh2IiSkFkjM+EhEmA==",
"requires": {
"debug": "3.1.0",
"sprintf": "0.1.5"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"sprintf": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz",
"integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8="
}
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "boost",
"productName": "Boostnote",
"version": "0.10.0",
"version": "0.11.2",
"main": "index.js",
"description": "Boostnote",
"license": "GPL-3.0",
@@ -58,9 +58,11 @@
"electron-gh-releases": "^2.0.2",
"flowchart.js": "^1.6.5",
"font-awesome": "^4.3.0",
"i18n-2": "^0.7.2",
"iconv-lite": "^0.4.19",
"immutable": "^3.8.1",
"js-sequence-diagrams": "^1000000.0.6",
"katex": "^0.8.3",
"katex": "^0.9.0",
"lodash": "^4.11.1",
"lodash-move": "^1.1.1",
"markdown-it": "^6.0.1",
@@ -84,12 +86,14 @@
"react-sortable-hoc": "^0.6.7",
"redux": "^3.5.2",
"sander": "^0.5.1",
"sanitize-html": "^1.18.2",
"striptags": "^2.2.1",
"superagent": "^1.2.0",
"superagent-promise": "^1.0.3"
"superagent-promise": "^1.0.3",
"uuid": "^3.2.1"
},
"devDependencies": {
"ava": "^0.16.0",
"ava": "^0.25.0",
"babel-core": "^6.14.0",
"babel-loader": "^6.2.0",
"babel-plugin-react-transform": "^2.0.0",
@@ -98,6 +102,7 @@
"babel-preset-react": "^6.3.13",
"babel-preset-react-hmre": "^1.0.1",
"babel-register": "^6.11.6",
"browser-env": "^3.2.5",
"concurrently": "^3.4.0",
"copy-to-clipboard": "^3.0.6",
"css-loader": "^0.19.0",
@@ -117,6 +122,7 @@
"jsdom": "^9.4.2",
"json-loader": "^0.5.4",
"merge-stream": "^1.0.0",
"mock-require": "^3.0.1",
"nib": "^1.1.0",
"react-color": "^2.2.2",
"react-css-modules": "^3.7.6",
@@ -140,7 +146,9 @@
"tests/**/*-test.js"
],
"require": [
"babel-register"
"babel-register",
"./tests/helpers/setup-browser-env.js",
"./tests/helpers/setup-electron-mock.js"
],
"babel": "inherit"
}

View File

@@ -1,4 +1,4 @@
:mega: Open sourcing our [Android and iOS apps](https://github.com/BoostIO/Boostnote-mobile)!
:mega: We've launched a blogging platform with markdown called **[Boostlog](https://boostlog.io/)**.
![Boostnote app screenshot](./resources/repository/top.png)
@@ -25,8 +25,8 @@ Boostnote is an open source project. It's an independent project with its ongoin
## Community
- [Facebook Group](https://www.facebook.com/groups/boostnote/)
- [Twitter](https://twitter.com/boostnoteapp)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzAzMjI1MTIyNTQ3LTc2MjNiYWU3NTc1YjZlMTk3NzFmOWE1ZWU1MGRhMzBkMGIwMWFjOWMxMDRiM2I2NzkzYzc4OGZhNmVhZjYzZTM)
- [Blog](https://boostlog.io/@junp1234)
- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzI3NTIxMTQzMTQzLTUyYWZmZWM1YzcwYzQ5OWQ5YzA3Y2M2NzUzNmIwNzYzMjg5NmQyOGJlNzcyZDJhMGY0ZDc0ZjdlZDFhMDdiMWE)
- [Blog](https://boostlog.io/tags/boostnote)
- [Reddit](https://www.reddit.com/r/Boostnote/)

View File

@@ -22,7 +22,7 @@ function dummyBoostnoteJSONData (override = {}, isLegacy = false) {
if (override.folders == null) {
data.folders = []
var folderCount = Math.floor((Math.random() * 5)) + 1
var folderCount = Math.floor((Math.random() * 5)) + 2
for (var i = 0; i < folderCount; i++) {
var key = keygen()
while (data.folders.some((folder) => folder.key === key)) {
@@ -105,11 +105,11 @@ function dummyStorage (storagePath, override = {}) {
sander.writeFileSync(path.join(storagePath, 'boostnote.json'), JSON.stringify(jsonData))
var notesData = []
var noteCount = Math.floor((Math.random() * 15)) + 1
var noteCount = Math.floor((Math.random() * 15)) + 2
for (var i = 0; i < noteCount; i++) {
var key = keygen()
var key = keygen(true)
while (notesData.some((note) => note.key === key)) {
key = keygen()
key = keygen(true)
}
var noteData = dummyNote({
@@ -149,9 +149,9 @@ function dummyLegacyStorage (storagePath, override = {}) {
var folderNotes = []
var noteCount = Math.floor((Math.random() * 5)) + 1
for (var i = 0; i < noteCount; i++) {
var key = keygen(6)
var key = keygen(true)
while (folderNotes.some((note) => note.key === key)) {
key = keygen(6)
key = keygen(true)
}
var noteData = dummyNote({

57
tests/fixtures/markdowns.js vendored Normal file
View File

@@ -0,0 +1,57 @@
const basic = `
# Welcome to Boostnote!
## Click here to edit markdown :wave:
<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>
## Docs :memo:
- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)
- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)
- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)
- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)
- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)
- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)
- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)
---
## Article Archive :books:
- [Reddit English](http://bit.ly/2mOJPu7)
- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)
- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)
- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)
---
## Community :beers:
- [GitHub](http://bit.ly/2AWWzkD)
- [Twitter](http://bit.ly/2z8BUJZ)
- [Facebook Group](http://bit.ly/2jcca8t)
`
const codeblock = `
\`\`\`js:filename.js:2
var project = 'boostnote';
\`\`\`
`
const katex = `
$$
c = \pm\sqrt{a^2 + b^2}
$$
`
const checkboxes = `
- [ ] Unchecked
- [x] Checked
`
const smartQuotes = 'This is a "QUOTE".'
export default {
basic,
codeblock,
katex,
checkboxes,
smartQuotes
}

View File

@@ -0,0 +1,9 @@
import browserEnv from 'browser-env'
browserEnv(['window', 'document'])
window.localStorage = {
// polyfill
getItem () {
return '{}'
}
}

View File

@@ -0,0 +1,11 @@
import mock from 'mock-require'
const noop = () => {}
mock('electron', {
remote: {
app: {
getAppPath: noop
}
}
})

View File

@@ -0,0 +1,36 @@
import test from 'ava'
import Markdown from 'browser/lib/markdown'
import markdownFixtures from '../fixtures/markdowns'
// basic markdown instance which meant to be used in every test cases.
// To test markdown options, initialize a new instance in your test case
const md = new Markdown()
test('Markdown.render() should renders markdown correctly', t => {
const rendered = md.render(markdownFixtures.basic)
t.snapshot(rendered)
})
test('Markdown.render() should renders codeblock correctly', t => {
const rendered = md.render(markdownFixtures.codeblock)
t.snapshot(rendered)
})
test('Markdown.render() should renders KaTeX correctly', t => {
const rendered = md.render(markdownFixtures.katex)
t.snapshot(rendered)
})
test('Markdown.render() should renders checkboxes', t => {
const rendered = md.render(markdownFixtures.checkboxes)
t.snapshot(rendered)
})
test('Markdown.render() should text with quotes correctly', t => {
const renderedSmartQuotes = md.render(markdownFixtures.smartQuotes)
t.snapshot(renderedSmartQuotes)
const newmd = new Markdown({ typographer: false })
const renderedNonSmartQuotes = newmd.render(markdownFixtures.smartQuotes)
t.snapshot(renderedNonSmartQuotes)
})

View File

@@ -29,5 +29,5 @@ test('RcParser should return a json object', t => {
})
function filePath (filename) {
return path.join('boostnoterc', filename)
return path.join(`${__dirname}/boostnoterc`, filename)
}

View File

@@ -0,0 +1,76 @@
# Snapshot report for `tests/lib/markdown-test.js`
The actual snapshot is saved in `markdown-test.js.snap`.
Generated by [AVA](https://ava.li).
## Markdown.render() should renders KaTeX correctly
> Snapshot 1
`<span class="katex-display"><span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>c</mi><mo>=</mo><mi>p</mi><mi>m</mi><mi>s</mi><mi>q</mi><mi>r</mi><mi>t</mi><mrow><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup></mrow></mrow><annotation encoding="application/x-tex">c = pmsqrt{a^2 + b^2}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.8641079999999999em;"></span><span class="strut bottom" style="height:1.0585479999999998em;vertical-align:-0.19444em;"></span><span class="base"><span class="mord mathit">c</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">p</span><span class="mord mathit">m</span><span class="mord mathit">s</span><span class="mord mathit" style="margin-right:0.03588em;">q</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit">t</span><span class="mord"><span class="mord"><span class="mord mathit">a</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mord rule" style="margin-right:0.2222222222222222em;"></span><span class="mbin">+</span><span class="mord rule" style="margin-right:0.2222222222222222em;"></span><span class="mord"><span class="mord mathit">b</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8641079999999999em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span></span></span>␊
`
## Markdown.render() should renders checkboxes
> Snapshot 1
`<ul>␊
<li class="taskListItem"><input type="checkbox" id="checkbox-2" /> Unchecked</li>␊
<li class="taskListItem"><input type="checkbox" checked id="checkbox-3" /> Checked</li>␊
</ul>␊
`
## Markdown.render() should renders codeblock correctly
> Snapshot 1
`<pre class="code CodeMirror"><span class="filename">filename.js</span><span class="lineNumber CodeMirror-gutters"><span class="CodeMirror-linenumber">2</span></span><code class="js">var project = 'boostnote';␊
</code></pre>␊
`
## Markdown.render() should renders markdown correctly
> Snapshot 1
`<h1 data-line="1" id="Welcome-to-Boostnote">Welcome to Boostnote!</h1>␊
<h2 data-line="2" id="Click-here-to-edit-markdown-%F0%9F%91%8B">Click here to edit markdown 👋</h2>␊
<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>␊
<h2 data-line="6" id="Docs-%F0%9F%93%9D">Docs 📝</h2>␊
<ul>␊
<li><a href="https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe">Boostnote | Boost your happiness, productivity and creativity.</a></li>␊
<li><a href="https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup">Cloud Syncing &amp; Backups</a></li>␊
<li><a href="https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps">How to sync your data across Desktop and Mobile apps</a></li>␊
<li><a href="https://github.com/BoostIO/Boostnote/wiki/Evernote">Convert data from <strong>Evernote</strong> to Boostnote.</a></li>␊
<li><a href="https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts">Keyboard Shortcuts</a></li>␊
<li><a href="https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode">Keymaps in Editor mode</a></li>␊
<li><a href="https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting">How to set syntax highlight in Snippet note</a></li>␊
</ul>␊
<hr />␊
<h2 data-line="17" id="Article-Archive-%F0%9F%93%9A">Article Archive 📚</h2>␊
<ul>␊
<li><a href="http://bit.ly/2mOJPu7">Reddit English</a></li>␊
<li><a href="https://www.reddit.com/r/boostnote_es/">Reddit Spanish</a></li>␊
<li><a href="https://www.reddit.com/r/boostnote_cn/">Reddit Chinese</a></li>␊
<li><a href="https://www.reddit.com/r/boostnote_jp/">Reddit Japanese</a></li>␊
</ul>␊
<hr />␊
<h2 data-line="25" id="Community-%F0%9F%8D%BB">Community 🍻</h2>␊
<ul>␊
<li><a href="http://bit.ly/2AWWzkD">GitHub</a></li>␊
<li><a href="http://bit.ly/2z8BUJZ">Twitter</a></li>␊
<li><a href="http://bit.ly/2jcca8t">Facebook Group</a></li>␊
</ul>␊
`
## Markdown.render() should text with quotes correctly
> Snapshot 1
`<p data-line="0">This is a “QUOTE”.</p>␊
`
> Snapshot 2
`<p data-line="0">This is a &quot;QUOTE&quot;.</p>␊
`

Binary file not shown.

1827
yarn.lock

File diff suppressed because it is too large Load Diff