)
TagListItem.propTypes = {
diff --git a/browser/components/TagListItem.styl b/browser/components/TagListItem.styl
index b35b30cf..555520b0 100644
--- a/browser/components/TagListItem.styl
+++ b/browser/components/TagListItem.styl
@@ -1,5 +1,9 @@
+.tagList-itemContainer
+ display flex
+
.tagList-item
display flex
+ flex 1
width 100%
height 26px
background-color transparent
@@ -20,9 +24,16 @@
color $ui-button-default-color
background-color $ui-button-default--active-backgroundColor
+.tagList-itemNarrow
+ composes tagList-item
+ flex none
+ width 20px
+ padding 0 4px
+
.tagList-item-active
background-color $ui-button-default--active-backgroundColor
display flex
+ flex 1
width 100%
height 26px
padding 0
@@ -36,10 +47,16 @@
background-color alpha($ui-button-default--active-backgroundColor, 60%)
transition 0.2s
+.tagList-itemNarrow-active
+ composes tagList-item-active
+ flex none
+ width 20px
+ padding 0 4px
+
.tagList-item-name
display block
flex 1
- padding 0 15px
+ padding 0 8px 0 4px
height 26px
line-height 26px
border-width 0 0 0 2px
@@ -49,7 +66,10 @@
text-overflow ellipsis
.tagList-item-count
- padding 0 3px
+ float right
+ line-height 26px
+ padding-right 15px
+ font-size 13px
body[data-theme="white"]
.tagList-item
diff --git a/browser/components/TodoListPercentage.styl b/browser/components/TodoListPercentage.styl
index 329663f9..6116cd58 100644
--- a/browser/components/TodoListPercentage.styl
+++ b/browser/components/TodoListPercentage.styl
@@ -47,5 +47,15 @@ body[data-theme="solarized-dark"]
.progressBar
background-color: #2aa198
+ .percentageText
+ color #fdf6e3
+
+body[data-theme="monokai"]
+ .percentageBar
+ background-color #f92672
+
+ .progressBar
+ background-color: #373831
+
.percentageText
color #fdf6e3
\ No newline at end of file
diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl
index cc6d7d92..373b321e 100644
--- a/browser/components/markdown.styl
+++ b/browser/components/markdown.styl
@@ -371,3 +371,30 @@ body[data-theme="solarized-dark"]
border-color themeSolarizedDarkTableBorder
&:last-child
border-right solid 1px themeSolarizedDarkTableBorder
+
+themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor
+themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%)
+themeMonokaiTableHead = themeMonokaiTableEven
+themeMonokaiTableBorder = themeDarkBorder
+
+body[data-theme="monokai"]
+ color $ui-monokai-text-color
+ border-color themeDarkBorder
+ background-color $ui-monokai-noteDetail-backgroundColor
+ table
+ thead
+ tr
+ background-color themeMonokaiTableHead
+ th
+ border-color themeMonokaiTableBorder
+ &:last-child
+ border-right solid 1px themeMonokaiTableBorder
+ tbody
+ tr:nth-child(2n + 1)
+ background-color themeMonokaiTableOdd
+ tr:nth-child(2n)
+ background-color themeMonokaiTableEven
+ td
+ border-color themeMonokaiTableBorder
+ &:last-child
+ border-right solid 1px themeMonokaiTableBorder
\ No newline at end of file
diff --git a/browser/lib/Languages.js b/browser/lib/Languages.js
new file mode 100644
index 00000000..09a1614e
--- /dev/null
+++ b/browser/lib/Languages.js
@@ -0,0 +1,75 @@
+const languages = [
+ {
+ name: 'Albanian',
+ locale: 'sq'
+ },
+ {
+ name: 'Chinese (zh-CN)',
+ locale: 'zh-CN'
+ },
+ {
+ name: 'Chinese (zh-TW)',
+ locale: 'zh-TW'
+ },
+ {
+ name: 'Danish',
+ locale: 'da'
+ },
+ {
+ name: 'English',
+ locale: 'en'
+ },
+ {
+ name: 'French',
+ locale: 'fr'
+ },
+ {
+ name: 'German',
+ locale: 'de'
+ },
+ {
+ name: 'Hungarian',
+ locale: 'hu'
+ },
+ {
+ name: 'Japanese',
+ locale: 'ja'
+ },
+ {
+ name: 'Korean',
+ locale: 'ko'
+ },
+ {
+ name: 'Norwegian',
+ locale: 'no'
+ },
+ {
+ name: 'Polish',
+ locale: 'pl'
+ },
+ {
+ name: 'Portuguese',
+ locale: 'pt'
+ },
+ {
+ name: 'Russian',
+ locale: 'ru'
+ },
+ {
+ name: 'Spanish',
+ locale: 'es-ES'
+ }
+]
+
+module.exports = {
+ getLocales () {
+ return languages.reduce(function (localeList, locale) {
+ localeList.push(locale.locale)
+ return localeList
+ }, [])
+ },
+ getLanguages () {
+ return languages
+ }
+}
+
diff --git a/browser/lib/i18n.js b/browser/lib/i18n.js
index f7bd96e9..e12e2aa6 100644
--- a/browser/lib/i18n.js
+++ b/browser/lib/i18n.js
@@ -1,11 +1,12 @@
const path = require('path')
const { remote } = require('electron')
const { app } = remote
+const { getLocales } = require('./Languages.js')
// load package for localization
const i18n = new (require('i18n-2'))({
// setup some locales - other locales default to the first locale
- locales: [ 'da', 'de', 'en', 'es-ES', 'fr', 'hu', 'ja', 'ko', 'pl', 'pt-BR', 'pt-PT', 'ru', 'sq', 'zh-CN', 'zh-TW' ],
+ locales: getLocales(),
extension: '.json',
directory: process.env.NODE_ENV === 'production'
? path.join(app.getAppPath(), './locales')
diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index 6f1f2f00..1ef488a7 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -5,7 +5,7 @@ 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'
+import { lastFindInArray } from './utils'
function createGutter (str, firstLineNumber) {
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
@@ -234,10 +234,6 @@ class Markdown {
if (!_.isString(content)) content = ''
return this.md.render(content)
}
-
- normalizeLinkText (linkText) {
- return this.md.normalizeLinkText(linkText)
- }
}
export default Markdown
diff --git a/browser/main/Detail/Detail.styl b/browser/main/Detail/Detail.styl
index d4c4100c..49a634f3 100644
--- a/browser/main/Detail/Detail.styl
+++ b/browser/main/Detail/Detail.styl
@@ -30,3 +30,10 @@ body[data-theme="solarized-dark"]
border-left 1px solid $ui-solarized-dark-borderColor
.empty-message
color $ui-solarized-dark-text-color
+
+body[data-theme="monokai"]
+ .root
+ background-color $ui-monokai-noteDetail-backgroundColor
+ border-left 1px solid $ui-monokai-borderColor
+ .empty-message
+ color $ui-monokai-text-color
diff --git a/browser/main/Detail/FolderSelect.styl b/browser/main/Detail/FolderSelect.styl
index 31930fe6..cfdc2734 100644
--- a/browser/main/Detail/FolderSelect.styl
+++ b/browser/main/Detail/FolderSelect.styl
@@ -133,3 +133,29 @@ body[data-theme="dark"]
color $ui-dark-button--active-color
.search-optionList-item-name-surfix
color $ui-dark-inactive-text-color
+
+body[data-theme="monokai"]
+ .root
+ color $ui-dark-text-color
+ &:hover
+ color white
+ background-color $ui-monokai-button--hover-backgroundColor
+ border-color $ui-monokai-borderColor
+
+ .search-optionList
+ color white
+ border-color $ui-monokai-borderColor
+ background-color $ui-monokai-button-backgroundColor
+
+ .search-optionList-item
+ &:hover
+ background-color lighten($ui-monokai-button--hover-backgroundColor, 15%)
+
+ .search-optionList-item--active
+ background-color $ui-monokai-button--active-backgroundColor
+ color $ui-monokai-button--active-color
+ &:hover
+ background-color $ui-monokai-button--active-backgroundColor
+ color $ui-monokai-button--active-color
+ .search-optionList-item-name-surfix
+ color $ui-monokai-inactive-text-color
diff --git a/browser/main/Detail/InfoPanel.styl b/browser/main/Detail/InfoPanel.styl
index d90dea49..480441bd 100644
--- a/browser/main/Detail/InfoPanel.styl
+++ b/browser/main/Detail/InfoPanel.styl
@@ -215,3 +215,43 @@ body[data-theme="solarized-dark"]
color $ui-dark-inactive-text-color
&:hover
color $ui-solarized-ark-text-color
+
+body[data-theme="monokai"]
+ .control-infoButton-panel
+ background-color $ui-monokai-noteList-backgroundColor
+
+ .control-infoButton-panel-trash
+ background-color $ui-monokai-noteList-backgroundColor
+
+ .modification-date
+ color $ui-monokai-text-color
+
+ .modification-date-desc
+ color $ui-inactive-text-color
+
+ .infoPanel-defaul-count
+ color $ui-monokai-text-color
+
+ .infoPanel-sub-count
+ color $ui-inactive-text-color
+
+ .infoPanel-default
+ color $ui-monokai-text-color
+
+ .infoPanel-sub
+ color $ui-inactive-text-color
+
+ .infoPanel-noteLink
+ background-color alpha($ui-monokai-borderColor, 20%)
+ color $ui-monokai-text-color
+
+ [id=export-wrap]
+ button
+ color $ui-dark-inactive-text-color
+ &:hover
+ background-color alpha($ui-monokai-borderColor, 20%)
+ color $ui-monokai-text-color
+ p
+ color $ui-dark-inactive-text-color
+ &:hover
+ color $ui-monokai-text-color
diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js
index 05883c0e..72f832e3 100755
--- a/browser/main/Detail/MarkdownNoteDetail.js
+++ b/browser/main/Detail/MarkdownNoteDetail.js
@@ -289,6 +289,7 @@ class MarkdownNoteDetail extends React.Component {
config={config}
value={note.content}
storageKey={note.storage}
+ noteKey={note.key}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
@@ -298,6 +299,7 @@ class MarkdownNoteDetail extends React.Component {
config={config}
value={note.content}
storageKey={note.storage}
+ noteKey={note.key}
onChange={this.handleUpdateContent.bind(this)}
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
/>
diff --git a/browser/main/Detail/MarkdownNoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl
index ad20f0f2..b27dc80e 100644
--- a/browser/main/Detail/MarkdownNoteDetail.styl
+++ b/browser/main/Detail/MarkdownNoteDetail.styl
@@ -71,3 +71,8 @@ body[data-theme="solarized-dark"]
.root
border-left 1px solid $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
+
+body[data-theme="monokai"]
+ .root
+ border-left 1px solid $ui-monokai-borderColor
+ background-color $ui-monokai-noteDetail-backgroundColor
diff --git a/browser/main/Detail/NoteDetailInfo.styl b/browser/main/Detail/NoteDetailInfo.styl
index bc3c9462..8d454203 100644
--- a/browser/main/Detail/NoteDetailInfo.styl
+++ b/browser/main/Detail/NoteDetailInfo.styl
@@ -98,3 +98,7 @@ body[data-theme="solarized-dark"]
border-color $ui-solarized-dark-borderColor
background-color $ui-solarized-dark-noteDetail-backgroundColor
+body[data-theme="monokai"]
+ .info
+ border-color $ui-monokai-borderColor
+ background-color $ui-monokai-noteDetail-backgroundColor
\ No newline at end of file
diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl
index 789d5186..f8ca48cc 100644
--- a/browser/main/Detail/SnippetNoteDetail.styl
+++ b/browser/main/Detail/SnippetNoteDetail.styl
@@ -152,4 +152,21 @@ body[data-theme="solarized-dark"]
.tabList
background-color $ui-solarized-dark-noteDetail-backgroundColor
- color $ui-solarized-dark-text-color
\ No newline at end of file
+ color $ui-solarized-dark-text-color
+
+body[data-theme="monokai"]
+ .root
+ border-left 1px solid $ui-monokai-borderColor
+ background-color $ui-monokai-noteDetail-backgroundColor
+
+ .body
+ background-color $ui-monokai-noteDetail-backgroundColor
+
+ .body .description textarea
+ background-color $ui-monokai-noteDetail-backgroundColor
+ color $ui-monokai-text-color
+ border 1px solid $ui-monokai-borderColor
+
+ .tabList
+ background-color $ui-monokai-noteDetail-backgroundColor
+ color $ui-monokai-text-color
\ No newline at end of file
diff --git a/browser/main/Detail/TagSelect.styl b/browser/main/Detail/TagSelect.styl
index 18d4d2e0..0ff4c6a3 100644
--- a/browser/main/Detail/TagSelect.styl
+++ b/browser/main/Detail/TagSelect.styl
@@ -81,4 +81,20 @@ body[data-theme="solarized-dark"]
.newTag
border-color none
background-color transparent
- color $ui-solarized-dark-text-color
\ No newline at end of file
+ color $ui-solarized-dark-text-color
+
+body[data-theme="monokai"]
+ .tag
+ background-color $ui-monokai-button-backgroundColor
+
+ .tag-removeButton
+ border-color $ui-button--focus-borderColor
+ background-color transparent
+
+ .tag-label
+ color $ui-monokai-text-color
+
+ .newTag
+ border-color none
+ background-color transparent
+ color $ui-monokai-text-color
diff --git a/browser/main/Detail/ToggleModeButton.styl b/browser/main/Detail/ToggleModeButton.styl
index 185a780c..2e7ab5fa 100644
--- a/browser/main/Detail/ToggleModeButton.styl
+++ b/browser/main/Detail/ToggleModeButton.styl
@@ -56,3 +56,10 @@ body[data-theme="solarized-dark"]
.active
background-color #1EC38B
box-shadow 2px 0px 7px #222222
+
+body[data-theme="monokai"]
+ .control-toggleModeButton
+ background-color #272822
+ .active
+ background-color #1EC38B
+ box-shadow 2px 0px 7px #222222
diff --git a/browser/main/Main.js b/browser/main/Main.js
index 14a56225..9c15a0fe 100644
--- a/browser/main/Main.js
+++ b/browser/main/Main.js
@@ -15,6 +15,7 @@ import eventEmitter from 'browser/main/lib/eventEmitter'
import { hashHistory } from 'react-router'
import store from 'browser/main/store'
import i18n from 'browser/lib/i18n'
+import { getLocales } from 'browser/lib/Languages'
const path = require('path')
const electron = require('electron')
const { remote } = electron
@@ -143,7 +144,8 @@ class Main extends React.Component {
const supportedThemes = [
'dark',
'white',
- 'solarized-dark'
+ 'solarized-dark',
+ 'monokai'
]
if (supportedThemes.indexOf(config.ui.theme) !== -1) {
@@ -152,24 +154,7 @@ class Main extends React.Component {
document.body.setAttribute('data-theme', 'default')
}
- const supportedLanguages = [
- 'sq',
- 'zh-CN',
- 'zh-TW',
- 'da',
- 'fr',
- 'de',
- 'hu',
- 'ja',
- 'ko',
- 'no',
- 'pl',
- 'pt',
- 'ru',
- 'es-ES'
- ]
-
- if (supportedLanguages.indexOf(config.ui.language) !== -1) {
+ if (getLocales().indexOf(config.ui.language) !== -1) {
i18n.setLocale(config.ui.language)
} else {
i18n.setLocale('en')
diff --git a/browser/main/NewNoteButton/NewNoteButton.styl b/browser/main/NewNoteButton/NewNoteButton.styl
index 81ff7e8d..e8e4b5f0 100644
--- a/browser/main/NewNoteButton/NewNoteButton.styl
+++ b/browser/main/NewNoteButton/NewNoteButton.styl
@@ -74,4 +74,8 @@ body[data-theme="dark"]
body[data-theme="solarized-dark"]
.root, .root--expanded
- background-color $ui-solarized-dark-noteList-backgroundColor
\ No newline at end of file
+ background-color $ui-solarized-dark-noteList-backgroundColor
+
+body[data-theme="monokai"]
+ .root, .root--expanded
+ background-color $ui-monokai-noteList-backgroundColor
diff --git a/browser/main/NoteList/NoteList.styl b/browser/main/NoteList/NoteList.styl
index 312f5143..ea261208 100644
--- a/browser/main/NoteList/NoteList.styl
+++ b/browser/main/NoteList/NoteList.styl
@@ -113,4 +113,28 @@ body[data-theme="solarized-dark"]
.control-button--active
color $ui-solarized-dark-text-color
&:active
- color $ui-solarized-dark-text-color
\ No newline at end of file
+ color $ui-solarized-dark-text-color
+
+body[data-theme="monokai"]
+ .root
+ border-color $ui-monokai-borderColor
+ background-color $ui-monokai-noteList-backgroundColor
+
+ .control
+ background-color $ui-monokai-noteList-backgroundColor
+ border-color $ui-monokai-borderColor
+
+ .control-sortBy-select
+ &:hover
+ transition 0.2s
+ color $ui-monokai-text-color
+
+ .control-button
+ color $ui-monokai-inactive-text-color
+ &:hover
+ color $ui-monokai-text-color
+
+ .control-button--active
+ color $ui-monokai-text-color
+ &:active
+ color $ui-monokai-text-color
diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js
index e413c647..876de0c0 100644
--- a/browser/main/NoteList/index.js
+++ b/browser/main/NoteList/index.js
@@ -343,11 +343,10 @@ class NoteList extends React.Component {
}
if (location.pathname.match(/\/tags/)) {
+ const listOfTags = params.tagname.split(' ')
return data.noteMap.map(note => {
return note
- }).filter(note => {
- return note.tags.includes(params.tagname)
- })
+ }).filter(note => listOfTags.every(tag => note.tags.includes(tag)))
}
return this.getContextNotes()
@@ -456,12 +455,19 @@ class NoteList extends React.Component {
}
handleDragStart (e, note) {
- const { selectedNoteKeys } = this.state
+ let { selectedNoteKeys } = this.state
+ const noteKey = getNoteKey(note)
+
+ if (!selectedNoteKeys.includes(noteKey)) {
+ selectedNoteKeys = []
+ selectedNoteKeys.push(noteKey)
+ }
+
const notes = this.notes.map((note) => Object.assign({}, note))
const selectedNotes = findNotesByKeys(notes, selectedNoteKeys)
const noteData = JSON.stringify(selectedNotes)
e.dataTransfer.setData('note', noteData)
- this.setState({ selectedNoteKeys: [] })
+ this.selectNextNote()
}
handleNoteContextMenu (e, uniqueKey) {
@@ -916,7 +922,7 @@ class NoteList extends React.Component {
if (note.isTrashed !== true || location.pathname === '/trashed') return true
})
- moment.locale('en', {
+ moment.updateLocale('en', {
relativeTime: {
future: 'in %s',
past: '%s ago',
diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl
index 666ae0cd..ecab70d0 100644
--- a/browser/main/SideNav/SideNav.styl
+++ b/browser/main/SideNav/SideNav.styl
@@ -117,3 +117,8 @@ body[data-theme="solarized-dark"]
.root, .root--folded
background-color $ui-solarized-dark-backgroundColor
border-right 1px solid $ui-solarized-dark-borderColor
+
+body[data-theme="monokai"]
+ .root, .root--folded
+ background-color $ui-monokai-backgroundColor
+ border-right 1px solid $ui-monokai-borderColor
diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js
index f8a65013..6b53478e 100644
--- a/browser/main/SideNav/index.js
+++ b/browser/main/SideNav/index.js
@@ -145,20 +145,27 @@ class SideNav extends React.Component {
tagListComponent () {
const { data, location, config } = this.props
+ const relatedTags = this.getRelatedTags(this.getActiveTags(location.pathname), data.noteMap)
let tagList = _.sortBy(data.tagNoteMap.map(
- (tag, name) => ({name, size: tag.size})),
- ['name']
- )
+ (tag, name) => ({ name, size: tag.size, related: relatedTags.has(name) })
+ ), ['name'])
if (config.sortTagsBy === 'COUNTER') {
tagList = _.sortBy(tagList, item => (0 - item.size))
}
+ if (config.ui.showOnlyRelatedTags && (relatedTags.size > 0)) {
+ tagList = tagList.filter(
+ tag => tag.related
+ )
+ }
return (
tagList.map(tag => {
return (
@@ -167,10 +174,30 @@ class SideNav extends React.Component {
)
}
+ getRelatedTags (activeTags, noteMap) {
+ if (activeTags.length === 0) {
+ return new Set()
+ }
+ const relatedNotes = noteMap.map(
+ note => ({key: note.key, tags: note.tags})
+ ).filter(
+ note => activeTags.every(tag => note.tags.includes(tag))
+ )
+ let relatedTags = new Set()
+ relatedNotes.forEach(note => note.tags.map(tag => relatedTags.add(tag)))
+ return relatedTags
+ }
+
getTagActive (path, tag) {
+ return this.getActiveTags(path).includes(tag)
+ }
+
+ getActiveTags (path) {
const pathSegments = path.split('/')
- const pathTag = pathSegments[pathSegments.length - 1]
- return pathTag === tag
+ const tags = pathSegments[pathSegments.length - 1]
+ return (tags === 'alltags')
+ ? []
+ : tags.split(' ')
}
handleClickTagListItem (name) {
@@ -192,6 +219,19 @@ class SideNav extends React.Component {
})
}
+ handleClickNarrowToTag (tag) {
+ const { router } = this.context
+ const { location } = this.props
+ let listOfTags = this.getActiveTags(location.pathname)
+ const indexOfTag = listOfTags.indexOf(tag)
+ if (indexOfTag > -1) {
+ listOfTags.splice(indexOfTag, 1)
+ } else {
+ listOfTags.push(tag)
+ }
+ router.push(`/tags/${listOfTags.join(' ')}`)
+ }
+
emptyTrash (entries) {
const { dispatch } = this.props
const deletionPromises = entries.map((note) => {
diff --git a/browser/main/StatusBar/StatusBar.styl b/browser/main/StatusBar/StatusBar.styl
index 9f189fec..52cc4b02 100644
--- a/browser/main/StatusBar/StatusBar.styl
+++ b/browser/main/StatusBar/StatusBar.styl
@@ -69,3 +69,14 @@ body[data-theme="dark"]
navDarkButtonColor()
border-color $ui-dark-borderColor
border-left 1px solid $ui-dark-borderColor
+
+body[data-theme="monokai"]
+ navButtonColor()
+ .zoom
+ border-color $ui-dark-borderColor
+ color $ui-monokai-text-color
+ &:hover
+ transition 0.15s
+ color $ui-monokai-active-color
+ &:active
+ color $ui-monokai-active-color
diff --git a/browser/main/TopBar/TopBar.styl b/browser/main/TopBar/TopBar.styl
index 0956571f..7654f66f 100644
--- a/browser/main/TopBar/TopBar.styl
+++ b/browser/main/TopBar/TopBar.styl
@@ -234,3 +234,25 @@ body[data-theme="solarized-dark"]
input
background-color $ui-solarized-dark-noteList-backgroundColor
color $ui-solarized-dark-text-color
+
+body[data-theme="monokai"]
+ .root, .root--expanded
+ background-color $ui-monokai-noteList-backgroundColor
+
+ .control
+ border-color $ui-monokai-borderColor
+ .control-search
+ background-color $ui-monokai-noteList-backgroundColor
+
+ .control-search-icon
+ absolute top bottom left
+ line-height 32px
+ width 35px
+ color $ui-monokai-inactive-text-color
+ background-color $ui-monokai-noteList-backgroundColor
+
+ .control-search-input
+ background-color $ui-monokai-noteList-backgroundColor
+ input
+ background-color $ui-monokai-noteList-backgroundColor
+ color $ui-monokai-text-color
diff --git a/browser/main/global.styl b/browser/main/global.styl
index 613c7611..7025163f 100644
--- a/browser/main/global.styl
+++ b/browser/main/global.styl
@@ -134,4 +134,10 @@ body[data-theme="solarized-dark"]
.sortableItemHelper
color: $ui-solarized-dark-text-color
+body[data-theme="monokai"]
+ .ModalBase
+ .modalBack
+ background-color $ui-monokai-backgroundColor
+ .sortableItemHelper
+ color: $ui-monokai-text-color
diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js
index 3e1a2162..ee8a57c7 100644
--- a/browser/main/lib/ConfigManager.js
+++ b/browser/main/lib/ConfigManager.js
@@ -135,6 +135,8 @@ function set (updates) {
document.body.setAttribute('data-theme', 'white')
} else if (newConfig.ui.theme === 'solarized-dark') {
document.body.setAttribute('data-theme', 'solarized-dark')
+ } else if (newConfig.ui.theme === 'monokai') {
+ document.body.setAttribute('data-theme', 'monokai')
} else {
document.body.setAttribute('data-theme', 'default')
}
diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js
new file mode 100644
index 00000000..c2e7f6d6
--- /dev/null
+++ b/browser/main/lib/dataApi/attachmentManagement.js
@@ -0,0 +1,204 @@
+const uniqueSlug = require('unique-slug')
+const fs = require('fs')
+const path = require('path')
+const findStorage = require('browser/lib/findStorage')
+const mdurl = require('mdurl')
+const escapeStringRegexp = require('escape-string-regexp')
+
+const STORAGE_FOLDER_PLACEHOLDER = ':storage'
+const DESTINATION_FOLDER = 'attachments'
+
+/**
+ * @description
+ * Copies a copy of an attachment to the storage folder specified by the given key and return the generated attachment name.
+ * Renames the file to match a unique file name.
+ *
+ * @param {String} sourceFilePath The source path of the attachment to be copied
+ * @param {String} storageKey Storage key of the destination storage
+ * @param {String} noteKey Key of the current note. Will be used as subfolder in :storage
+ * @param {boolean} useRandomName determines whether a random filename for the new file is used. If false the source file name is used
+ * @return {Promise} name (inclusive extension) of the generated file
+ */
+function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = true) {
+ return new Promise((resolve, reject) => {
+ if (!sourceFilePath) {
+ reject('sourceFilePath has to be given')
+ }
+
+ if (!storageKey) {
+ reject('storageKey has to be given')
+ }
+
+ if (!noteKey) {
+ reject('noteKey has to be given')
+ }
+
+ try {
+ if (!fs.existsSync(sourceFilePath)) {
+ reject('source file does not exist')
+ }
+
+ const targetStorage = findStorage.findStorage(storageKey)
+
+ const inputFile = fs.createReadStream(sourceFilePath)
+ let destinationName
+ if (useRandomName) {
+ destinationName = `${uniqueSlug()}${path.extname(sourceFilePath)}`
+ } else {
+ destinationName = path.basename(sourceFilePath)
+ }
+ const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
+ createAttachmentDestinationFolder(targetStorage.path, noteKey)
+ const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName))
+ inputFile.pipe(outputFile)
+ resolve(destinationName)
+ } catch (e) {
+ return reject(e)
+ }
+ })
+}
+
+function createAttachmentDestinationFolder (destinationStoragePath, noteKey) {
+ let destinationDir = path.join(destinationStoragePath, DESTINATION_FOLDER)
+ if (!fs.existsSync(destinationDir)) {
+ fs.mkdirSync(destinationDir)
+ }
+ destinationDir = path.join(destinationStoragePath, DESTINATION_FOLDER, noteKey)
+ if (!fs.existsSync(destinationDir)) {
+ fs.mkdirSync(destinationDir)
+ }
+}
+
+/**
+ * @description Fixes the URLs embedded in the generated HTML so that they again refer actual local files.
+ * @param {String} renderedHTML HTML in that the links should be fixed
+ * @param {String} storagePath Path of the current storage
+ * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
+ */
+function fixLocalURLS (renderedHTML, storagePath) {
+ return renderedHTML.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
+}
+
+/**
+ * @description Generates the markdown code for a given attachment
+ * @param {String} fileName Name of the attachment
+ * @param {String} path Path of the attachment
+ * @param {Boolean} showPreview Indicator whether the generated markdown should show a preview of the image. Note that at the moment only previews for images are supported
+ * @returns {String} Generated markdown code
+ */
+function generateAttachmentMarkdown (fileName, path, showPreview) {
+ return `${showPreview ? '!' : ''}[${fileName}](${path})`
+}
+
+/**
+ * @description Handles the drop-event of a file. Includes the necessary markdown code and copies the file to the corresponding storage folder.
+ * The method calls {CodeEditor#insertAttachmentMd()} to include the generated markdown at the needed place!
+ * @param {CodeEditor} codeEditor Markdown editor. Its insertAttachmentMd() method will be called to include the markdown code
+ * @param {String} storageKey Key of the current storage
+ * @param {String} noteKey Key of the current note
+ * @param {Event} dropEvent DropEvent
+ */
+function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) {
+ const file = dropEvent.dataTransfer.files[0]
+ const filePath = file.path
+ const originalFileName = path.basename(filePath)
+ const fileType = file['type']
+
+ copyAttachment(filePath, storageKey, noteKey).then((fileName) => {
+ const showPreview = fileType.startsWith('image')
+ const imageMd = generateAttachmentMarkdown(originalFileName, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName), showPreview)
+ codeEditor.insertAttachmentMd(imageMd)
+ })
+}
+
+/**
+ * @description Creates a new file in the storage folder belonging to the current note and inserts the correct markdown code
+ * @param {CodeEditor} codeEditor Markdown editor. Its insertAttachmentMd() method will be called to include the markdown code
+ * @param {String} storageKey Key of the current storage
+ * @param {String} noteKey Key of the current note
+ * @param {DataTransferItem} dataTransferItem Part of the past-event
+ */
+function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) {
+ if (!codeEditor) {
+ throw new Error('codeEditor has to be given')
+ }
+ if (!storageKey) {
+ throw new Error('storageKey has to be given')
+ }
+
+ if (!noteKey) {
+ throw new Error('noteKey has to be given')
+ }
+ if (!dataTransferItem) {
+ throw new Error('dataTransferItem has to be given')
+ }
+
+ const blob = dataTransferItem.getAsFile()
+ const reader = new FileReader()
+ let base64data
+ const targetStorage = findStorage.findStorage(storageKey)
+ const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
+ createAttachmentDestinationFolder(targetStorage.path, noteKey)
+
+ const imageName = `${uniqueSlug()}.png`
+ const imagePath = path.join(destinationDir, imageName)
+
+ reader.onloadend = function () {
+ base64data = reader.result.replace(/^data:image\/png;base64,/, '')
+ base64data += base64data.replace('+', ' ')
+ const binaryData = new Buffer(base64data, 'base64').toString('binary')
+ fs.writeFile(imagePath, binaryData, 'binary')
+ const imageMd = generateAttachmentMarkdown(imageName, imagePath, true)
+ codeEditor.insertAttachmentMd(imageMd)
+ }
+ reader.readAsDataURL(blob)
+}
+
+/**
+ * @description Returns all attachment paths of the given markdown
+ * @param {String} markdownContent content in which the attachment paths should be found
+ * @returns {String[]} Array of the relativ paths (starting with :storage) of the attachments of the given markdown
+ */
+function getAttachmentsInContent (markdownContent) {
+ const preparedInput = markdownContent.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep)
+ const regexp = new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + '([a-zA-Z0-9]|-)+' + escapeStringRegexp(path.sep) + '[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+)?', 'g')
+ return preparedInput.match(regexp)
+}
+
+/**
+ * @description Returns an array of the absolute paths of the attachments referenced in the given markdown code
+ * @param {String} markdownContent content in which the attachment paths should be found
+ * @param {String} storagePath path of the current storage
+ * @returns {String[]} Absolute paths of the referenced attachments
+ */
+function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) {
+ const temp = getAttachmentsInContent(markdownContent)
+ const result = []
+ for (const relativePath of temp) {
+ result.push(relativePath.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER)))
+ }
+ return result
+}
+
+/**
+ * @description Deletes all :storage and noteKey references from the given input.
+ * @param input Input in which the references should be deleted
+ * @param noteKey Key of the current note
+ * @returns {String} Input without the references
+ */
+function removeStorageAndNoteReferences (input, noteKey) {
+ return input.replace(new RegExp(mdurl.encode(path.sep), 'g'), path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey, 'g'), DESTINATION_FOLDER)
+}
+
+module.exports = {
+ copyAttachment,
+ fixLocalURLS,
+ generateAttachmentMarkdown,
+ handleAttachmentDrop,
+ handlePastImageEvent,
+ getAttachmentsInContent,
+ getAbsolutePathsOfAttachmentsInContent,
+ removeStorageAndNoteReferences,
+ STORAGE_FOLDER_PLACEHOLDER,
+ DESTINATION_FOLDER
+}
diff --git a/browser/main/lib/dataApi/copyImage.js b/browser/main/lib/dataApi/copyImage.js
index 6a79b8b7..24053bdd 100644
--- a/browser/main/lib/dataApi/copyImage.js
+++ b/browser/main/lib/dataApi/copyImage.js
@@ -2,6 +2,8 @@ const fs = require('fs')
const path = require('path')
const { findStorage } = require('browser/lib/findStorage')
+// TODO: ehhc: delete this
+
/**
* @description Copy an image and return the path.
* @param {String} filePath
@@ -21,8 +23,12 @@ function copyImage (filePath, storageKey, rename = true) {
const imageDir = path.join(targetStorage.path, 'images')
if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir)
const outputImage = fs.createWriteStream(path.join(imageDir, basename))
+ outputImage.on('error', reject)
+ inputImage.on('error', reject)
+ inputImage.on('end', () => {
+ resolve(basename)
+ })
inputImage.pipe(outputImage)
- resolve(basename)
} catch (e) {
return reject(e)
}
diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js
index 313bb85b..e4fec5f4 100755
--- a/browser/main/lib/dataApi/exportNote.js
+++ b/browser/main/lib/dataApi/exportNote.js
@@ -1,13 +1,9 @@
import copyFile from 'browser/main/lib/dataApi/copyFile'
-import {findStorage} from 'browser/lib/findStorage'
-import filenamify from 'filenamify'
+import { findStorage } from 'browser/lib/findStorage'
const fs = require('fs')
const path = require('path')
-const LOCAL_STORED_REGEX = /!\[(.*?)]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
-const IMAGES_FOLDER_NAME = 'images'
-
/**
* Export note together with images
*
@@ -28,21 +24,7 @@ function exportNote (storageKey, noteContent, targetPath, outputFormatter) {
throw new Error('Storage path is not found')
}
- let exportedData = noteContent.replace(LOCAL_STORED_REGEX, (match, dstFilename, srcFilename) => {
- dstFilename = filenamify(dstFilename, {replacement: '_'})
- if (!path.extname(dstFilename)) {
- dstFilename += path.extname(srcFilename)
- }
-
- const dstRelativePath = path.join(IMAGES_FOLDER_NAME, dstFilename)
-
- exportTasks.push({
- src: path.join(IMAGES_FOLDER_NAME, srcFilename),
- dst: dstRelativePath
- })
-
- return ``
- })
+ let exportedData = noteContent
if (outputFormatter) {
exportedData = outputFormatter(exportedData, exportTasks)
diff --git a/browser/main/lib/dataApi/moveNote.js b/browser/main/lib/dataApi/moveNote.js
index 928d331b..cffb5c53 100644
--- a/browser/main/lib/dataApi/moveNote.js
+++ b/browser/main/lib/dataApi/moveNote.js
@@ -68,6 +68,8 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
return noteData
})
.then(function moveImages (noteData) {
+ if (oldStorage.path === newStorage.path) return noteData
+
const searchImagesRegex = /!\[.*?]\(\s*?\/:storage\/(.*\.\S*?)\)/gi
let match = searchImagesRegex.exec(noteData.content)
@@ -75,6 +77,7 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
while (match != null) {
const [, filename] = match
const oldPath = path.join(oldStorage.path, 'images', filename)
+ // TODO: ehhc: attachmentManagement
moveTasks.push(
copyImage(oldPath, noteData.storage, false)
.then(() => {
diff --git a/browser/main/modals/CreateFolderModal.styl b/browser/main/modals/CreateFolderModal.styl
index 45f2e852..1b96e123 100644
--- a/browser/main/modals/CreateFolderModal.styl
+++ b/browser/main/modals/CreateFolderModal.styl
@@ -102,3 +102,29 @@ body[data-theme="solarized-dark"]
.control-confirmButton
colorSolarizedDarkPrimaryButton()
+
+body[data-theme="monokai"]
+ .root
+ modalMonokai()
+ width 500px
+ height 270px
+ overflow hidden
+ position relative
+
+ .header
+ background-color transparent
+ border-color $ui-dark-borderColor
+ color $ui-monokai-text-color
+
+ .control-folder-label
+ color $ui-monokai-text-color
+
+ .control-folder-input
+ border 1px solid $ui-input--create-folder-modal
+ color white
+
+ .description
+ color $ui-inactive-text-color
+
+ .control-confirmButton
+ colorMonokaiPrimaryButton()
diff --git a/browser/main/modals/NewNoteModal.styl b/browser/main/modals/NewNoteModal.styl
index 748ab88c..db14133f 100644
--- a/browser/main/modals/NewNoteModal.styl
+++ b/browser/main/modals/NewNoteModal.styl
@@ -81,3 +81,19 @@ body[data-theme="solarized-dark"]
.description
color $ui-solarized-dark-text-color
+body[data-theme="monokai"]
+ .root
+ background-color transparent
+
+ .header
+ color $ui-monokai-text-color
+
+ .control-button
+ border-color $ui-monokai-borderColor
+ color $ui-monokai-text-color
+ background-color transparent
+ &:focus
+ colorDarkPrimaryButton()
+
+ .description
+ color $ui-monokai-text-color
diff --git a/browser/main/modals/PreferencesModal/ConfigTab.styl b/browser/main/modals/PreferencesModal/ConfigTab.styl
index f6f7ace9..0e5f81fb 100644
--- a/browser/main/modals/PreferencesModal/ConfigTab.styl
+++ b/browser/main/modals/PreferencesModal/ConfigTab.styl
@@ -133,6 +133,11 @@ colorSolarizedDarkControl()
background-color $ui-solarized-dark-button-backgroundColor
color $ui-solarized-dark-text-color
+colorMonokaiControl()
+ border none
+ background-color $ui-monokai-button-backgroundColor
+ color $ui-monokai-text-color
+
body[data-theme="dark"]
.root
@@ -189,4 +194,29 @@ body[data-theme="solarized-dark"]
select, .group-section-control-input
colorSolarizedDarkControl()
+body[data-theme="monokai"]
+ .root
+ color $ui-monokai-text-color
+ .group-header
+ color $ui-monokai-text-color
+ border-color $ui-monokai-borderColor
+
+ .group-header2
+ color $ui-monokai-text-color
+
+ .group-section-control-input
+ border-color $ui-monokai-borderColor
+
+ .group-control
+ border-color $ui-monokai-borderColor
+ .group-control-leftButton
+ colorDarkDefaultButton()
+ border-color $ui-monokai-borderColor
+ .group-control-rightButton
+ colorMonokaiPrimaryButton()
+ .group-hint
+ colorMonokaiControl()
+ .group-section-control
+ select, .group-section-control-input
+ colorMonokaiControl()
diff --git a/browser/main/modals/PreferencesModal/Crowdfunding.styl b/browser/main/modals/PreferencesModal/Crowdfunding.styl
index 930c33f0..3d4af539 100644
--- a/browser/main/modals/PreferencesModal/Crowdfunding.styl
+++ b/browser/main/modals/PreferencesModal/Crowdfunding.styl
@@ -33,4 +33,10 @@ body[data-theme="solarized-dark"]
.root
color $ui-solarized-dark-text-color
p
- color $ui-solarized-dark-text-color
\ No newline at end of file
+ color $ui-solarized-dark-text-color
+
+body[data-theme="monokai"]
+ .root
+ color $ui-monokai-text-color
+ p
+ color $ui-monokai-text-color
diff --git a/browser/main/modals/PreferencesModal/FolderItem.styl b/browser/main/modals/PreferencesModal/FolderItem.styl
index acc4cbfb..8bcf2b02 100644
--- a/browser/main/modals/PreferencesModal/FolderItem.styl
+++ b/browser/main/modals/PreferencesModal/FolderItem.styl
@@ -126,3 +126,26 @@ body[data-theme="solarized-dark"]
.folderItem-right-dangerButton
colorSolarizedDarkPrimaryButton()
+
+body[data-theme="monokai"]
+ .folderItem
+ &:hover
+ background-color $ui-monokai-button-backgroundColor
+
+ .folderItem-left-danger
+ color $danger-color
+
+ .folderItem-left-key
+ color $ui-dark-inactive-text-color
+
+ .folderItem-left-colorButton
+ colorMonokaiPrimaryButton()
+
+ .folderItem-right-button
+ colorMonokaiPrimaryButton()
+
+ .folderItem-right-confirmButton
+ colorMonokaiPrimaryButton()
+
+ .folderItem-right-dangerButton
+ colorMonokaiPrimaryButton()
diff --git a/browser/main/modals/PreferencesModal/InfoTab.styl b/browser/main/modals/PreferencesModal/InfoTab.styl
index cc04a10f..491fc4d4 100644
--- a/browser/main/modals/PreferencesModal/InfoTab.styl
+++ b/browser/main/modals/PreferencesModal/InfoTab.styl
@@ -68,3 +68,10 @@ body[data-theme="solarized-dark"]
.list
a
color $ui-solarized-dark-active-color
+
+body[data-theme="monokai"]
+ .root
+ color $ui-monokai-text-color
+.list
+ a
+ color $ui-monokai-active-color
diff --git a/browser/main/modals/PreferencesModal/PreferencesModal.styl b/browser/main/modals/PreferencesModal/PreferencesModal.styl
index 57b5dbad..d21f6c28 100644
--- a/browser/main/modals/PreferencesModal/PreferencesModal.styl
+++ b/browser/main/modals/PreferencesModal/PreferencesModal.styl
@@ -116,3 +116,26 @@ body[data-theme="solarized-dark"]
&:hover
color white
+body[data-theme="monokai"]
+ .root
+ background-color transparent
+ .top-bar
+ background-color transparent
+ border-color $ui-monokai-borderColor
+ p
+ color $ui-monokai-text-color
+ .nav
+ background-color transparent
+ border-color $ui-monokai-borderColor
+ .nav-button
+ background-color transparent
+ color $ui-monokai-text-color
+ &:hover
+ color $ui-monokai-text-color
+
+ .nav-button--active
+ @extend .nav-button
+ color $ui-monokai-button--active-color
+ background-color $ui-monokai-button--active-backgroundColor
+ &:hover
+ color white
diff --git a/browser/main/modals/PreferencesModal/StoragesTab.styl b/browser/main/modals/PreferencesModal/StoragesTab.styl
index 230f0aed..9804d7e7 100644
--- a/browser/main/modals/PreferencesModal/StoragesTab.styl
+++ b/browser/main/modals/PreferencesModal/StoragesTab.styl
@@ -199,3 +199,40 @@ body[data-theme="solarized-dark"]
colorDarkDefaultButton()
border-color $ui-solarized-dark-borderColor
+body[data-theme="monokai"]
+ .root
+ color $ui-monokai-text-color
+
+ .folderList-item
+ border-bottom $ui-monokai-borderColor
+
+ .folderList-empty
+ color $ui-monokai-text-color
+
+ .list-empty
+ color $ui-monokai-text-color
+ .list-control-addStorageButton
+ border-color $ui-monokai-button-backgroundColor
+ background-color $ui-monokai-button-backgroundColor
+ color $ui-monokai-text-color
+
+ .addStorage-header
+ color $ui-monokai-text-color
+ border-color $ui-monokai-borderColor
+
+ .addStorage-body-section-name-input
+ border-color $$ui-monokai-borderColor
+
+ .addStorage-body-section-type-description
+ color $ui-monokai-text-color
+
+ .addStorage-body-section-path-button
+ colorPrimaryButton()
+ .addStorage-body-control
+ border-color $ui-monokai-borderColor
+
+ .addStorage-body-control-createButton
+ colorDarkPrimaryButton()
+ .addStorage-body-control-cancelButton
+ colorDarkDefaultButton()
+ border-color $ui-monokai-borderColor
diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js
index a092129a..a607f548 100644
--- a/browser/main/modals/PreferencesModal/UiTab.js
+++ b/browser/main/modals/PreferencesModal/UiTab.js
@@ -10,6 +10,7 @@ import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir'
import _ from 'lodash'
import i18n from 'browser/lib/i18n'
+import { getLanguages } from 'browser/lib/Languages'
const OSX = global.process.platform === 'darwin'
@@ -65,6 +66,7 @@ class UiTab extends React.Component {
language: this.refs.uiLanguage.value,
showCopyNotification: this.refs.showCopyNotification.checked,
confirmDeletion: this.refs.confirmDeletion.checked,
+ showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked,
disableDirectWrite: this.refs.uiD2w != null
? this.refs.uiD2w.checked
: false
@@ -170,6 +172,7 @@ class UiTab extends React.Component {
+
@@ -182,22 +185,9 @@ class UiTab extends React.Component {
onChange={(e) => this.handleUIChange(e)}
ref='uiLanguage'
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ {
+ getLanguages().map((language) => )
+ }
@@ -222,6 +212,16 @@ class UiTab extends React.Component {
{i18n.__('Show a confirmation dialog when deleting notes')}
+
+
+
{
global.process.platform === 'win32'
?
diff --git a/browser/styles/index.styl b/browser/styles/index.styl
index 6fb208b1..7d32e77a 100644
--- a/browser/styles/index.styl
+++ b/browser/styles/index.styl
@@ -118,6 +118,16 @@ colorSolarizedDarkPrimaryButton()
&:active:hover
background-color $dark-primary-button-background--active
+colorMonokaiPrimaryButton()
+ color $ui-monokai-text-color
+ background-color $ui-monokai-button-backgroundColor
+ border none
+ &:hover
+ background-color $dark-primary-button-background--hover
+ &:active
+ &:active:hover
+ background-color $dark-primary-button-background--active
+
// Danger button(Brand color)
$danger-button-background = #c9302c
@@ -348,3 +358,29 @@ modalSolarizedDark()
background-color $ui-solarized-dark-backgroundColor
overflow hidden
border-radius $modal-border-radius
+
+/******* Monokai theme ********/
+$ui-monokai-backgroundColor = #272822
+$ui-monokai-noteList-backgroundColor = #272822
+$ui-monokai-noteDetail-backgroundColor = #272822
+
+$ui-monokai-text-color = #f8f8f2
+$ui-monokai-active-color = #f92672
+
+$ui-monokai-borderColor = #373831
+
+$ui-monokai-tag-backgroundColor = #f92672
+
+$ui-monokai-button-backgroundColor = #373831
+$ui-monokai-button--active-color = white
+$ui-monokai-button--active-backgroundColor = #f92672
+$ui-monokai-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%)
+$ui-monokai-button--focus-borderColor = lighten(#369DCD, 25%)
+
+modalmonokai()
+ position relative
+ z-index $modal-z-index
+ width 100%
+ background-color $ui-monokai-backgroundColor
+ overflow hidden
+ border-radius $modal-border-radius
\ No newline at end of file
diff --git a/locales/es-ES.json b/locales/es-ES.json
index ea20641b..d533579c 100644
--- a/locales/es-ES.json
+++ b/locales/es-ES.json
@@ -87,7 +87,7 @@
"Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "Boostnote es utilizado en alrededor de 200 países y regiones diferentes por una increíble comunidad de desarrolladores.",
"To continue supporting this growth, and to satisfy community expectations,": "Para continuar apoyando este crecimiento y satisfacer las expectativas de la comunidad,",
"we would like to invest more time and resources in this project.": "nos gustaría invertir más tiempo y recursos en este proyecto.",
- "If you like this project and see its potential, you can help by supporting us on OpenCollective!": "Si te gusta este proyecto y ves potencial en él, ¡puedes ayudar apoyándonos en OpenCollective!",
+ "If you like this project and see its potential, you can help by supporting us on OpenCollective!": "Si te gusta este proyecto y ves su potencial, ¡puedes ayudar apoyándonos en OpenCollective!",
"Thanks,": "Gracias,",
"Boostnote maintainers": "Equipo de Boostnote",
"Support via OpenCollective": "Contribuir vía OpenCollective",
@@ -149,5 +149,5 @@
"Sanitization": "Saneamiento",
"Only allow secure html tags (recommended)": "Solo permitir etiquetas html seguras (recomendado)",
"Allow styles": "Permitir estilos",
- "Allow dangerous html tags": "Permitir etiques html peligrosas"
+ "Allow dangerous html tags": "Permitir etiquetas html peligrosas"
}
diff --git a/locales/zh-TW.json b/locales/zh-TW.json
index 06f40608..bc60cfcc 100755
--- a/locales/zh-TW.json
+++ b/locales/zh-TW.json
@@ -1,135 +1,135 @@
{
"Notes": "筆記",
"Tags": "標籤",
- "Preferences": "首選項",
- "Make a note": "新建筆記",
+ "Preferences": "偏好設定",
+ "Make a note": "做點筆記",
"Ctrl": "Ctrl",
"Ctrl(^)": "Ctrl",
- "to create a new note": "新建筆記",
+ "to create a new note": "新增筆記",
"Toggle Mode": "切換模式",
"Trash": "廢紙簍",
- "MODIFICATION DATE": "更改時間",
- "Words": "單詞",
+ "MODIFICATION DATE": "修改時間",
+ "Words": "單字",
"Letters": "字數",
- "STORAGE": "本地儲存",
+ "STORAGE": "本機儲存空間",
"FOLDER": "資料夾",
- "CREATION DATE": "創建時間",
- "NOTE LINK": "筆記鏈接",
+ "CREATION DATE": "建立時間",
+ "NOTE LINK": "筆記連結",
".md": ".md",
".txt": ".txt",
".html": ".html",
"Print": "列印",
- "Your preferences for Boostnote": "個性設置",
- "Storages": "本地儲存",
- "Add Storage Location": "添加一個本地儲存位置",
- "Add Folder": "新建資料夾",
- "Open Storage folder": "打開一個本地儲存位置",
- "Unlink": "取消鏈接",
+ "Your preferences for Boostnote": "Boostnote 偏好設定",
+ "Storages": "本機儲存空間",
+ "Add Storage Location": "新增一個本機儲存位置",
+ "Add Folder": "新增資料夾",
+ "Open Storage folder": "開啟儲存資料夾",
+ "Unlink": "解除連結",
"Edit": "編輯",
"Delete": "刪除",
"Interface": "界面",
"Interface Theme": "主題",
- "Default": "默認",
+ "Default": "預設",
"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 Font Size": "編輯器字型大小",
"Editor Font Family": "編輯器字體",
- "Editor Indent Style": "縮進風格",
+ "Editor Indent Style": "縮排風格",
"Spaces": "空格",
"Tabs": "Tabs",
- "Switch to Preview": "快速切換到預覽界面",
- "When Editor Blurred": "當編輯器失去焦點的時候,切換到預覽界面",
- "When Editor Blurred, Edit On Double Click": "當編輯器失去焦點的時候預覽,雙擊切換到編輯界面",
- "On Right Click": "右鍵點擊切換兩個界面",
+ "Switch to Preview": "切回預覽頁面的時機",
+ "When Editor Blurred": "當編輯器失去焦點時",
+ "When Editor Blurred, Edit On Double Click": "當編輯器失去焦點時,雙擊切換到編輯畫面",
+ "On Right Click": "點擊右鍵切換兩個頁面",
"Editor Keymap": "編輯器 Keymap",
- "default": "默認",
+ "default": "預設",
"vim": "vim",
"emacs": "emacs",
- "⚠️ Please restart boostnote after you change the keymap": "⚠️ 設置好快捷鍵後,記得重啟设置好快捷键后,记得重启boostnote",
+ "⚠️ 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": "粘貼網頁鏈接的時候,顯示為網頁標題",
- "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": "LaTeX 單行開頭分隔符",
- "LaTeX Inline Close Delimiter": "LaTeX 單行結尾分隔符",
- "LaTeX Block Open Delimiter": "LaTeX 多行開頭分隔符",
- "LaTeX Block Close Delimiter": "LaTeX 多行結尾分隔符",
- "Community": "社區",
+ "Allow editor to scroll past the last line": "允許編輯器捲軸捲動超過最後一行",
+ "Bring in web page title when pasting URL on editor": "在編輯器貼上網址的時候,自動加上網頁標題",
+ "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": "LaTeX 單行開頭符號",
+ "LaTeX Inline Close Delimiter": "LaTeX 單行結尾符號",
+ "LaTeX Block Open Delimiter": "LaTeX 多行開頭符號",
+ "LaTeX Block Close Delimiter": "LaTeX 多行結尾符號",
+ "Community": "社群",
"Subscribe to Newsletter": "訂閱郵件",
"GitHub": "GitHub",
"Blog": "部落格",
- "Facebook Group": "Facebook Group",
+ "Facebook Group": "Facebook 社團",
"Twitter": "Twitter",
"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": "官網",
"Development": "開發",
- " : Development configurations for Boostnote.": " : Boostnote的開發配置",
+ " : 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": "眾籌",
+ "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.": "大約有200個不同的國家和地區的優秀開發者們都在使用Boostnote!",
- "To continue supporting this growth, and to satisfy community expectations,": "為了繼續支持這種發展,和滿足社區的期待,",
+ "Thank you for using Boostnote!": "謝謝你使用 Boostnote!",
+ "Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "大約有 200 個不同的國家和地區的優秀開發者們都在使用 Boostnote!",
+ "To continue supporting this growth, and to satisfy community expectations,": "為了繼續支持這種發展,和滿足社群的期待,",
"we would like to invest more time and resources in this project.": "我們非常願意投入更多的時間和資源到這個專案中。",
- "If you like this project and see its potential, you can help by supporting us on OpenCollective!": "如果你喜歡這款軟體並且看好它的潛力, 請在OpenCollective上支持我們!",
+ "If you like this project and see its potential, you can help by supporting us on OpenCollective!": "如果你喜歡這款軟體並且看好它的潛力, 請在 OpenCollective 上支持我們!",
"Thanks,": "十分感謝!",
- "Boostnote maintainers": "Boostnote的維護人員",
- "Support via OpenCollective": "在OpenCollective上支持我們",
+ "Boostnote maintainers": "Boostnote 的維護人員",
+ "Support via OpenCollective": "在 OpenCollective 上支持我們",
"Language": "語言",
"English": "English",
"German": "German",
"French": "French",
- "Show \"Saved to Clipboard\" notification when copying": "複製的時候,顯示 \"已複製\" 提示",
+ "Show \"Saved to Clipboard\" notification when copying": "複製的時候,顯示 \"已複製到剪貼簿\" 的通知",
"All Notes": "所有筆記",
- "Starred": "星標收藏",
+ "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": "代碼筆記",
- "This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "創建代碼片段,支持多種語法代碼片段",
- "Tab to switch format": "使用Tab鍵切換格式",
- "Updated": "更新時間",
- "Created": "創建時間",
- "Alphabetically": "A~Z排序",
- "Default View": "默認視圖",
- "Compressed View": "列表視圖",
- "Search": "搜索",
+ "Markdown Note": "Markdown 筆記",
+ "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "建立文件、清單,也可以使用程式碼區塊甚至是 Latex 區塊。",
+ "Snippet Note": "程式碼片段筆記",
+ "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": "保存",
+ "Blog Address": "部落格網址",
+ "Save": "儲存",
"Auth": "Auth",
"Authentication Method": "認證方法",
"JWT": "JWT",
"USER": "USER",
"Token": "Token",
- "Storage": "本地儲存",
+ "Storage": "本機儲存空間",
"Hotkeys": "快捷鍵",
"Show/Hide Boostnote": "顯示/隱藏 Boostnote",
- "Restore": "恢復",
+ "Restore": "還原",
"Permanent Delete": "永久刪除",
"Confirm note deletion": "確認刪除筆記",
- "This will permanently remove this note.": "永久地刪除這條筆記",
- "Successfully applied!": "設置成功",
+ "This will permanently remove this note.": "這將會永久地刪除這條筆記",
+ "Successfully applied!": "設定成功",
"Albanian": "Albanian",
"Chinese (zh-CN)": "简体中文",
"Chinese (zh-TW)": "繁體中文",
@@ -142,11 +142,11 @@
"Spanish": "Spanish",
"You have to save!": "你必須儲存一下!",
"Russian": "Russian",
- "Editor Rulers": "Editor Rulers",
- "Enable": "開啟",
- "Disable": "關閉",
- "Sanitization": "代碼處理",
- "Only allow secure html tags (recommended)": "只允許安全的html標籤(推薦)",
+ "Editor Rulers": "編輯器中顯示垂直尺規",
+ "Enable": "啟用",
+ "Disable": "禁用",
+ "Sanitization": "過濾 HTML 程式碼",
+ "Only allow secure html tags (recommended)": "只允許安全的 HTML 標籤 (建議)",
"Allow styles": "允許樣式",
- "Allow dangerous html tags": "允許危險的html標籤"
+ "Allow dangerous html tags": "允許危險的 HTML 標籤"
}
diff --git a/package.json b/package.json
index 038d4c00..82c90896 100644
--- a/package.json
+++ b/package.json
@@ -92,6 +92,7 @@
"striptags": "^2.2.1",
"superagent": "^1.2.0",
"superagent-promise": "^1.0.3",
+ "unique-slug": "2.0.0",
"uuid": "^3.2.1"
},
"devDependencies": {
diff --git a/resources/app.png b/resources/app.png
index ac817bc6..ee1b3c93 100755
Binary files a/resources/app.png and b/resources/app.png differ
diff --git a/resources/boostnote-install.png b/resources/boostnote-install.png
index 6bb31b4d..980e672d 100755
Binary files a/resources/boostnote-install.png and b/resources/boostnote-install.png differ
diff --git a/resources/boostnote-install@2x.png b/resources/boostnote-install@2x.png
index 6b4dd5fb..e45e137f 100755
Binary files a/resources/boostnote-install@2x.png and b/resources/boostnote-install@2x.png differ
diff --git a/resources/dmg.png b/resources/dmg.png
index 49196123..3d2cd438 100755
Binary files a/resources/dmg.png and b/resources/dmg.png differ
diff --git a/resources/finder.png b/resources/finder.png
index 4caf9249..7adb292a 100644
Binary files a/resources/finder.png and b/resources/finder.png differ
diff --git a/resources/repository/top.png b/resources/repository/top.png
index ac8dc699..29dfc033 100644
Binary files a/resources/repository/top.png and b/resources/repository/top.png differ
diff --git a/resources/tray-icon-dark.png b/resources/tray-icon-dark.png
index 4aa26bfd..20b47832 100644
Binary files a/resources/tray-icon-dark.png and b/resources/tray-icon-dark.png differ
diff --git a/resources/tray-icon-dark@2x.png b/resources/tray-icon-dark@2x.png
index 5e58d5ef..7d43788d 100644
Binary files a/resources/tray-icon-dark@2x.png and b/resources/tray-icon-dark@2x.png differ
diff --git a/resources/tray-icon-default.png b/resources/tray-icon-default.png
index 08725fb7..942b0220 100644
Binary files a/resources/tray-icon-default.png and b/resources/tray-icon-default.png differ
diff --git a/resources/tray-icon-default@2x.png b/resources/tray-icon-default@2x.png
index c2a7e17e..9cf088d9 100644
Binary files a/resources/tray-icon-default@2x.png and b/resources/tray-icon-default@2x.png differ
diff --git a/resources/tray-icon.png b/resources/tray-icon.png
index 08725fb7..942b0220 100644
Binary files a/resources/tray-icon.png and b/resources/tray-icon.png differ
diff --git a/resources/tray-icon@2x.png b/resources/tray-icon@2x.png
index c2a7e17e..9cf088d9 100644
Binary files a/resources/tray-icon@2x.png and b/resources/tray-icon@2x.png differ
diff --git a/tests/components/__snapshots__/TagListItem.snapshot.test.js.snap b/tests/components/__snapshots__/TagListItem.snapshot.test.js.snap
index 0805edcd..80293ab4 100644
--- a/tests/components/__snapshots__/TagListItem.snapshot.test.js.snap
+++ b/tests/components/__snapshots__/TagListItem.snapshot.test.js.snap
@@ -1,19 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TagListItem renders correctly 1`] = `
-
+
`;
diff --git a/tests/dataApi/attachmentManagement.test.js b/tests/dataApi/attachmentManagement.test.js
new file mode 100644
index 00000000..d58a8eb8
--- /dev/null
+++ b/tests/dataApi/attachmentManagement.test.js
@@ -0,0 +1,262 @@
+'use strict'
+
+jest.mock('fs')
+const fs = require('fs')
+const path = require('path')
+const findStorage = require('browser/lib/findStorage')
+jest.mock('unique-slug')
+const uniqueSlug = require('unique-slug')
+const mdurl = require('mdurl')
+
+const systemUnderTest = require('browser/main/lib/dataApi/attachmentManagement')
+
+it('should test that copyAttachment should throw an error if sourcePath or storageKey or noteKey are undefined', function () {
+ systemUnderTest.copyAttachment(undefined, 'storageKey').then(() => {}, error => {
+ expect(error).toBe('sourceFilePath has to be given')
+ })
+ systemUnderTest.copyAttachment(null, 'storageKey', 'noteKey').then(() => {}, error => {
+ expect(error).toBe('sourceFilePath has to be given')
+ })
+ systemUnderTest.copyAttachment('source', undefined, 'noteKey').then(() => {}, error => {
+ expect(error).toBe('storageKey has to be given')
+ })
+ systemUnderTest.copyAttachment('source', null, 'noteKey').then(() => {}, error => {
+ expect(error).toBe('storageKey has to be given')
+ })
+ systemUnderTest.copyAttachment('source', 'storageKey', null).then(() => {}, error => {
+ expect(error).toBe('noteKey has to be given')
+ })
+ systemUnderTest.copyAttachment('source', 'storageKey', undefined).then(() => {}, error => {
+ expect(error).toBe('noteKey has to be given')
+ })
+})
+
+it('should test that copyAttachment should throw an error if sourcePath dosen\'t exists', function () {
+ fs.existsSync = jest.fn()
+ fs.existsSync.mockReturnValue(false)
+
+ systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then(() => {}, error => {
+ expect(error).toBe('source file does not exist')
+ expect(fs.existsSync).toHaveBeenCalledWith('path')
+ })
+})
+
+it('should test that copyAttachment works correctly assuming correct working of fs', function () {
+ const dummyExtension = '.ext'
+ const sourcePath = 'path' + dummyExtension
+ const storageKey = 'storageKey'
+ const noteKey = 'noteKey'
+ const dummyUniquePath = 'dummyPath'
+ const dummyStorage = {path: 'dummyStoragePath'}
+
+ fs.existsSync = jest.fn()
+ fs.existsSync.mockReturnValue(true)
+ fs.createReadStream = jest.fn()
+ fs.createReadStream.mockReturnValue({pipe: jest.fn()})
+ fs.createWriteStream = jest.fn()
+
+ findStorage.findStorage = jest.fn()
+ findStorage.findStorage.mockReturnValue(dummyStorage)
+ uniqueSlug.mockReturnValue(dummyUniquePath)
+
+ systemUnderTest.copyAttachment(sourcePath, storageKey, noteKey).then(
+ function (newFileName) {
+ expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey)
+ expect(fs.createReadStream).toHaveBeenCalledWith(sourcePath)
+ expect(fs.existsSync).toHaveBeenCalledWith(sourcePath)
+ expect(fs.createReadStream().pipe).toHaveBeenCalled()
+ expect(fs.createWriteStream).toHaveBeenCalledWith(path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey, dummyUniquePath + dummyExtension))
+ expect(newFileName).toBe(dummyUniquePath + dummyExtension)
+ })
+})
+
+it('should test that copyAttachment creates a new folder if the attachment folder doesn\'t exist', function () {
+ const dummyStorage = {path: 'dummyStoragePath'}
+ const noteKey = 'noteKey'
+ const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER)
+ const attachmentFolderNoteKyPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey)
+
+ fs.existsSync = jest.fn()
+ fs.existsSync.mockReturnValueOnce(true)
+ fs.existsSync.mockReturnValueOnce(false)
+ fs.existsSync.mockReturnValueOnce(false)
+ fs.mkdirSync = jest.fn()
+
+ findStorage.findStorage = jest.fn()
+ findStorage.findStorage.mockReturnValue(dummyStorage)
+ uniqueSlug.mockReturnValue('dummyPath')
+
+ systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then(
+ function () {
+ expect(fs.existsSync).toHaveBeenCalledWith(attachmentFolderPath)
+ expect(fs.mkdirSync).toHaveBeenCalledWith(attachmentFolderPath)
+ expect(fs.existsSync).toHaveBeenLastCalledWith(attachmentFolderNoteKyPath)
+ expect(fs.mkdirSync).toHaveBeenLastCalledWith(attachmentFolderNoteKyPath)
+ })
+})
+
+it('should test that copyAttachment don\'t uses a random file name if not intended ', function () {
+ const dummyStorage = {path: 'dummyStoragePath'}
+
+ fs.existsSync = jest.fn()
+ fs.existsSync.mockReturnValueOnce(true)
+ fs.existsSync.mockReturnValueOnce(false)
+ fs.mkdirSync = jest.fn()
+
+ findStorage.findStorage = jest.fn()
+ findStorage.findStorage.mockReturnValue(dummyStorage)
+ uniqueSlug.mockReturnValue('dummyPath')
+
+ systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey', false).then(
+ function (newFileName) {
+ expect(newFileName).toBe('path')
+ })
+})
+
+it('should replace the all ":storage" path with the actual storage path', function () {
+ const storageFolder = systemUnderTest.DESTINATION_FOLDER
+ const testInput =
+ '\n' +
+ ' \n' +
+ ' //header\n' +
+ ' \n' +
+ ' \n' +
+ '