From d79b6e094af5648d8584deaab554c9636943ebe6 Mon Sep 17 00:00:00 2001 From: ehhc Date: Sun, 9 Sep 2018 17:41:51 +0200 Subject: [PATCH 001/159] export folder should also export the attachments -> fixes #2374 --- browser/components/MarkdownPreview.js | 39 ++---------------------- browser/main/SideNav/StorageItem.js | 14 +++++++++ browser/main/lib/dataApi/exportFolder.js | 8 ++--- browser/main/lib/dataApi/exportNote.js | 26 +++++++++++++--- package.json | 2 +- yarn.lock | 6 ++-- 6 files changed, 46 insertions(+), 49 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 5376a773..ebf32ccd 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -251,26 +251,7 @@ export default class MarkdownPreview extends React.Component { } handleSaveAsMd () { - this.exportAsDocument('md', (noteContent, exportTasks) => { - let result = noteContent - if (this.props && this.props.storagePath && this.props.noteKey) { - const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( - noteContent, - this.props.storagePath - ) - attachmentsAbsolutePaths.forEach(attachment => { - exportTasks.push({ - src: attachment, - dst: attachmentManagement.DESTINATION_FOLDER - }) - }) - result = attachmentManagement.removeStorageAndNoteReferences( - noteContent, - this.props.noteKey - ) - } - return result - }) + this.exportAsDocument('md') } handleSaveAsHtml () { @@ -301,11 +282,6 @@ export default class MarkdownPreview extends React.Component { escapeHtmlCharacters(noteContent, { detectCodeBlock: true }) ) const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] - const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( - noteContent, - this.props.storagePath - ) - files.forEach(file => { if (global.process.platform === 'win32') { file = file.replace('file:///', '') @@ -317,16 +293,6 @@ export default class MarkdownPreview extends React.Component { dst: 'css' }) }) - attachmentsAbsolutePaths.forEach(attachment => { - exportTasks.push({ - src: attachment, - dst: attachmentManagement.DESTINATION_FOLDER - }) - }) - body = attachmentManagement.removeStorageAndNoteReferences( - body, - this.props.noteKey - ) let styles = '' files.forEach(file => { @@ -359,8 +325,9 @@ export default class MarkdownPreview extends React.Component { if (filename) { const content = this.props.value const storage = this.props.storagePath + const nodeKey = this.props.noteKey - exportNote(storage, content, filename, contentFormatter) + exportNote(nodeKey, storage, content, filename, contentFormatter) .then(res => { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'info', diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index d72f0a8f..242bf328 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -164,6 +164,20 @@ class StorageItem extends React.Component { folderKey: data.folderKey, fileType: data.fileType }) + return data + }) + .then(data => { + dialog.showMessageBox(remote.getCurrentWindow(), { + type: 'info', + message: 'Exported to "' + data.exportDir + '"' + }) + }) + .catch(err => { + dialog.showErrorBox( + 'Export error', + err ? err.message || err : 'Unexpected error during export' + ) + throw err }) } }) diff --git a/browser/main/lib/dataApi/exportFolder.js b/browser/main/lib/dataApi/exportFolder.js index 3e998f15..771f77dc 100644 --- a/browser/main/lib/dataApi/exportFolder.js +++ b/browser/main/lib/dataApi/exportFolder.js @@ -1,9 +1,9 @@ import { findStorage } from 'browser/lib/findStorage' import resolveStorageData from './resolveStorageData' import resolveStorageNotes from './resolveStorageNotes' +import exportNote from './exportNote' import filenamify from 'filenamify' import * as path from 'path' -import * as fs from 'fs' /** * @param {String} storageKey @@ -45,9 +45,9 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) { notes .filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE') - .forEach(snippet => { - const notePath = path.join(exportDir, `${filenamify(snippet.title, {replacement: '_'})}.${fileType}`) - fs.writeFileSync(notePath, snippet.content) + .forEach(note => { + const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`) + exportNote(note.key, storage.path, note.content, notePath, null) }) return { diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js index e4fec5f4..b358e548 100755 --- a/browser/main/lib/dataApi/exportNote.js +++ b/browser/main/lib/dataApi/exportNote.js @@ -4,27 +4,43 @@ import { findStorage } from 'browser/lib/findStorage' const fs = require('fs') const path = require('path') +const attachmentManagement = require('./attachmentManagement') + /** - * Export note together with images + * Export note together with attachments * - * If images is stored in the storage, creates 'images' subfolder in target directory - * and copies images to it. Changes links to images in the content of the note + * If attachments are stored in the storage, creates 'attachments' subfolder in target directory + * and copies attachments to it. Changes links to images in the content of the note * + * @param {String} nodeKey key of the node that should be exported * @param {String} storageKey or storage path * @param {String} noteContent Content to export * @param {String} targetPath Path to exported file * @param {function} outputFormatter * @return {Promise.<*[]>} */ -function exportNote (storageKey, noteContent, targetPath, outputFormatter) { +function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatter) { const storagePath = path.isAbsolute(storageKey) ? storageKey : findStorage(storageKey).path const exportTasks = [] if (!storagePath) { throw new Error('Storage path is not found') } + const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( + noteContent, + storagePath + ) + attachmentsAbsolutePaths.forEach(attachment => { + exportTasks.push({ + src: attachment, + dst: attachmentManagement.DESTINATION_FOLDER + }) + }) - let exportedData = noteContent + let exportedData = attachmentManagement.removeStorageAndNoteReferences( + noteContent, + nodeKey + ) if (outputFormatter) { exportedData = outputFormatter(exportedData, exportTasks) diff --git a/package.json b/package.json index c9e22164..dd793f9e 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "electron-gh-releases": "^2.0.2", "escape-string-regexp": "^1.0.5", "file-url": "^2.0.2", - "filenamify": "^2.0.0", + "filenamify": "^2.1.0", "flowchart.js": "^1.6.5", "font-awesome": "^4.3.0", "fs-extra": "^5.0.0", diff --git a/yarn.lock b/yarn.lock index 4ecfa51b..0d919cf3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3514,9 +3514,9 @@ filename-reserved-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" -filenamify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.0.0.tgz#bd162262c0b6e94bfbcdcf19a3bbb3764f785695" +filenamify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" dependencies: filename-reserved-regex "^2.0.0" strip-outer "^1.0.0" From 6510152138e7a4a7e7d813b5100941f21a27275a Mon Sep 17 00:00:00 2001 From: Martin Price Date: Mon, 17 Sep 2018 11:55:01 +0100 Subject: [PATCH 002/159] Always redirect to `/home` when jumping to a note by hash The note which we are jumping to may not be available in the note list for a number of reasons (e.g. if there is an active search, or if another storage folder is selected, or if the note list is showing starred notes). This affects both when we are creating a new note (which may not match the current search criteria), and when jumping to a note via a link in another note (and the linked note may not be available for any of the above reasons). 2241 --- browser/main/NoteList/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index f7dd0764..27932d71 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -170,16 +170,15 @@ class NoteList extends React.Component { } } - focusNote (selectedNoteKeys, noteKey) { + focusNote (selectedNoteKeys, noteKey, pathname) { const { router } = this.context - const { location } = this.props this.setState({ selectedNoteKeys }) router.push({ - pathname: location.pathname, + pathname, query: { key: noteKey } @@ -198,6 +197,7 @@ class NoteList extends React.Component { } let { selectedNoteKeys } = this.state const { shiftKeyDown } = this.state + const { location } = this.props let targetIndex = this.getTargetIndex() @@ -214,7 +214,7 @@ class NoteList extends React.Component { selectedNoteKeys.push(priorNoteKey) } - this.focusNote(selectedNoteKeys, priorNoteKey) + this.focusNote(selectedNoteKeys, priorNoteKey, location.pathname) ee.emit('list:moved') } @@ -225,6 +225,7 @@ class NoteList extends React.Component { } let { selectedNoteKeys } = this.state const { shiftKeyDown } = this.state + const { location } = this.props let targetIndex = this.getTargetIndex() const isTargetLastNote = targetIndex === this.notes.length - 1 @@ -247,7 +248,7 @@ class NoteList extends React.Component { selectedNoteKeys.push(nextNoteKey) } - this.focusNote(selectedNoteKeys, nextNoteKey) + this.focusNote(selectedNoteKeys, nextNoteKey, location.pathname) ee.emit('list:moved') } @@ -259,7 +260,7 @@ class NoteList extends React.Component { } const selectedNoteKeys = [noteHash] - this.focusNote(selectedNoteKeys, noteHash) + this.focusNote(selectedNoteKeys, noteHash, '/home') ee.emit('list:moved') } From e9dac8c8f30719357b59fa03c54879ec3f95321a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 1 Oct 2018 18:16:26 +0200 Subject: [PATCH 003/159] fix scrolling in note list --- browser/main/NoteList/index.js | 3 +-- browser/main/TopBar/index.js | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 30ad93c3..a9818e4f 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -2,7 +2,6 @@ import PropTypes from 'prop-types' import React from 'react' import CSSModules from 'browser/lib/CSSModules' -import debounceRender from 'react-debounce-render' import styles from './NoteList.styl' import moment from 'moment' import _ from 'lodash' @@ -1078,4 +1077,4 @@ NoteList.propTypes = { }) } -export default debounceRender(CSSModules(NoteList, styles)) +export default CSSModules(NoteList, styles) diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index a5687ecb..d1844532 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -6,6 +6,7 @@ import _ from 'lodash' import ee from 'browser/main/lib/eventEmitter' import NewNoteButton from 'browser/main/NewNoteButton' import i18n from 'browser/lib/i18n' +import debounce from 'lodash/debounce' class TopBar extends React.Component { constructor (props) { @@ -25,6 +26,10 @@ class TopBar extends React.Component { } this.codeInitHandler = this.handleCodeInit.bind(this) + + this.updateKeyword = debounce(this.updateKeyword, 1000 / 60, { + maxWait: 1000 / 8 + }) } componentDidMount () { @@ -94,7 +99,6 @@ class TopBar extends React.Component { } handleKeyUp (e) { - const { router } = this.context // reset states this.setState({ isConfirmTranslation: false @@ -106,21 +110,21 @@ class TopBar extends React.Component { isConfirmTranslation: true }) const keyword = this.refs.searchInput.value - router.push(`/searched/${encodeURIComponent(keyword)}`) - this.setState({ - search: keyword - }) + this.updateKeyword(keyword) } } handleSearchChange (e) { - const { router } = this.context - const keyword = this.refs.searchInput.value if (this.state.isAlphabet || this.state.isConfirmTranslation) { - router.push(`/searched/${encodeURIComponent(keyword)}`) + const keyword = this.refs.searchInput.value + this.updateKeyword(keyword) } else { e.preventDefault() } + } + + updateKeyword(keyword) { + this.context.router.push(`/searched/${encodeURIComponent(keyword)}`) this.setState({ search: keyword }) From fa6c504b34934609edb9a99007a8811addabcdae Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 1 Oct 2018 18:42:18 +0200 Subject: [PATCH 004/159] fix lint error --- browser/main/TopBar/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index d1844532..91256daf 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -123,7 +123,7 @@ class TopBar extends React.Component { } } - updateKeyword(keyword) { + updateKeyword (keyword) { this.context.router.push(`/searched/${encodeURIComponent(keyword)}`) this.setState({ search: keyword From e52bcf33c579de9ddb80f7fb4bb5751adb39bb02 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 1 Oct 2018 18:54:50 +0200 Subject: [PATCH 005/159] add `precommit` command --- package.json | 6 +++ yarn.lock | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f4e5f532..4fe3434a 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "grunt": "^0.4.5", "grunt-electron-installer": "2.1.0", "history": "^1.17.0", + "husky": "^1.1.0", "identity-obj-proxy": "^3.0.0", "jest": "^22.4.3", "jest-localstorage-mock": "^2.2.0", @@ -179,5 +180,10 @@ "/tests/jest.js", "jest-localstorage-mock" ] + }, + "husky": { + "hooks": { + "pre-commit": "npm run lint" + } } } diff --git a/yarn.lock b/yarn.lock index 687bb6de..5c9c1cc4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1677,6 +1677,10 @@ ci-info@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + circular-json@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" @@ -2074,6 +2078,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cosmiconfig@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + create-error-class@^3.0.0, create-error-class@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" @@ -3309,6 +3321,18 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -3615,6 +3639,12 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + findup-sync@~0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.1.3.tgz#7f3e7a97b82392c653bf06589bd85190e93c3683" @@ -3870,6 +3900,10 @@ get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -4411,6 +4445,21 @@ humanize-plus@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030" +husky@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-1.1.0.tgz#7271e85f5d98b54349788839b720c9a60cd95dba" + dependencies: + cosmiconfig "^5.0.6" + execa "^0.9.0" + find-up "^3.0.0" + get-stdin "^6.0.0" + is-ci "^1.2.1" + pkg-dir "^3.0.0" + please-upgrade-node "^3.1.1" + read-pkg "^4.0.1" + run-node "^1.0.0" + slash "^2.0.0" + i18n-2@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/i18n-2/-/i18n-2-0.7.2.tgz#7efb1a7adc67869adea0688951577464aa793aaf" @@ -4617,6 +4666,12 @@ is-ci@^1.0.10, is-ci@^1.0.7: dependencies: ci-info "^1.0.0" +is-ci@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + dependencies: + ci-info "^1.5.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4649,6 +4704,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -5286,7 +5345,7 @@ js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.8.1: +js-yaml@^3.8.1, js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: @@ -5612,6 +5671,13 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash-es@^4.2.1: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05" @@ -6595,16 +6661,32 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" +p-limit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^2.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" +p-try@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + package-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44" @@ -6800,12 +6882,24 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + dependencies: + find-up "^3.0.0" + pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" dependencies: find-up "^2.1.0" +please-upgrade-node@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" + dependencies: + semver-compare "^1.0.0" + plist@^2.0.0, plist@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025" @@ -7455,6 +7549,14 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" + dependencies: + normalize-package-data "^2.3.2" + parse-json "^4.0.0" + pify "^3.0.0" + readable-stream@^1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -7805,6 +7907,10 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" + run-parallel@^1.1.2: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -7904,6 +8010,10 @@ section-iterator@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -8057,6 +8167,10 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" From f308836264e8e438c2e111b8607c975fce3afe2c Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 2 Oct 2018 23:48:07 +0200 Subject: [PATCH 006/159] add new fenced block language: gallery --- browser/components/MarkdownPreview.js | 33 ++++++++++++++++++- browser/components/markdown.styl | 46 ++++++++++++++++++++++++++- browser/lib/markdown.js | 6 ++++ package.json | 2 ++ yarn.lock | 38 ++++++++++++++++++++++ 5 files changed, 123 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index eacc4e21..48951204 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -21,6 +21,8 @@ import yaml from 'js-yaml' import context from 'browser/lib/context' import i18n from 'browser/lib/i18n' import fs from 'fs' +import { render } from 'react-dom' +import Carousel from 'react-image-carousel' const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') @@ -39,7 +41,8 @@ const appPath = fileUrl( ) const CSS_FILES = [ `${appPath}/node_modules/katex/dist/katex.min.css`, - `${appPath}/node_modules/codemirror/lib/codemirror.css` + `${appPath}/node_modules/codemirror/lib/codemirror.css`, + `${appPath}/node_modules/react-image-carousel/lib/css/main.min.css` ] function buildStyle ( @@ -789,6 +792,34 @@ export default class MarkdownPreview extends React.Component { mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme) } ) + + _.forEach( + this.refs.root.contentWindow.document.querySelectorAll('.gallery'), + el => { + const images = el.innerHTML.split(/\n/g).filter(i => i.length > 0) + el.innerHTML = '' + + const height = el.attributes.getNamedItem('data-height') + if (height && height.value !== 'undefined') { + el.style.height = height.value + 'vh' + } + + let autoplay = el.attributes.getNamedItem('data-autoplay') + if (autoplay && autoplay.value !== 'undefined') { + autoplay = parseInt(autoplay.value, 10) || 0 + } else { + autoplay = 0 + } + + render( + , + el + ) + } + ) } focus () { diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index 2e17a75b..6e43273e 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -381,6 +381,26 @@ pre.fence canvas, svg max-width 100% !important + .gallery + width 100% + height 50vh + + .carousel + .carousel-main img + min-width auto + max-width 100% + min-height auto + max-height 100% + + .carousel-footer::-webkit-scrollbar-corner + background-color transparent + + .carousel-main, .carousel-footer + background-color $ui-noteDetail-backgroundColor + .prev, .next + color $ui-text-color + background-color $ui-tag-backgroundColor + themeDarkBackground = darken(#21252B, 10%) themeDarkText = #f9f9f9 themeDarkBorder = lighten(themeDarkBackground, 20%) @@ -432,6 +452,14 @@ body[data-theme="dark"] background-color themeDarkBorder color themeDarkText + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-dark-noteDetail-backgroundColor + .prev, .next + color $ui-dark-text-color + background-color $ui-dark-tag-backgroundColor + themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%) themeSolarizedDarkTableHead = themeSolarizedDarkTableEven @@ -459,6 +487,14 @@ body[data-theme="solarized-dark"] &:last-child border-right solid 1px themeSolarizedDarkTableBorder + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-solarized-dark-noteDetail-backgroundColor + .prev, .next + color $ui-solarized-dark-text-color + background-color $ui-solarized-dark-tag-backgroundColor + themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%) themeMonokaiTableHead = themeMonokaiTableEven @@ -486,4 +522,12 @@ body[data-theme="monokai"] &:last-child border-right solid 1px themeMonokaiTableBorder kbd - background-color themeDarkBackground \ No newline at end of file + background-color themeDarkBackground + + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-monokai-noteDetail-backgroundColor + .prev, .next + color $ui-monokai-text-color + background-color $ui-monokai-tag-backgroundColor diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index f8ae7f05..52a13700 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -143,6 +143,12 @@ class Markdown {
${token.content}
` }, + gallery: token => { + return `
+          ${token.fileName}
+          
+        
` + }, mermaid: token => { return `
           ${token.fileName}
diff --git a/package.json b/package.json
index 3e95603b..560a2e7d 100644
--- a/package.json
+++ b/package.json
@@ -95,8 +95,10 @@
     "react-codemirror": "^0.3.0",
     "react-debounce-render": "^4.0.1",
     "react-dom": "^15.0.2",
+    "react-image-carousel": "^2.0.18",
     "react-redux": "^4.4.5",
     "react-sortable-hoc": "^0.6.7",
+    "react-transition-group": "^2.5.0",
     "redux": "^3.5.2",
     "sander": "^0.5.1",
     "sanitize-html": "^1.18.2",
diff --git a/yarn.lock b/yarn.lock
index 5c426c92..55f4102a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2701,6 +2701,10 @@ doctrine@^2.0.0, doctrine@^2.0.2:
   dependencies:
     esutils "^2.0.2"
 
+dom-helpers@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
+
 dom-serializer@0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -5279,6 +5283,10 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
 
+"js-tokens@^3.0.0 || ^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+
 js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0:
   version "3.11.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
@@ -5729,6 +5737,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
   dependencies:
     js-tokens "^3.0.0"
 
+loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
 loud-rejection@^1.0.0, loud-rejection@^1.2.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@@ -7155,6 +7169,13 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8,
     loose-envify "^1.3.1"
     object-assign "^4.1.1"
 
+prop-types@^15.6.2:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
+  dependencies:
+    loose-envify "^1.3.1"
+    object-assign "^4.1.1"
+
 proxy-addr@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@@ -7330,6 +7351,10 @@ react-dom@^15.0.2:
     object-assign "^4.1.0"
     prop-types "^15.5.10"
 
+react-image-carousel@^2.0.18:
+  version "2.0.18"
+  resolved "https://registry.yarnpkg.com/react-image-carousel/-/react-image-carousel-2.0.18.tgz#5868ea09bd9cca09c4467d3d02695cd4e7792f28"
+
 react-input-autosize@^1.1.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.2.0.tgz#87241071159f742123897691da6796ec33b57d05"
@@ -7337,6 +7362,10 @@ react-input-autosize@^1.1.0:
     create-react-class "^15.5.2"
     prop-types "^15.5.8"
 
+react-lifecycles-compat@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+
 react-proxy@^1.1.7:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
@@ -7402,6 +7431,15 @@ react-transform-hmr@^1.0.3:
     global "^4.3.0"
     react-proxy "^1.1.7"
 
+react-transition-group@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874"
+  dependencies:
+    dom-helpers "^3.3.1"
+    loose-envify "^1.4.0"
+    prop-types "^15.6.2"
+    react-lifecycles-compat "^3.0.4"
+
 react@^15.5.4:
   version "15.6.2"
   resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"

From 39eaed260a834e51283cbcc42c002f1152955a36 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain 
Date: Mon, 8 Oct 2018 15:57:42 +0200
Subject: [PATCH 007/159] display correctly attached image

---
 browser/main/lib/dataApi/attachmentManagement.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js
index 912450c1..38648941 100644
--- a/browser/main/lib/dataApi/attachmentManagement.js
+++ b/browser/main/lib/dataApi/attachmentManagement.js
@@ -227,7 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) {
  * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
  */
 function fixLocalURLS (renderedHTML, storagePath) {
-  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?"', 'g'), function (match) {
+  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '[\w\/]+', 'g'), function (match) {
     var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
     return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
   })

From 7a5a821f8a8b8919d9c5ae0b0dca377485ec25f2 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain 
Date: Tue, 9 Oct 2018 00:47:37 +0200
Subject: [PATCH 008/159] fix failing test

---
 browser/main/lib/dataApi/attachmentManagement.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js
index 38648941..a81d3bd4 100644
--- a/browser/main/lib/dataApi/attachmentManagement.js
+++ b/browser/main/lib/dataApi/attachmentManagement.js
@@ -227,7 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) {
  * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
  */
 function fixLocalURLS (renderedHTML, storagePath) {
-  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '[\w\/]+', 'g'), function (match) {
+  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:\\\/|%5C)[\\w.]+', 'g'), function (match) {
     var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
     return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
   })

From 5d9b1abe82a68c51b99564f6f117191c246f98b3 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain 
Date: Tue, 9 Oct 2018 01:18:19 +0200
Subject: [PATCH 009/159] allow markdown image syntax

---
 browser/lib/markdown.js | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index 52a13700..cc5e86dc 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -144,9 +144,18 @@ class Markdown {
         
` }, gallery: token => { + const content = token.content.split('\n').slice(0, -1).map(line => { + const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line) + if (match) { + return match[1] + } else { + return line + } + }).join('\n') + return `
           ${token.fileName}
-          
+          
         
` }, mermaid: token => { From db97ab51acf97786048061929371d5f6141cd930 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 11:44:03 +0000 Subject: [PATCH 010/159] Bracket matching option added to config --- browser/components/CodeEditor.js | 373 ++++++++++++------ browser/components/MarkdownEditor.js | 1 + browser/components/MarkdownSplitEditor.js | 1 + browser/main/Detail/SnippetNoteDetail.js | 1 + browser/main/lib/ConfigManager.js | 1 + .../modals/PreferencesModal/SnippetEditor.js | 11 +- .../modals/PreferencesModal/SnippetTab.js | 1 + browser/main/modals/PreferencesModal/UiTab.js | 17 +- 8 files changed, 284 insertions(+), 122 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 41d71622..f4ce77b9 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -5,25 +5,35 @@ import CodeMirror from 'codemirror' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' -import { options, TableEditor, Alignment } from '@susisu/mte-kernel' +import { + options, + TableEditor, + Alignment +} from '@susisu/mte-kernel' import TextEditorInterface from 'browser/lib/TextEditorInterface' import eventEmitter from 'browser/main/lib/eventEmitter' import iconv from 'iconv-lite' import crypto from 'crypto' import consts from 'browser/lib/consts' import fs from 'fs' -const { ipcRenderer } = require('electron') +const { + ipcRenderer +} = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import TurndownService from 'turndown' -import { gfm } from 'turndown-plugin-gfm' +import { + gfm +} from 'turndown-plugin-gfm' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({ column: ruler })) : []) + (enableRulers ? rulers.map(ruler => ({ + column: ruler + })) : []) export default class CodeEditor extends React.Component { - constructor (props) { + constructor(props) { super(props) this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { @@ -46,7 +56,10 @@ export default class CodeEditor extends React.Component { } this.props.onBlur != null && this.props.onBlur(e) - const { storageKey, noteKey } = this.props + const { + storageKey, + noteKey + } = this.props attachmentManagement.deleteAttachmentsNotPresentInNote( this.editor.getValue(), storageKey, @@ -65,7 +78,7 @@ export default class CodeEditor extends React.Component { this.editorActivityHandler = () => this.handleEditorActivity() } - handleSearch (msg) { + handleSearch(msg) { const cm = this.editor const component = this @@ -76,7 +89,7 @@ export default class CodeEditor extends React.Component { component.searchState = makeOverlay(msg, 'searching') cm.addOverlay(component.searchState) - function makeOverlay (query, style) { + function makeOverlay(query, style) { query = new RegExp( query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi' @@ -99,17 +112,19 @@ export default class CodeEditor extends React.Component { }) } - handleFormatTable () { - this.tableEditor.formatAll(options({textWidthOptions: {}})) + handleFormatTable() { + this.tableEditor.formatAll(options({ + textWidthOptions: {} + })) } - handleEditorActivity () { + handleEditorActivity() { if (!this.textEditorInterface.transaction) { this.updateTableEditorState() } } - updateTableEditorState () { + updateTableEditorState() { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { @@ -125,19 +140,20 @@ export default class CodeEditor extends React.Component { } } - componentDidMount () { - const { rulers, enableRulers } = this.props + componentDidMount() { + const { + rulers, + enableRulers + } = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) - const defaultSnippet = [ - { - id: crypto.randomBytes(16).toString('hex'), - name: 'Dummy text', - prefix: ['lorem', 'ipsum'], - content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - } - ] + const defaultSnippet = [{ + id: crypto.randomBytes(16).toString('hex'), + name: 'Dummy text', + prefix: ['lorem', 'ipsum'], + content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + }] if (!fs.existsSync(consts.SNIPPET_FILE)) { fs.writeFileSync( consts.SNIPPET_FILE, @@ -215,12 +231,17 @@ export default class CodeEditor extends React.Component { dragDrop: false, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: { - pairs: '()[]{}\'\'""$$**``', + autoCloseBrackets: (this.props.enableBracketMatching ? { + pairs: '()[]{}\'\'""$$**``ll', triples: '```"""\'\'\'', explode: '[]{}``$$', override: true - }, + } : { + pairs: '', + triples: '', + explode: '', + override: true + }), extraKeys: this.defaultKeyMap }) @@ -253,43 +274,117 @@ export default class CodeEditor extends React.Component { }) this.editorKeyMap = CodeMirror.normalizeKeyMap({ - 'Tab': () => { this.tableEditor.nextCell(this.tableEditorOptions) }, - 'Shift-Tab': () => { this.tableEditor.previousCell(this.tableEditorOptions) }, - 'Enter': () => { this.tableEditor.nextRow(this.tableEditorOptions) }, - 'Ctrl-Enter': () => { this.tableEditor.escape(this.tableEditorOptions) }, - 'Cmd-Enter': () => { this.tableEditor.escape(this.tableEditorOptions) }, - 'Shift-Ctrl-Left': () => { this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) }, - 'Shift-Cmd-Left': () => { this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) }, - 'Shift-Ctrl-Right': () => { this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) }, - 'Shift-Cmd-Right': () => { this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) }, - 'Shift-Ctrl-Up': () => { this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) }, - 'Shift-Cmd-Up': () => { this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) }, - 'Shift-Ctrl-Down': () => { this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) }, - 'Shift-Cmd-Down': () => { this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) }, - 'Ctrl-Left': () => { this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) }, - 'Cmd-Left': () => { this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) }, - 'Ctrl-Right': () => { this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) }, - 'Cmd-Right': () => { this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) }, - 'Ctrl-Up': () => { this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) }, - 'Cmd-Up': () => { this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) }, - 'Ctrl-Down': () => { this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) }, - 'Cmd-Down': () => { this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) }, - 'Ctrl-K Ctrl-I': () => { this.tableEditor.insertRow(this.tableEditorOptions) }, - 'Cmd-K Cmd-I': () => { this.tableEditor.insertRow(this.tableEditorOptions) }, - 'Ctrl-L Ctrl-I': () => { this.tableEditor.deleteRow(this.tableEditorOptions) }, - 'Cmd-L Cmd-I': () => { this.tableEditor.deleteRow(this.tableEditorOptions) }, - 'Ctrl-K Ctrl-J': () => { this.tableEditor.insertColumn(this.tableEditorOptions) }, - 'Cmd-K Cmd-J': () => { this.tableEditor.insertColumn(this.tableEditorOptions) }, - 'Ctrl-L Ctrl-J': () => { this.tableEditor.deleteColumn(this.tableEditorOptions) }, - 'Cmd-L Cmd-J': () => { this.tableEditor.deleteColumn(this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Left': () => { this.tableEditor.moveColumn(-1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Left': () => { this.tableEditor.moveColumn(-1, this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Right': () => { this.tableEditor.moveColumn(1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Right': () => { this.tableEditor.moveColumn(1, this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Up': () => { this.tableEditor.moveRow(-1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Up': () => { this.tableEditor.moveRow(-1, this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Down': () => { this.tableEditor.moveRow(1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Down': () => { this.tableEditor.moveRow(1, this.tableEditorOptions) } + 'Tab': () => { + this.tableEditor.nextCell(this.tableEditorOptions) + }, + 'Shift-Tab': () => { + this.tableEditor.previousCell(this.tableEditorOptions) + }, + 'Enter': () => { + this.tableEditor.nextRow(this.tableEditorOptions) + }, + 'Ctrl-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Cmd-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Shift-Ctrl-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Cmd-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Cmd-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Cmd-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Ctrl-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Shift-Cmd-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Ctrl-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Cmd-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Ctrl-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Cmd-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Ctrl-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Cmd-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Ctrl-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Cmd-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Cmd-K Cmd-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Cmd-L Cmd-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Cmd-K Cmd-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Cmd-L Cmd-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + } }) if (this.props.enableTableEditor) { @@ -302,7 +397,7 @@ export default class CodeEditor extends React.Component { }) } - expandSnippet (line, cursor, cm, snippets) { + expandSnippet(line, cursor, cm, snippets) { const wordBeforeCursor = this.getWordBeforeCursor( line, cursor.line, @@ -345,7 +440,7 @@ export default class CodeEditor extends React.Component { return false } - getWordBeforeCursor (line, lineNumber, cursorPosition) { + getWordBeforeCursor(line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n/ @@ -370,17 +465,23 @@ export default class CodeEditor extends React.Component { return { text: wordBeforeCursor, range: { - from: { line: lineNumber, ch: originCursorPosition }, - to: { line: lineNumber, ch: cursorPosition } + from: { + line: lineNumber, + ch: originCursorPosition + }, + to: { + line: lineNumber, + ch: cursorPosition + } } } } - quitEditor () { + quitEditor() { document.querySelector('textarea').blur() } - componentWillUnmount () { + componentWillUnmount() { this.editor.off('focus', this.focusHandler) this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) @@ -393,9 +494,12 @@ export default class CodeEditor extends React.Component { eventEmitter.off('code:format-table', this.formatTable) } - componentDidUpdate (prevProps, prevState) { + componentDidUpdate(prevProps, prevState) { let needRefresh = false - const { rulers, enableRulers } = this.props + const { + rulers, + enableRulers + } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) } @@ -436,6 +540,21 @@ export default class CodeEditor extends React.Component { this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd) } + if (prevProps.enableBracketMatching !== this.props.enableBracketMatching) { + const bracketObject = (this.props.enableBracketMatching ? { + pairs: '()[]{}\'\'""$$**``ll', + triples: '```"""\'\'\'', + explode: '[]{}``$$', + override: true + } : { + pairs: '', + triples: '', + explode: '', + override: true + }); + this.editor.setOption('autoCloseBrackets', bracketObject) + } + if (prevProps.enableTableEditor !== this.props.enableTableEditor) { if (this.props.enableTableEditor) { this.editor.on('cursorActivity', this.editorActivityHandler) @@ -462,7 +581,7 @@ export default class CodeEditor extends React.Component { } } - setMode (mode) { + setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -470,16 +589,16 @@ export default class CodeEditor extends React.Component { CodeMirror.autoLoadMode(this.editor, syntax.mode) } - handleChange (e) { + handleChange(e) { this.value = this.editor.getValue() if (this.props.onChange) { this.props.onChange(e) } } - moveCursorTo (row, col) {} + moveCursorTo(row, col) {} - scrollToLine (event, num) { + scrollToLine(event, num) { const cursor = { line: num, ch: 1 @@ -487,15 +606,15 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } - focus () { + focus() { this.editor.focus() } - blur () { + blur() { this.editor.blur() } - reload () { + reload() { // Change event shouldn't be fired when switch note this.editor.off('change', this.changeHandler) this.value = this.props.value @@ -505,15 +624,18 @@ export default class CodeEditor extends React.Component { this.editor.refresh() } - setValue (value) { + setValue(value) { const cursor = this.editor.getCursor() this.editor.setValue(value) this.editor.setCursor(cursor) } - handleDropImage (dropEvent) { + handleDropImage(dropEvent) { dropEvent.preventDefault() - const { storageKey, noteKey } = this.props + const { + storageKey, + noteKey + } = this.props attachmentManagement.handleAttachmentDrop( this, storageKey, @@ -522,13 +644,16 @@ export default class CodeEditor extends React.Component { ) } - insertAttachmentMd (imageMd) { + insertAttachmentMd(imageMd) { this.editor.replaceSelection(imageMd) } - handlePaste (editor, e) { + handlePaste(editor, e) { const clipboardData = e.clipboardData - const { storageKey, noteKey } = this.props + const { + storageKey, + noteKey + } = this.props const dataTransferItem = clipboardData.items[0] const pastedTxt = clipboardData.getData('text') const isURL = str => { @@ -537,15 +662,21 @@ export default class CodeEditor extends React.Component { } const isInLinkTag = editor => { const startCursor = editor.getCursor('start') - const prevChar = editor.getRange( - { line: startCursor.line, ch: startCursor.ch - 2 }, - { line: startCursor.line, ch: startCursor.ch } - ) + const prevChar = editor.getRange({ + line: startCursor.line, + ch: startCursor.ch - 2 + }, { + line: startCursor.line, + ch: startCursor.ch + }) const endCursor = editor.getCursor('end') - const nextChar = editor.getRange( - { line: endCursor.line, ch: endCursor.ch }, - { line: endCursor.line, ch: endCursor.ch + 1 } - ) + const nextChar = editor.getRange({ + line: endCursor.line, + ch: endCursor.ch + }, { + line: endCursor.line, + ch: endCursor.ch + 1 + }) return prevChar === '](' && nextChar === ')' } @@ -576,13 +707,13 @@ export default class CodeEditor extends React.Component { } } - handleScroll (e) { + handleScroll(e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handlePasteUrl (e, editor, pastedTxt) { + handlePasteUrl(e, editor, pastedTxt) { e.preventDefault() const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) @@ -605,8 +736,8 @@ export default class CodeEditor extends React.Component { } fetch(pastedTxt, { - method: 'get' - }) + method: 'get' + }) .then(response => { if (isImageReponse(response)) { return this.mapImageResponse(response, pastedTxt) @@ -622,13 +753,13 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml (e, editor, pastedHtml) { + handlePasteHtml(e, editor, pastedHtml) { e.preventDefault() const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - mapNormalResponse (response, pastedTxt) { + mapNormalResponse(response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { try { @@ -648,7 +779,7 @@ export default class CodeEditor extends React.Component { }) } - mapImageResponse (response, pastedTxt) { + mapImageResponse(response, pastedTxt) { return new Promise((resolve, reject) => { try { const url = response.url @@ -661,18 +792,18 @@ export default class CodeEditor extends React.Component { }) } - decodeResponse (response) { + decodeResponse(response) { const headers = response.headers - const _charset = headers.has('content-type') - ? this.extractContentTypeCharset(headers.get('content-type')) - : undefined + 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' + iconv.encodingExists(_charset) ? + _charset : + 'utf-8' resolve(iconv.decode(new Buffer(buff), charset).toString()) } catch (e) { reject(e) @@ -681,7 +812,7 @@ export default class CodeEditor extends React.Component { }) } - extractContentTypeCharset (contentType) { + extractContentTypeCharset(contentType) { return contentType .split(';') .filter(str => { @@ -692,21 +823,29 @@ export default class CodeEditor extends React.Component { })[0] } - render () { - const {className, fontSize} = this.props + render() { + const { + className, + fontSize + } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width - return ( -
this.handleDropImage(e)} + } + } + onDrop = { + e => this.handleDropImage(e) + } /> ) } @@ -731,4 +870,4 @@ CodeEditor.defaultProps = { fontFamily: 'Monaco, Consolas', indentSize: 4, indentType: 'space' -} +} \ No newline at end of file diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 9c8a06d6..f10ea8bc 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -268,6 +268,7 @@ class MarkdownEditor extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} scrollPastEnd={config.editor.scrollPastEnd} storageKey={storageKey} noteKey={noteKey} diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index ca2d3108..004a6eb0 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -158,6 +158,7 @@ class MarkdownSplitEditor extends React.Component { fontFamily={config.editor.fontFamily} fontSize={editorFontSize} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} indentType={config.editor.indentType} indentSize={editorIndentSize} enableRulers={config.editor.enableRulers} diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index afd81102..90b8faa9 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -699,6 +699,7 @@ class SnippetNoteDetail extends React.Component { indentType={config.editor.indentType} indentSize={editorIndentSize} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} keyMap={config.editor.keyMap} scrollPastEnd={config.editor.scrollPastEnd} fetchUrlTitle={config.editor.fetchUrlTitle} diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 9611f21d..eddf0909 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -44,6 +44,7 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, + enableBracketMatching: true, switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE' scrollPastEnd: false, diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index 4ce5dc34..ec3698c7 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -27,12 +27,17 @@ class SnippetEditor extends React.Component { dragDrop: false, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: { - pairs: '()[]{}\'\'""$$**``', + autoCloseBrackets: (this.enableBracketMatching ? { + pairs: '()[]{}\'\'""$$**``ll', triples: '```"""\'\'\'', explode: '[]{}``$$', override: true - }, + }: { + pairs: '', + triples: '', + explode: '', + override: true + }), mode: 'null' }) this.cm.setSize('100%', '100%') diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index b83fa205..40b65d39 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -136,6 +136,7 @@ class SnippetTab extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} scrollPastEnd={config.editor.scrollPastEnd} onRef={ref => { this.snippetEditor = ref }} />
diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 6bc3b0a3..5c6110c3 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -62,7 +62,8 @@ class UiTab extends React.Component { checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } - + console.log("This is a console log") + const newConfig = { ui: { theme: this.refs.uiTheme.value, @@ -94,7 +95,8 @@ class UiTab extends React.Component { fetchUrlTitle: this.refs.editorFetchUrlTitle.checked, enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, - frontMatterTitleField: this.refs.frontMatterTitleField.value + frontMatterTitleField: this.refs.frontMatterTitleField.value, + enableBracketMatching: this.refs.enableBracketMatching.checked }, preview: { fontSize: this.refs.previewFontSize.value, @@ -539,6 +541,17 @@ class UiTab extends React.Component { +
+ +
+
{i18n.__('Preview')}
From 59d31c9a182bacda1a0b5db2b4211dff6f875bc1 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 12:01:12 +0000 Subject: [PATCH 011/159] Deleted useless console log --- browser/main/modals/PreferencesModal/UiTab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 5c6110c3..ab72c914 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -62,7 +62,6 @@ class UiTab extends React.Component { checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } - console.log("This is a console log") const newConfig = { ui: { From ab65fb7a5ca41b1bb3561d15b2d9259d64aea330 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 13:18:13 +0000 Subject: [PATCH 012/159] Choosing which characters to match and explode --- browser/components/CodeEditor.js | 993 ++++++++---------- browser/components/MarkdownEditor.js | 3 + browser/components/MarkdownSplitEditor.js | 3 + browser/main/Detail/SnippetNoteDetail.js | 3 + browser/main/lib/ConfigManager.js | 3 + .../modals/PreferencesModal/SnippetTab.js | 3 + browser/main/modals/PreferencesModal/UiTab.js | 48 +- 7 files changed, 516 insertions(+), 540 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index f4ce77b9..3e416ab2 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -1,121 +1,110 @@ +import 'codemirror-mode-elixir' + +import {Alignment, options, TableEditor} from '@susisu/mte-kernel' +import consts from 'browser/lib/consts' +import convertModeName from 'browser/lib/convertModeName' +import TextEditorInterface from 'browser/lib/TextEditorInterface' +import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' +import eventEmitter from 'browser/main/lib/eventEmitter' +import CodeMirror from 'codemirror' +import crypto from 'crypto' +import fs from 'fs' +import iconv from 'iconv-lite' +import _ from 'lodash' import PropTypes from 'prop-types' import React from 'react' -import _ from 'lodash' -import CodeMirror from 'codemirror' -import 'codemirror-mode-elixir' -import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' -import convertModeName from 'browser/lib/convertModeName' -import { - options, - TableEditor, - Alignment -} from '@susisu/mte-kernel' -import TextEditorInterface from 'browser/lib/TextEditorInterface' -import eventEmitter from 'browser/main/lib/eventEmitter' -import iconv from 'iconv-lite' -import crypto from 'crypto' -import consts from 'browser/lib/consts' -import fs from 'fs' -const { - ipcRenderer -} = require('electron') + +const {ipcRenderer} = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import TurndownService from 'turndown' -import { - gfm -} from 'turndown-plugin-gfm' +import{gfm} from 'turndown-plugin-gfm' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({ - column: ruler - })) : []) + (enableRulers ? rulers.map(ruler => ({column: ruler})) : []) 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) { - if (el === this.refs.root) { - return - } - el = el.parentNode - } - this.props.onBlur != null && this.props.onBlur(e) + 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) { + if (el === this.refs.root) { + return + } + el = el.parentNode + } + this.props.onBlur != null && this.props.onBlur(e) - const { - storageKey, - noteKey - } = this.props - attachmentManagement.deleteAttachmentsNotPresentInNote( - this.editor.getValue(), - storageKey, - noteKey - ) - } - this.pasteHandler = (editor, e) => this.handlePaste(editor, e) - this.loadStyleHandler = e => { - this.editor.refresh() - } - this.searchHandler = (e, msg) => this.handleSearch(msg) - this.searchState = null - this.scrollToLineHandeler = this.scrollToLine.bind(this) + const {storageKey, noteKey} = this.props + attachmentManagement.deleteAttachmentsNotPresentInNote( + this.editor.getValue(), storageKey, noteKey) + } this.pasteHandler = (editor, e) => + this.handlePaste(editor, e) this.loadStyleHandler = + e => { + this.editor.refresh() + } this.searchHandler = (e, msg) => + this.handleSearch(msg) this.searchState = + null this.scrollToLineHandeler = + this.scrollToLine + .bind(this) - this.formatTable = () => this.handleFormatTable() - this.editorActivityHandler = () => this.handleEditorActivity() + this.formatTable = () => + this.handleFormatTable() this + .editorActivityHandler = () => + this.handleEditorActivity() } handleSearch(msg) { const cm = this.editor const component = this - if (component.searchState) cm.removeOverlay(component.searchState) + if (component.searchState) + cm.removeOverlay(component.searchState) if (msg.length < 3) return - cm.operation(function () { - component.searchState = makeOverlay(msg, 'searching') - cm.addOverlay(component.searchState) + cm.operation(function() { + component.searchState = makeOverlay(msg, 'searching') + cm.addOverlay(component.searchState) - function makeOverlay(query, style) { - query = new RegExp( - query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), - 'gi' - ) - return { - token: function (stream) { - query.lastIndex = stream.pos - var match = query.exec(stream.string) - if (match && match.index === stream.pos) { - stream.pos += match[0].length || 1 - return style - } else if (match) { - stream.pos = match.index - } else { - stream.skipToEnd() + function makeOverlay(query, style) { + query = new RegExp( + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), + 'gi') + return { + token: function(stream) { + query.lastIndex = stream.pos + var match = query.exec(stream.string) + if (match && match.index === stream.pos) { + stream.pos += match[0].length || 1 + return style + } + else if (match) { + stream.pos = match.index + } + else { + stream.skipToEnd() + } + } } } - } - } - }) + }) } handleFormatTable() { - this.tableEditor.formatAll(options({ - textWidthOptions: {} - })) + this.tableEditor.formatAll(options({textWidthOptions: {}})) } handleEditorActivity() { @@ -128,23 +117,23 @@ export default class CodeEditor extends React.Component { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { - this.extraKeysMode = 'editor' - this.editor.setOption('extraKeys', this.editorKeyMap) + this.extraKeysMode = + 'editor' this.editor.setOption('extraKeys', this.editorKeyMap) } - } else { + } + else { if (this.extraKeysMode !== 'default') { - this.extraKeysMode = 'default' - this.editor.setOption('extraKeys', this.defaultKeyMap) - this.tableEditor.resetSmartCursor() + this.extraKeysMode = + 'default' this.editor + .setOption( + 'extraKeys', + this.defaultKeyMap) this.tableEditor.resetSmartCursor() } } } componentDidMount() { - const { - rulers, - enableRulers - } = this.props + const {rulers, enableRulers} = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -152,109 +141,99 @@ export default class CodeEditor extends React.Component { id: crypto.randomBytes(16).toString('hex'), name: 'Dummy text', prefix: ['lorem', 'ipsum'], - content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - }] - if (!fs.existsSync(consts.SNIPPET_FILE)) { + content: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + }] if (!fs.existsSync(consts.SNIPPET_FILE)) { fs.writeFileSync( - consts.SNIPPET_FILE, - JSON.stringify(defaultSnippet, null, 4), - 'utf8' - ) + consts.SNIPPET_FILE, JSON.stringify(defaultSnippet, null, 4), 'utf8') } - this.defaultKeyMap = CodeMirror.normalizeKeyMap({ - Tab: function (cm) { - const cursor = cm.getCursor() - const line = cm.getLine(cursor.line) - const cursorPosition = cursor.ch - const charBeforeCursor = line.substr(cursorPosition - 1, 1) - if (cm.somethingSelected()) cm.indentSelection('add') - else { - const tabs = cm.getOption('indentWithTabs') - if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { - cm.execCommand('goLineStart') - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - cm.execCommand('goLineEnd') - } else if ( - !charBeforeCursor.match(/\t|\s|\r|\n/) && - cursor.ch > 1 - ) { - // text expansion on tab key if the char before is alphabet - const snippets = JSON.parse( - fs.readFileSync(consts.SNIPPET_FILE, 'utf8') - ) - if (expandSnippet(line, cursor, cm, snippets) === false) { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') + this.defaultKeyMap = + CodeMirror + .normalizeKeyMap({ + Tab: function(cm) { + const cursor = cm.getCursor() + const line = cm.getLine(cursor.line) + const cursorPosition = cursor.ch + const charBeforeCursor = line.substr(cursorPosition - 1, 1) + if (cm.somethingSelected()) cm.indentSelection('add') else { + const tabs = cm.getOption('indentWithTabs') + if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { + cm.execCommand('goLineStart') + if (tabs) { + cm.execCommand('insertTab') + } + else {cm.execCommand('insertSoftTab')} cm.execCommand( + 'goLineEnd') + } + else if ( + !charBeforeCursor.match(/\t|\s|\r|\n/) && cursor.ch > 1) { + // text expansion on tab key if the char before is alphabet + const snippets = + JSON.parse(fs.readFileSync(consts.SNIPPET_FILE, 'utf8')) + if (expandSnippet(line, cursor, cm, snippets) === false) { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + } + } + else { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + } + } + }, + 'Cmd-T': function(cm) { + // Do nothing + }, + Enter: 'boostNewLineAndIndentContinueMarkdownList', + 'Ctrl-C': cm => { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') { + document.execCommand('copy') + } + return CodeMirror.Pass } - } - } else { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - } - } - }, - 'Cmd-T': function (cm) { - // Do nothing - }, - Enter: 'boostNewLineAndIndentContinueMarkdownList', - 'Ctrl-C': cm => { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') { - document.execCommand('copy') - } - return CodeMirror.Pass - } - }) + }) - this.value = this.props.value - this.editor = CodeMirror(this.refs.root, { - rulers: buildCMRulers(rulers, enableRulers), - value: this.props.value, - lineNumbers: this.props.displayLineNumbers, - lineWrapping: true, - theme: this.props.theme, - indentUnit: this.props.indentSize, - tabSize: this.props.indentSize, - indentWithTabs: this.props.indentType !== 'space', - keyMap: this.props.keyMap, - scrollPastEnd: this.props.scrollPastEnd, - inputStyle: 'textarea', - dragDrop: false, - foldGutter: true, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: (this.props.enableBracketMatching ? { - pairs: '()[]{}\'\'""$$**``ll', - triples: '```"""\'\'\'', - explode: '[]{}``$$', - override: true - } : { - pairs: '', - triples: '', - explode: '', - override: true - }), - extraKeys: this.defaultKeyMap - }) + this.value = this.props.value this.editor = + CodeMirror(this.refs.root, { + rulers: buildCMRulers(rulers, enableRulers), + value: this.props.value, + lineNumbers: this.props.displayLineNumbers, + lineWrapping: true, + theme: this.props.theme, + indentUnit: this.props.indentSize, + tabSize: this.props.indentSize, + indentWithTabs: this.props.indentType !== 'space', + keyMap: this.props.keyMap, + scrollPastEnd: this.props.scrollPastEnd, + inputStyle: 'textarea', + dragDrop: false, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + autoCloseBrackets: { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, + override: true + }, + extraKeys: this.defaultKeyMap + }) - this.setMode(this.props.mode) + 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) + this.editor.on('focus', this.focusHandler) this.editor + .on('blur', this.blurHandler) this.editor + .on('change', this.changeHandler) this.editor.on( + 'paste', this.pasteHandler) eventEmitter.on('top:search', this.searchHandler) - eventEmitter.emit('code:init') - this.editor.on('scroll', this.scrollHandler) + eventEmitter.emit('code:init') this.editor.on('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -263,146 +242,151 @@ export default class CodeEditor extends React.Component { CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor) CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor) CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor) - CodeMirror.Vim.map('ZZ', ':q', 'normal') + CodeMirror.Vim + .map('ZZ', ':q', 'normal') - this.textEditorInterface = new TextEditorInterface(this.editor) - this.tableEditor = new TableEditor(this.textEditorInterface) - eventEmitter.on('code:format-table', this.formatTable) + this.textEditorInterface = + new TextEditorInterface(this.editor) this.tableEditor = + new TableEditor(this.textEditorInterface) + eventEmitter + .on('code:format-table', this.formatTable) - this.tableEditorOptions = options({ - smartCursor: true - }) + this.tableEditorOptions = options({smartCursor: true}) - this.editorKeyMap = CodeMirror.normalizeKeyMap({ - 'Tab': () => { - this.tableEditor.nextCell(this.tableEditorOptions) - }, - 'Shift-Tab': () => { - this.tableEditor.previousCell(this.tableEditorOptions) - }, - 'Enter': () => { - this.tableEditor.nextRow(this.tableEditorOptions) - }, - 'Ctrl-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Cmd-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Shift-Ctrl-Left': () => { - this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Cmd-Left': () => { - this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Right': () => { - this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Cmd-Right': () => { - this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Up': () => { - this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Cmd-Up': () => { - this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Ctrl-Down': () => { - this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) - }, - 'Shift-Cmd-Down': () => { - this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) - }, - 'Ctrl-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Cmd-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Ctrl-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Cmd-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Ctrl-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Cmd-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Ctrl-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Cmd-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Cmd-K Cmd-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Cmd-L Cmd-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Cmd-K Cmd-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Cmd-L Cmd-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - } - }) + this.editorKeyMap = + CodeMirror.normalizeKeyMap({ + 'Tab': () => { + this.tableEditor.nextCell(this.tableEditorOptions) + }, + 'Shift-Tab': () => { + this.tableEditor.previousCell(this.tableEditorOptions) + }, + 'Enter': () => { + this.tableEditor.nextRow(this.tableEditorOptions) + }, + 'Ctrl-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Cmd-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Shift-Ctrl-Left': () => { + this.tableEditor.alignColumn( + Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Cmd-Left': () => { + this.tableEditor.alignColumn( + Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Right': () => { + this.tableEditor.alignColumn( + Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Cmd-Right': () => { + this.tableEditor.alignColumn( + Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Up': () => { + this.tableEditor.alignColumn( + Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Cmd-Up': () => { + this.tableEditor.alignColumn( + Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Ctrl-Down': () => { + this.tableEditor.alignColumn( + Alignment.NONE, this.tableEditorOptions) + }, + 'Shift-Cmd-Down': () => { + this.tableEditor.alignColumn( + Alignment.NONE, this.tableEditorOptions) + }, + 'Ctrl-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Cmd-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Ctrl-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Cmd-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Ctrl-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Cmd-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Ctrl-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Cmd-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Cmd-K Cmd-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Cmd-L Cmd-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Cmd-K Cmd-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Cmd-L Cmd-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + } + }) if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) - this.editor.on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) this.editor + .on('changes', this.editorActivityHandler) } - this.setState({ - clientWidth: this.refs.root.clientWidth - }) + this.setState({clientWidth: this.refs.root.clientWidth}) } expandSnippet(line, cursor, cm, snippets) { - const wordBeforeCursor = this.getWordBeforeCursor( - line, - cursor.line, - cursor.ch - ) + const wordBeforeCursor = + this.getWordBeforeCursor(line, cursor.line, cursor.ch) const templateCursorString = ':{}' for (let i = 0; i < snippets.length; i++) { if (snippets[i].prefix.indexOf(wordBeforeCursor.text) !== -1) { @@ -416,10 +400,8 @@ export default class CodeEditor extends React.Component { cursorLineNumber = j cursorLinePosition = cursorIndex cm.replaceRange( - snippets[i].content.replace(templateCursorString, ''), - wordBeforeCursor.range.from, - wordBeforeCursor.range.to - ) + snippets[i].content.replace(templateCursorString, ''), + wordBeforeCursor.range.from, wordBeforeCursor.range.to) cm.setCursor({ line: cursor.line + cursorLineNumber, ch: cursorLinePosition @@ -428,10 +410,8 @@ export default class CodeEditor extends React.Component { } } else { cm.replaceRange( - snippets[i].content, - wordBeforeCursor.range.from, - wordBeforeCursor.range.to - ) + snippets[i].content, wordBeforeCursor.range.from, + wordBeforeCursor.range.to) } return true } @@ -446,7 +426,8 @@ export default class CodeEditor extends React.Component { const emptyChars = /\t|\s|\r|\n/ // to prevent the word to expand is long that will crash the whole app - // the safeStop is there to stop user to expand words that longer than 20 chars + // the safeStop is there to stop user to expand words that longer than 20 + // chars const safeStop = 20 while (cursorPosition > 0) { @@ -454,25 +435,17 @@ export default class CodeEditor extends React.Component { // if char is not an empty char if (!emptyChars.test(currentChar)) { wordBeforeCursor = currentChar + wordBeforeCursor - } else if (wordBeforeCursor.length >= safeStop) { - throw new Error('Your snippet trigger is too long !') - } else { - break } - cursorPosition-- + else if (wordBeforeCursor.length >= safeStop) { + throw new Error('Your snippet trigger is too long !') + } + else {break} cursorPosition-- } return { - text: wordBeforeCursor, - range: { - from: { - line: lineNumber, - ch: originCursorPosition - }, - to: { - line: lineNumber, - ch: cursorPosition - } + text: wordBeforeCursor, range: { + from: {line: lineNumber, ch: originCursorPosition}, + to: {line: lineNumber, ch: cursorPosition} } } } @@ -482,12 +455,12 @@ 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) - eventEmitter.off('top:search', this.searchHandler) - this.editor.off('scroll', this.scrollHandler) + this.editor.off('focus', this.focusHandler) this.editor + .off('blur', this.blurHandler) this.editor + .off('change', this.changeHandler) this.editor.off( + 'paste', this.pasteHandler) + eventEmitter.off('top:search', this.searchHandler) this.editor.off( + 'scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) @@ -496,10 +469,7 @@ export default class CodeEditor extends React.Component { componentDidUpdate(prevProps, prevState) { let needRefresh = false - const { - rulers, - enableRulers - } = this.props + const {rulers, enableRulers} = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) } @@ -517,16 +487,14 @@ export default class CodeEditor extends React.Component { needRefresh = true } - if ( - prevProps.enableRulers !== enableRulers || - prevProps.rulers !== rulers - ) { + if (prevProps.enableRulers !== enableRulers || + prevProps.rulers !== rulers) { this.editor.setOption('rulers', buildCMRulers(rulers, enableRulers)) } if (prevProps.indentSize !== this.props.indentSize) { - this.editor.setOption('indentUnit', this.props.indentSize) - this.editor.setOption('tabSize', this.props.indentSize) + this.editor.setOption('indentUnit', this.props.indentSize) this.editor + .setOption('tabSize', this.props.indentSize) } if (prevProps.indentType !== this.props.indentType) { this.editor.setOption('indentWithTabs', this.props.indentType !== 'space') @@ -540,38 +508,33 @@ export default class CodeEditor extends React.Component { this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd) } - if (prevProps.enableBracketMatching !== this.props.enableBracketMatching) { - const bracketObject = (this.props.enableBracketMatching ? { - pairs: '()[]{}\'\'""$$**``ll', - triples: '```"""\'\'\'', - explode: '[]{}``$$', + if (prevProps.matchingPairs !== this.props.matchingPairs || + prevProps.matchingTriples !== this.props.matchingTriples || + prevProps.explodingPairs !== this.props.explodingPairs) { + const bracketObject = { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, override: true - } : { - pairs: '', - triples: '', - explode: '', - override: true - }); + } this.editor.setOption('autoCloseBrackets', bracketObject) } if (prevProps.enableTableEditor !== this.props.enableTableEditor) { if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) - this.editor.on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) this.editor + .on('changes', this.editorActivityHandler) } else { - this.editor.off('cursorActivity', this.editorActivityHandler) - this.editor.off('changes', this.editorActivityHandler) + this.editor.off('cursorActivity', this.editorActivityHandler) this + .editor.off('changes', this.editorActivityHandler) } - this.extraKeysMode = 'default' - this.editor.setOption('extraKeys', this.defaultKeyMap) + this.extraKeysMode = + 'default' this.editor.setOption('extraKeys', this.defaultKeyMap) } if (this.state.clientWidth !== this.refs.root.clientWidth) { - this.setState({ - clientWidth: this.refs.root.clientWidth - }) + this.setState({clientWidth: this.refs.root.clientWidth}) needRefresh = true } @@ -583,9 +546,11 @@ export default class CodeEditor extends React.Component { setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) - if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') + if (syntax == null) syntax = + CodeMirror + .findModeByName('Plain Text') - this.editor.setOption('mode', syntax.mime) + this.editor.setOption('mode', syntax.mime) CodeMirror.autoLoadMode(this.editor, syntax.mode) } @@ -599,11 +564,7 @@ export default class CodeEditor extends React.Component { moveCursorTo(row, col) {} scrollToLine(event, num) { - const cursor = { - line: num, - ch: 1 - } - this.editor.setCursor(cursor) + const cursor = { line: num, ch: 1 } this.editor.setCursor(cursor) } focus() { @@ -616,32 +577,22 @@ export default class CodeEditor extends React.Component { reload() { // Change event shouldn't be fired when switch note - this.editor.off('change', this.changeHandler) - this.value = this.props.value - this.editor.setValue(this.props.value) - this.editor.clearHistory() - this.editor.on('change', this.changeHandler) - this.editor.refresh() + this.editor.off('change', this.changeHandler) this.value = + this.props.value this.editor.setValue(this.props.value) this.editor + .clearHistory() this.editor.on('change', this.changeHandler) this + .editor.refresh() } setValue(value) { - const cursor = this.editor.getCursor() - this.editor.setValue(value) - this.editor.setCursor(cursor) + const cursor = this.editor.getCursor() this.editor.setValue(value) this + .editor.setCursor(cursor) } handleDropImage(dropEvent) { dropEvent.preventDefault() - const { - storageKey, - noteKey - } = this.props + const {storageKey, noteKey} = this.props attachmentManagement.handleAttachmentDrop( - this, - storageKey, - noteKey, - dropEvent - ) + this, storageKey, noteKey, dropEvent) } insertAttachmentMd(imageMd) { @@ -650,59 +601,43 @@ export default class CodeEditor extends React.Component { handlePaste(editor, e) { const clipboardData = e.clipboardData - const { - storageKey, - noteKey - } = this.props - const dataTransferItem = clipboardData.items[0] - const pastedTxt = clipboardData.getData('text') - const isURL = str => { - const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ - return matcher.test(str) - } - const isInLinkTag = editor => { - const startCursor = editor.getCursor('start') - const prevChar = editor.getRange({ - line: startCursor.line, - ch: startCursor.ch - 2 - }, { - line: startCursor.line, - ch: startCursor.ch - }) - const endCursor = editor.getCursor('end') - const nextChar = editor.getRange({ - line: endCursor.line, - ch: endCursor.ch - }, { - line: endCursor.line, - ch: endCursor.ch + 1 - }) - return prevChar === '](' && nextChar === ')' - } + const {storageKey, noteKey} = this.props + const dataTransferItem = clipboardData.items[0] const pastedTxt = + clipboardData.getData('text') + const isURL = + str => { + const matcher = + /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ + return matcher.test(str) + } const isInLinkTag = + editor => { + const startCursor = editor.getCursor('start') + const prevChar = editor.getRange( + {line: startCursor.line, ch: startCursor.ch - 2}, + {line: startCursor.line, ch: startCursor.ch}) + const endCursor = editor.getCursor('end') + const nextChar = editor.getRange( + {line: endCursor.line, ch: endCursor.ch}, + {line: endCursor.line, ch: endCursor.ch + 1}) + return prevChar === '](' && nextChar === ')' + } const pastedHtml = clipboardData.getData('text/html') if (pastedHtml !== '') { this.handlePasteHtml(e, editor, pastedHtml) - } else if (dataTransferItem.type.match('image')) { + } + else if (dataTransferItem.type.match('image')) { attachmentManagement.handlePastImageEvent( - this, - storageKey, - noteKey, - dataTransferItem - ) - } else if ( - this.props.fetchUrlTitle && - isURL(pastedTxt) && - !isInLinkTag(editor) - ) { + this, storageKey, noteKey, dataTransferItem) + } + else if ( + this.props.fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { this.handlePasteUrl(e, editor, pastedTxt) } if (attachmentManagement.isAttachmentLink(pastedTxt)) { attachmentManagement - .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) - .then(modifiedText => { - this.editor.replaceSelection(modifiedText) - }) + .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) + .then(modifiedText => {this.editor.replaceSelection(modifiedText)}) e.preventDefault() } } @@ -718,39 +653,36 @@ export default class CodeEditor extends React.Component { const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) - const isImageReponse = response => { - return ( - response.headers.has('content-type') && - response.headers.get('content-type').match(/^image\/.+$/) - ) - } - const replaceTaggedUrl = replacement => { - const value = editor.getValue() - const cursor = editor.getCursor() - const newValue = value.replace(taggedUrl, replacement) - const newCursor = Object.assign({}, cursor, { - ch: cursor.ch + newValue.length - value.length - }) - editor.setValue(newValue) - editor.setCursor(newCursor) - } + const isImageReponse = + response => { + return ( + response.headers.has('content-type') && + response.headers.get('content-type').match(/^image\/.+$/)) + } const replaceTaggedUrl = + replacement => { + const value = editor.getValue() + const cursor = editor.getCursor() + const newValue = value.replace(taggedUrl, replacement) + const newCursor = Object.assign( + {}, cursor, {ch: cursor.ch + newValue.length - value.length}) + editor.setValue(newValue) + editor.setCursor(newCursor) + } - fetch(pastedTxt, { - method: 'get' - }) - .then(response => { - if (isImageReponse(response)) { - return this.mapImageResponse(response, pastedTxt) - } else { - return this.mapNormalResponse(response, pastedTxt) - } - }) - .then(replacement => { - replaceTaggedUrl(replacement) - }) - .catch(e => { - replaceTaggedUrl(pastedTxt) - }) + fetch(pastedTxt, {method: 'get'}) + .then(response => { + if (isImageReponse(response)) { + return this.mapImageResponse( + response, pastedTxt) + } else { + return this.mapNormalResponse( + response, pastedTxt) + } + }) + .then( + replacement => { + replaceTaggedUrl(replacement)}) + .catch(e => {replaceTaggedUrl(pastedTxt)}) } handlePasteHtml(e, editor, pastedHtml) { @@ -760,23 +692,21 @@ export default class CodeEditor extends React.Component { } mapNormalResponse(response, pastedTxt) { - return this.decodeResponse(response).then(body => { - return new Promise((resolve, reject) => { - try { - const parsedBody = new window.DOMParser().parseFromString( - body, - 'text/html' - ) - const escapePipe = (str) => { - return str.replace('|', '\\|') + return this.decodeResponse(response).then( + body => {return new Promise((resolve, reject) => { + try { + const parsedBody = + new window.DOMParser().parseFromString(body, 'text/html') + const escapePipe = + (str) => { + return str.replace('|', '\\|') + } const linkWithTitle = + `[${escapePipe(parsedBody.title)}](${pastedTxt})` + resolve(linkWithTitle) + } catch (e) { + reject(e) } - const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})` - resolve(linkWithTitle) - } catch (e) { - reject(e) - } - }) - }) + })}) } mapImageResponse(response, pastedTxt) { @@ -795,39 +725,30 @@ 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) - } - }) - }) + 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] + 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 + const {className, fontSize} = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width return ( < @@ -835,18 +756,12 @@ export default class CodeEditor extends React.Component { className == null ? 'CodeEditor' : `CodeEditor ${className}` } ref = 'root' - tabIndex = '-1' - style = { - { - fontFamily, - fontSize: fontSize, - width: width - } - } - onDrop = { - e => this.handleDropImage(e) - } - /> + tabIndex = '-1' + style = { + { fontFamily, fontSize: fontSize, width: width } + } onDrop = { + e => this.handleDropImage(e) + } /> ) } } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index f10ea8bc..dcb1b5ea 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -269,6 +269,9 @@ class MarkdownEditor extends React.Component { rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} scrollPastEnd={config.editor.scrollPastEnd} storageKey={storageKey} noteKey={noteKey} diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 004a6eb0..689799c8 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -159,6 +159,9 @@ class MarkdownSplitEditor extends React.Component { fontSize={editorFontSize} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} indentType={config.editor.indentType} indentSize={editorIndentSize} enableRulers={config.editor.enableRulers} diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 90b8faa9..86ce2c7a 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -700,6 +700,9 @@ class SnippetNoteDetail extends React.Component { indentSize={editorIndentSize} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} keyMap={config.editor.keyMap} scrollPastEnd={config.editor.scrollPastEnd} fetchUrlTitle={config.editor.fetchUrlTitle} diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index eddf0909..c86e9288 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -45,6 +45,9 @@ export const DEFAULT_CONFIG = { rulers: [80, 120], displayLineNumbers: true, enableBracketMatching: true, + matchingPairs:'()[]{}\'\'""$$**``', + matchingTriples:'```"""\'\'\'', + explodingPairs:'[]{}``$$', switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE' scrollPastEnd: false, diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index 40b65d39..ebd4012e 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -137,6 +137,9 @@ class SnippetTab extends React.Component { rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} scrollPastEnd={config.editor.scrollPastEnd} onRef={ref => { this.snippetEditor = ref }} />
diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index ab72c914..2bd0c9d0 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -95,7 +95,10 @@ class UiTab extends React.Component { enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, frontMatterTitleField: this.refs.frontMatterTitleField.value, - enableBracketMatching: this.refs.enableBracketMatching.checked + enableBracketMatching: this.refs.enableBracketMatching.checked, + matchingPairs: this.refs.matchingPairs.value, + matchingTriples: this.refs.matchingTriples.value, + explodingPairs: this.refs.explodingPairs.value }, preview: { fontSize: this.refs.previewFontSize.value, @@ -551,6 +554,48 @@ class UiTab extends React.Component {
+
+
+ {i18n.__('Matching character pairs')} +
+
+ this.handleUIChange(e)} + type='text' + /> +
+
+ +
+
+ {i18n.__('Matching character triples')} +
+
+ this.handleUIChange(e)} + type='text' + /> +
+
+ +
+
+ {i18n.__('Exploding character pairs')} +
+
+ this.handleUIChange(e)} + type='text' + /> +
+
+
{i18n.__('Preview')}
@@ -578,6 +623,7 @@ class UiTab extends React.Component { />
+
{i18n.__('Code Block Theme')}
From 441c70b388e7505cdacb7286c6bd40676326a164 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 13:25:22 +0000 Subject: [PATCH 013/159] Removing checkmark and fixed Code Editor indentation --- browser/components/CodeEditor.js | 973 ++++++++++-------- browser/components/MarkdownEditor.js | 1 - browser/components/MarkdownSplitEditor.js | 1 - browser/main/Detail/SnippetNoteDetail.js | 1 - browser/main/lib/ConfigManager.js | 1 - .../modals/PreferencesModal/SnippetEditor.js | 15 +- .../modals/PreferencesModal/SnippetTab.js | 1 - browser/main/modals/PreferencesModal/UiTab.js | 12 - 8 files changed, 530 insertions(+), 475 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 3e416ab2..83f64a18 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -1,110 +1,121 @@ -import 'codemirror-mode-elixir' - -import {Alignment, options, TableEditor} from '@susisu/mte-kernel' -import consts from 'browser/lib/consts' -import convertModeName from 'browser/lib/convertModeName' -import TextEditorInterface from 'browser/lib/TextEditorInterface' -import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' -import eventEmitter from 'browser/main/lib/eventEmitter' -import CodeMirror from 'codemirror' -import crypto from 'crypto' -import fs from 'fs' -import iconv from 'iconv-lite' -import _ from 'lodash' import PropTypes from 'prop-types' import React from 'react' - -const {ipcRenderer} = require('electron') +import _ from 'lodash' +import CodeMirror from 'codemirror' +import 'codemirror-mode-elixir' +import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' +import convertModeName from 'browser/lib/convertModeName' +import { + options, + TableEditor, + Alignment +} from '@susisu/mte-kernel' +import TextEditorInterface from 'browser/lib/TextEditorInterface' +import eventEmitter from 'browser/main/lib/eventEmitter' +import iconv from 'iconv-lite' +import crypto from 'crypto' +import consts from 'browser/lib/consts' +import fs from 'fs' +const { + ipcRenderer +} = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import TurndownService from 'turndown' -import{gfm} from 'turndown-plugin-gfm' +import { + gfm +} from 'turndown-plugin-gfm' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({column: ruler})) : []) + (enableRulers ? rulers.map(ruler => ({ + column: ruler + })) : []) 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) { - if (el === this.refs.root) { - return - } - el = el.parentNode - } - this.props.onBlur != null && this.props.onBlur(e) + 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) { + if (el === this.refs.root) { + return + } + el = el.parentNode + } + this.props.onBlur != null && this.props.onBlur(e) - const {storageKey, noteKey} = this.props - attachmentManagement.deleteAttachmentsNotPresentInNote( - this.editor.getValue(), storageKey, noteKey) - } this.pasteHandler = (editor, e) => - this.handlePaste(editor, e) this.loadStyleHandler = - e => { - this.editor.refresh() - } this.searchHandler = (e, msg) => - this.handleSearch(msg) this.searchState = - null this.scrollToLineHandeler = - this.scrollToLine - .bind(this) + const { + storageKey, + noteKey + } = this.props + attachmentManagement.deleteAttachmentsNotPresentInNote( + this.editor.getValue(), + storageKey, + noteKey + ) + } + this.pasteHandler = (editor, e) => this.handlePaste(editor, e) + this.loadStyleHandler = e => { + this.editor.refresh() + } + this.searchHandler = (e, msg) => this.handleSearch(msg) + this.searchState = null + this.scrollToLineHandeler = this.scrollToLine.bind(this) - this.formatTable = () => - this.handleFormatTable() this - .editorActivityHandler = () => - this.handleEditorActivity() + this.formatTable = () => this.handleFormatTable() + this.editorActivityHandler = () => this.handleEditorActivity() } handleSearch(msg) { const cm = this.editor const component = this - if (component.searchState) - cm.removeOverlay(component.searchState) + if (component.searchState) cm.removeOverlay(component.searchState) if (msg.length < 3) return - cm.operation(function() { - component.searchState = makeOverlay(msg, 'searching') - cm.addOverlay(component.searchState) + cm.operation(function () { + component.searchState = makeOverlay(msg, 'searching') + cm.addOverlay(component.searchState) - function makeOverlay(query, style) { - query = new RegExp( - query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), - 'gi') - return { - token: function(stream) { - query.lastIndex = stream.pos - var match = query.exec(stream.string) - if (match && match.index === stream.pos) { - stream.pos += match[0].length || 1 - return style - } - else if (match) { - stream.pos = match.index - } - else { - stream.skipToEnd() - } - } + function makeOverlay(query, style) { + query = new RegExp( + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), + 'gi' + ) + return { + token: function (stream) { + query.lastIndex = stream.pos + var match = query.exec(stream.string) + if (match && match.index === stream.pos) { + stream.pos += match[0].length || 1 + return style + } else if (match) { + stream.pos = match.index + } else { + stream.skipToEnd() } } - }) + } + } + }) } handleFormatTable() { - this.tableEditor.formatAll(options({textWidthOptions: {}})) + this.tableEditor.formatAll(options({ + textWidthOptions: {} + })) } handleEditorActivity() { @@ -117,23 +128,23 @@ export default class CodeEditor extends React.Component { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { - this.extraKeysMode = - 'editor' this.editor.setOption('extraKeys', this.editorKeyMap) + this.extraKeysMode = 'editor' + this.editor.setOption('extraKeys', this.editorKeyMap) } - } - else { + } else { if (this.extraKeysMode !== 'default') { - this.extraKeysMode = - 'default' this.editor - .setOption( - 'extraKeys', - this.defaultKeyMap) this.tableEditor.resetSmartCursor() + this.extraKeysMode = 'default' + this.editor.setOption('extraKeys', this.defaultKeyMap) + this.tableEditor.resetSmartCursor() } } } componentDidMount() { - const {rulers, enableRulers} = this.props + const { + rulers, + enableRulers + } = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -141,99 +152,104 @@ export default class CodeEditor extends React.Component { id: crypto.randomBytes(16).toString('hex'), name: 'Dummy text', prefix: ['lorem', 'ipsum'], - content: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - }] if (!fs.existsSync(consts.SNIPPET_FILE)) { + content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + }] + if (!fs.existsSync(consts.SNIPPET_FILE)) { fs.writeFileSync( - consts.SNIPPET_FILE, JSON.stringify(defaultSnippet, null, 4), 'utf8') + consts.SNIPPET_FILE, + JSON.stringify(defaultSnippet, null, 4), + 'utf8' + ) } - this.defaultKeyMap = - CodeMirror - .normalizeKeyMap({ - Tab: function(cm) { - const cursor = cm.getCursor() - const line = cm.getLine(cursor.line) - const cursorPosition = cursor.ch - const charBeforeCursor = line.substr(cursorPosition - 1, 1) - if (cm.somethingSelected()) cm.indentSelection('add') else { - const tabs = cm.getOption('indentWithTabs') - if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { - cm.execCommand('goLineStart') - if (tabs) { - cm.execCommand('insertTab') - } - else {cm.execCommand('insertSoftTab')} cm.execCommand( - 'goLineEnd') - } - else if ( - !charBeforeCursor.match(/\t|\s|\r|\n/) && cursor.ch > 1) { - // text expansion on tab key if the char before is alphabet - const snippets = - JSON.parse(fs.readFileSync(consts.SNIPPET_FILE, 'utf8')) - if (expandSnippet(line, cursor, cm, snippets) === false) { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - } - } - else { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - } - } - }, - 'Cmd-T': function(cm) { - // Do nothing - }, - Enter: 'boostNewLineAndIndentContinueMarkdownList', - 'Ctrl-C': cm => { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') { - document.execCommand('copy') - } - return CodeMirror.Pass + this.defaultKeyMap = CodeMirror.normalizeKeyMap({ + Tab: function (cm) { + const cursor = cm.getCursor() + const line = cm.getLine(cursor.line) + const cursorPosition = cursor.ch + const charBeforeCursor = line.substr(cursorPosition - 1, 1) + if (cm.somethingSelected()) cm.indentSelection('add') + else { + const tabs = cm.getOption('indentWithTabs') + if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { + cm.execCommand('goLineStart') + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + cm.execCommand('goLineEnd') + } else if ( + !charBeforeCursor.match(/\t|\s|\r|\n/) && + cursor.ch > 1 + ) { + // text expansion on tab key if the char before is alphabet + const snippets = JSON.parse( + fs.readFileSync(consts.SNIPPET_FILE, 'utf8') + ) + if (expandSnippet(line, cursor, cm, snippets) === false) { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') } - }) + } + } else { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + } + } + }, + 'Cmd-T': function (cm) { + // Do nothing + }, + Enter: 'boostNewLineAndIndentContinueMarkdownList', + 'Ctrl-C': cm => { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') { + document.execCommand('copy') + } + return CodeMirror.Pass + } + }) - this.value = this.props.value this.editor = - CodeMirror(this.refs.root, { - rulers: buildCMRulers(rulers, enableRulers), - value: this.props.value, - lineNumbers: this.props.displayLineNumbers, - lineWrapping: true, - theme: this.props.theme, - indentUnit: this.props.indentSize, - tabSize: this.props.indentSize, - indentWithTabs: this.props.indentType !== 'space', - keyMap: this.props.keyMap, - scrollPastEnd: this.props.scrollPastEnd, - inputStyle: 'textarea', - dragDrop: false, - foldGutter: true, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: { - pairs: this.props.matchingPairs, - triples: this.props.matchingTriples, - explode: this.props.explodingPairs, - override: true - }, - extraKeys: this.defaultKeyMap - }) + this.value = this.props.value + this.editor = CodeMirror(this.refs.root, { + rulers: buildCMRulers(rulers, enableRulers), + value: this.props.value, + lineNumbers: this.props.displayLineNumbers, + lineWrapping: true, + theme: this.props.theme, + indentUnit: this.props.indentSize, + tabSize: this.props.indentSize, + indentWithTabs: this.props.indentType !== 'space', + keyMap: this.props.keyMap, + scrollPastEnd: this.props.scrollPastEnd, + inputStyle: 'textarea', + dragDrop: false, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + autoCloseBrackets: { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, + override: true + }, + extraKeys: this.defaultKeyMap + }) - this.setMode(this.props.mode) + 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) + this.editor.on('focus', this.focusHandler) + this.editor.on('blur', this.blurHandler) + this.editor.on('change', this.changeHandler) + this.editor.on('paste', this.pasteHandler) eventEmitter.on('top:search', this.searchHandler) - eventEmitter.emit('code:init') this.editor.on('scroll', this.scrollHandler) + eventEmitter.emit('code:init') + this.editor.on('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -242,151 +258,146 @@ export default class CodeEditor extends React.Component { CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor) CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor) CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor) - CodeMirror.Vim - .map('ZZ', ':q', 'normal') + CodeMirror.Vim.map('ZZ', ':q', 'normal') - this.textEditorInterface = - new TextEditorInterface(this.editor) this.tableEditor = - new TableEditor(this.textEditorInterface) - eventEmitter - .on('code:format-table', this.formatTable) + this.textEditorInterface = new TextEditorInterface(this.editor) + this.tableEditor = new TableEditor(this.textEditorInterface) + eventEmitter.on('code:format-table', this.formatTable) - this.tableEditorOptions = options({smartCursor: true}) + this.tableEditorOptions = options({ + smartCursor: true + }) - this.editorKeyMap = - CodeMirror.normalizeKeyMap({ - 'Tab': () => { - this.tableEditor.nextCell(this.tableEditorOptions) - }, - 'Shift-Tab': () => { - this.tableEditor.previousCell(this.tableEditorOptions) - }, - 'Enter': () => { - this.tableEditor.nextRow(this.tableEditorOptions) - }, - 'Ctrl-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Cmd-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Shift-Ctrl-Left': () => { - this.tableEditor.alignColumn( - Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Cmd-Left': () => { - this.tableEditor.alignColumn( - Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Right': () => { - this.tableEditor.alignColumn( - Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Cmd-Right': () => { - this.tableEditor.alignColumn( - Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Up': () => { - this.tableEditor.alignColumn( - Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Cmd-Up': () => { - this.tableEditor.alignColumn( - Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Ctrl-Down': () => { - this.tableEditor.alignColumn( - Alignment.NONE, this.tableEditorOptions) - }, - 'Shift-Cmd-Down': () => { - this.tableEditor.alignColumn( - Alignment.NONE, this.tableEditorOptions) - }, - 'Ctrl-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Cmd-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Ctrl-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Cmd-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Ctrl-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Cmd-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Ctrl-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Cmd-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Cmd-K Cmd-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Cmd-L Cmd-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Cmd-K Cmd-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Cmd-L Cmd-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - } - }) + this.editorKeyMap = CodeMirror.normalizeKeyMap({ + 'Tab': () => { + this.tableEditor.nextCell(this.tableEditorOptions) + }, + 'Shift-Tab': () => { + this.tableEditor.previousCell(this.tableEditorOptions) + }, + 'Enter': () => { + this.tableEditor.nextRow(this.tableEditorOptions) + }, + 'Ctrl-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Cmd-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Shift-Ctrl-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Cmd-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Cmd-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Cmd-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Ctrl-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Shift-Cmd-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Ctrl-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Cmd-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Ctrl-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Cmd-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Ctrl-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Cmd-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Ctrl-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Cmd-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Cmd-K Cmd-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Cmd-L Cmd-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Cmd-K Cmd-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Cmd-L Cmd-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + } + }) if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) this.editor - .on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) + this.editor.on('changes', this.editorActivityHandler) } - this.setState({clientWidth: this.refs.root.clientWidth}) + this.setState({ + clientWidth: this.refs.root.clientWidth + }) } expandSnippet(line, cursor, cm, snippets) { - const wordBeforeCursor = - this.getWordBeforeCursor(line, cursor.line, cursor.ch) + const wordBeforeCursor = this.getWordBeforeCursor( + line, + cursor.line, + cursor.ch + ) const templateCursorString = ':{}' for (let i = 0; i < snippets.length; i++) { if (snippets[i].prefix.indexOf(wordBeforeCursor.text) !== -1) { @@ -400,8 +411,10 @@ export default class CodeEditor extends React.Component { cursorLineNumber = j cursorLinePosition = cursorIndex cm.replaceRange( - snippets[i].content.replace(templateCursorString, ''), - wordBeforeCursor.range.from, wordBeforeCursor.range.to) + snippets[i].content.replace(templateCursorString, ''), + wordBeforeCursor.range.from, + wordBeforeCursor.range.to + ) cm.setCursor({ line: cursor.line + cursorLineNumber, ch: cursorLinePosition @@ -410,8 +423,10 @@ export default class CodeEditor extends React.Component { } } else { cm.replaceRange( - snippets[i].content, wordBeforeCursor.range.from, - wordBeforeCursor.range.to) + snippets[i].content, + wordBeforeCursor.range.from, + wordBeforeCursor.range.to + ) } return true } @@ -426,8 +441,7 @@ export default class CodeEditor extends React.Component { const emptyChars = /\t|\s|\r|\n/ // to prevent the word to expand is long that will crash the whole app - // the safeStop is there to stop user to expand words that longer than 20 - // chars + // the safeStop is there to stop user to expand words that longer than 20 chars const safeStop = 20 while (cursorPosition > 0) { @@ -435,17 +449,25 @@ export default class CodeEditor extends React.Component { // if char is not an empty char if (!emptyChars.test(currentChar)) { wordBeforeCursor = currentChar + wordBeforeCursor - } - else if (wordBeforeCursor.length >= safeStop) { + } else if (wordBeforeCursor.length >= safeStop) { throw new Error('Your snippet trigger is too long !') + } else { + break } - else {break} cursorPosition-- + cursorPosition-- } return { - text: wordBeforeCursor, range: { - from: {line: lineNumber, ch: originCursorPosition}, - to: {line: lineNumber, ch: cursorPosition} + text: wordBeforeCursor, + range: { + from: { + line: lineNumber, + ch: originCursorPosition + }, + to: { + line: lineNumber, + ch: cursorPosition + } } } } @@ -455,12 +477,12 @@ 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) - eventEmitter.off('top:search', this.searchHandler) this.editor.off( - 'scroll', this.scrollHandler) + this.editor.off('focus', this.focusHandler) + this.editor.off('blur', this.blurHandler) + this.editor.off('change', this.changeHandler) + this.editor.off('paste', this.pasteHandler) + eventEmitter.off('top:search', this.searchHandler) + this.editor.off('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) @@ -469,7 +491,10 @@ export default class CodeEditor extends React.Component { componentDidUpdate(prevProps, prevState) { let needRefresh = false - const {rulers, enableRulers} = this.props + const { + rulers, + enableRulers + } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) } @@ -487,14 +512,16 @@ export default class CodeEditor extends React.Component { needRefresh = true } - if (prevProps.enableRulers !== enableRulers || - prevProps.rulers !== rulers) { + if ( + prevProps.enableRulers !== enableRulers || + prevProps.rulers !== rulers + ) { this.editor.setOption('rulers', buildCMRulers(rulers, enableRulers)) } if (prevProps.indentSize !== this.props.indentSize) { - this.editor.setOption('indentUnit', this.props.indentSize) this.editor - .setOption('tabSize', this.props.indentSize) + this.editor.setOption('indentUnit', this.props.indentSize) + this.editor.setOption('tabSize', this.props.indentSize) } if (prevProps.indentType !== this.props.indentType) { this.editor.setOption('indentWithTabs', this.props.indentType !== 'space') @@ -509,9 +536,9 @@ export default class CodeEditor extends React.Component { } if (prevProps.matchingPairs !== this.props.matchingPairs || - prevProps.matchingTriples !== this.props.matchingTriples || - prevProps.explodingPairs !== this.props.explodingPairs) { - const bracketObject = { + prevProps.matchingTriples !== this.props.matchingTriples || + prevProps.explodingPairs !== this.props.explodingPairs) { + const bracketObject ={ pairs: this.props.matchingPairs, triples: this.props.matchingTriples, explode: this.props.explodingPairs, @@ -522,19 +549,21 @@ export default class CodeEditor extends React.Component { if (prevProps.enableTableEditor !== this.props.enableTableEditor) { if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) this.editor - .on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) + this.editor.on('changes', this.editorActivityHandler) } else { - this.editor.off('cursorActivity', this.editorActivityHandler) this - .editor.off('changes', this.editorActivityHandler) + this.editor.off('cursorActivity', this.editorActivityHandler) + this.editor.off('changes', this.editorActivityHandler) } - this.extraKeysMode = - 'default' this.editor.setOption('extraKeys', this.defaultKeyMap) + this.extraKeysMode = 'default' + this.editor.setOption('extraKeys', this.defaultKeyMap) } if (this.state.clientWidth !== this.refs.root.clientWidth) { - this.setState({clientWidth: this.refs.root.clientWidth}) + this.setState({ + clientWidth: this.refs.root.clientWidth + }) needRefresh = true } @@ -546,11 +575,9 @@ export default class CodeEditor extends React.Component { setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) - if (syntax == null) syntax = - CodeMirror - .findModeByName('Plain Text') + if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') - this.editor.setOption('mode', syntax.mime) + this.editor.setOption('mode', syntax.mime) CodeMirror.autoLoadMode(this.editor, syntax.mode) } @@ -564,7 +591,11 @@ export default class CodeEditor extends React.Component { moveCursorTo(row, col) {} scrollToLine(event, num) { - const cursor = { line: num, ch: 1 } this.editor.setCursor(cursor) + const cursor = { + line: num, + ch: 1 + } + this.editor.setCursor(cursor) } focus() { @@ -577,22 +608,32 @@ export default class CodeEditor extends React.Component { reload() { // Change event shouldn't be fired when switch note - this.editor.off('change', this.changeHandler) this.value = - this.props.value this.editor.setValue(this.props.value) this.editor - .clearHistory() this.editor.on('change', this.changeHandler) this - .editor.refresh() + this.editor.off('change', this.changeHandler) + this.value = this.props.value + this.editor.setValue(this.props.value) + this.editor.clearHistory() + this.editor.on('change', this.changeHandler) + this.editor.refresh() } setValue(value) { - const cursor = this.editor.getCursor() this.editor.setValue(value) this - .editor.setCursor(cursor) + const cursor = this.editor.getCursor() + this.editor.setValue(value) + this.editor.setCursor(cursor) } handleDropImage(dropEvent) { dropEvent.preventDefault() - const {storageKey, noteKey} = this.props + const { + storageKey, + noteKey + } = this.props attachmentManagement.handleAttachmentDrop( - this, storageKey, noteKey, dropEvent) + this, + storageKey, + noteKey, + dropEvent + ) } insertAttachmentMd(imageMd) { @@ -601,43 +642,59 @@ export default class CodeEditor extends React.Component { handlePaste(editor, e) { const clipboardData = e.clipboardData - const {storageKey, noteKey} = this.props - const dataTransferItem = clipboardData.items[0] const pastedTxt = - clipboardData.getData('text') - const isURL = - str => { - const matcher = - /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ - return matcher.test(str) - } const isInLinkTag = - editor => { - const startCursor = editor.getCursor('start') - const prevChar = editor.getRange( - {line: startCursor.line, ch: startCursor.ch - 2}, - {line: startCursor.line, ch: startCursor.ch}) - const endCursor = editor.getCursor('end') - const nextChar = editor.getRange( - {line: endCursor.line, ch: endCursor.ch}, - {line: endCursor.line, ch: endCursor.ch + 1}) - return prevChar === '](' && nextChar === ')' - } + const { + storageKey, + noteKey + } = this.props + const dataTransferItem = clipboardData.items[0] + const pastedTxt = clipboardData.getData('text') + const isURL = str => { + const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ + return matcher.test(str) + } + const isInLinkTag = editor => { + const startCursor = editor.getCursor('start') + const prevChar = editor.getRange({ + line: startCursor.line, + ch: startCursor.ch - 2 + }, { + line: startCursor.line, + ch: startCursor.ch + }) + const endCursor = editor.getCursor('end') + const nextChar = editor.getRange({ + line: endCursor.line, + ch: endCursor.ch + }, { + line: endCursor.line, + ch: endCursor.ch + 1 + }) + return prevChar === '](' && nextChar === ')' + } const pastedHtml = clipboardData.getData('text/html') if (pastedHtml !== '') { this.handlePasteHtml(e, editor, pastedHtml) - } - else if (dataTransferItem.type.match('image')) { + } else if (dataTransferItem.type.match('image')) { attachmentManagement.handlePastImageEvent( - this, storageKey, noteKey, dataTransferItem) - } - else if ( - this.props.fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { + this, + storageKey, + noteKey, + dataTransferItem + ) + } else if ( + this.props.fetchUrlTitle && + isURL(pastedTxt) && + !isInLinkTag(editor) + ) { this.handlePasteUrl(e, editor, pastedTxt) } if (attachmentManagement.isAttachmentLink(pastedTxt)) { attachmentManagement - .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) - .then(modifiedText => {this.editor.replaceSelection(modifiedText)}) + .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) + .then(modifiedText => { + this.editor.replaceSelection(modifiedText) + }) e.preventDefault() } } @@ -653,36 +710,39 @@ export default class CodeEditor extends React.Component { const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) - const isImageReponse = - response => { - return ( - response.headers.has('content-type') && - response.headers.get('content-type').match(/^image\/.+$/)) - } const replaceTaggedUrl = - replacement => { - const value = editor.getValue() - const cursor = editor.getCursor() - const newValue = value.replace(taggedUrl, replacement) - const newCursor = Object.assign( - {}, cursor, {ch: cursor.ch + newValue.length - value.length}) - editor.setValue(newValue) - editor.setCursor(newCursor) - } + const isImageReponse = response => { + return ( + response.headers.has('content-type') && + response.headers.get('content-type').match(/^image\/.+$/) + ) + } + const replaceTaggedUrl = replacement => { + const value = editor.getValue() + const cursor = editor.getCursor() + const newValue = value.replace(taggedUrl, replacement) + const newCursor = Object.assign({}, cursor, { + ch: cursor.ch + newValue.length - value.length + }) + editor.setValue(newValue) + editor.setCursor(newCursor) + } - fetch(pastedTxt, {method: 'get'}) - .then(response => { - if (isImageReponse(response)) { - return this.mapImageResponse( - response, pastedTxt) - } else { - return this.mapNormalResponse( - response, pastedTxt) - } - }) - .then( - replacement => { - replaceTaggedUrl(replacement)}) - .catch(e => {replaceTaggedUrl(pastedTxt)}) + fetch(pastedTxt, { + method: 'get' + }) + .then(response => { + if (isImageReponse(response)) { + return this.mapImageResponse(response, pastedTxt) + } else { + return this.mapNormalResponse(response, pastedTxt) + } + }) + .then(replacement => { + replaceTaggedUrl(replacement) + }) + .catch(e => { + replaceTaggedUrl(pastedTxt) + }) } handlePasteHtml(e, editor, pastedHtml) { @@ -692,21 +752,23 @@ export default class CodeEditor extends React.Component { } mapNormalResponse(response, pastedTxt) { - return this.decodeResponse(response).then( - body => {return new Promise((resolve, reject) => { - try { - const parsedBody = - new window.DOMParser().parseFromString(body, 'text/html') - const escapePipe = - (str) => { - return str.replace('|', '\\|') - } const linkWithTitle = - `[${escapePipe(parsedBody.title)}](${pastedTxt})` - resolve(linkWithTitle) - } catch (e) { - reject(e) + return this.decodeResponse(response).then(body => { + return new Promise((resolve, reject) => { + try { + const parsedBody = new window.DOMParser().parseFromString( + body, + 'text/html' + ) + const escapePipe = (str) => { + return str.replace('|', '\\|') } - })}) + const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})` + resolve(linkWithTitle) + } catch (e) { + reject(e) + } + }) + }) } mapImageResponse(response, pastedTxt) { @@ -725,30 +787,39 @@ 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) - } - })}) + 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] + 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 + const { + className, + fontSize + } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width return ( < @@ -756,12 +827,18 @@ export default class CodeEditor extends React.Component { className == null ? 'CodeEditor' : `CodeEditor ${className}` } ref = 'root' - tabIndex = '-1' - style = { - { fontFamily, fontSize: fontSize, width: width } - } onDrop = { - e => this.handleDropImage(e) - } /> + tabIndex = '-1' + style = { + { + fontFamily, + fontSize: fontSize, + width: width + } + } + onDrop = { + e => this.handleDropImage(e) + } + /> ) } } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index dcb1b5ea..fcfc636e 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -268,7 +268,6 @@ class MarkdownEditor extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 689799c8..58867bac 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -158,7 +158,6 @@ class MarkdownSplitEditor extends React.Component { fontFamily={config.editor.fontFamily} fontSize={editorFontSize} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 86ce2c7a..d252d274 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -699,7 +699,6 @@ class SnippetNoteDetail extends React.Component { indentType={config.editor.indentType} indentSize={editorIndentSize} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index c86e9288..942ce93d 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -44,7 +44,6 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, - enableBracketMatching: true, matchingPairs:'()[]{}\'\'""$$**``', matchingTriples:'```"""\'\'\'', explodingPairs:'[]{}``$$', diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index ec3698c7..071f265f 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -27,17 +27,12 @@ class SnippetEditor extends React.Component { dragDrop: false, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: (this.enableBracketMatching ? { - pairs: '()[]{}\'\'""$$**``ll', - triples: '```"""\'\'\'', - explode: '[]{}``$$', + autoCloseBrackets: { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, override: true - }: { - pairs: '', - triples: '', - explode: '', - override: true - }), + }, mode: 'null' }) this.cm.setSize('100%', '100%') diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index ebd4012e..5f5b0aac 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -136,7 +136,6 @@ class SnippetTab extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 2bd0c9d0..ac020238 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -95,7 +95,6 @@ class UiTab extends React.Component { enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, frontMatterTitleField: this.refs.frontMatterTitleField.value, - enableBracketMatching: this.refs.enableBracketMatching.checked, matchingPairs: this.refs.matchingPairs.value, matchingTriples: this.refs.matchingTriples.value, explodingPairs: this.refs.explodingPairs.value @@ -543,17 +542,6 @@ class UiTab extends React.Component {
-
- -
-
{i18n.__('Matching character pairs')} From a46c51945973710be2159969256a05ea2db31ed3 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 14:03:21 +0000 Subject: [PATCH 014/159] Fixing indentations --- browser/components/CodeEditor.js | 104 +++++++++++++++---------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 83f64a18..9037c2e2 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -33,7 +33,7 @@ const buildCMRulers = (rulers, enableRulers) => })) : []) export default class CodeEditor extends React.Component { - constructor(props) { + constructor (props) { super(props) this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { @@ -78,7 +78,7 @@ export default class CodeEditor extends React.Component { this.editorActivityHandler = () => this.handleEditorActivity() } - handleSearch(msg) { + handleSearch (msg) { const cm = this.editor const component = this @@ -89,7 +89,7 @@ export default class CodeEditor extends React.Component { component.searchState = makeOverlay(msg, 'searching') cm.addOverlay(component.searchState) - function makeOverlay(query, style) { + function makeOverlay (query, style) { query = new RegExp( query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi' @@ -112,19 +112,19 @@ export default class CodeEditor extends React.Component { }) } - handleFormatTable() { + handleFormatTable () { this.tableEditor.formatAll(options({ textWidthOptions: {} })) } - handleEditorActivity() { + handleEditorActivity () { if (!this.textEditorInterface.transaction) { this.updateTableEditorState() } } - updateTableEditorState() { + updateTableEditorState () { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { @@ -140,7 +140,7 @@ export default class CodeEditor extends React.Component { } } - componentDidMount() { + componentDidMount () { const { rulers, enableRulers @@ -392,7 +392,7 @@ export default class CodeEditor extends React.Component { }) } - expandSnippet(line, cursor, cm, snippets) { + expandSnippet (line, cursor, cm, snippets) { const wordBeforeCursor = this.getWordBeforeCursor( line, cursor.line, @@ -435,7 +435,7 @@ export default class CodeEditor extends React.Component { return false } - getWordBeforeCursor(line, lineNumber, cursorPosition) { + getWordBeforeCursor (line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n/ @@ -472,11 +472,11 @@ export default class CodeEditor extends React.Component { } } - quitEditor() { + quitEditor () { document.querySelector('textarea').blur() } - componentWillUnmount() { + componentWillUnmount () { this.editor.off('focus', this.focusHandler) this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) @@ -489,7 +489,7 @@ export default class CodeEditor extends React.Component { eventEmitter.off('code:format-table', this.formatTable) } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate (prevProps, prevState) { let needRefresh = false const { rulers, @@ -538,7 +538,7 @@ export default class CodeEditor extends React.Component { if (prevProps.matchingPairs !== this.props.matchingPairs || prevProps.matchingTriples !== this.props.matchingTriples || prevProps.explodingPairs !== this.props.explodingPairs) { - const bracketObject ={ + const bracketObject = { pairs: this.props.matchingPairs, triples: this.props.matchingTriples, explode: this.props.explodingPairs, @@ -573,7 +573,7 @@ export default class CodeEditor extends React.Component { } } - setMode(mode) { + setMode (mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -581,16 +581,16 @@ export default class CodeEditor extends React.Component { CodeMirror.autoLoadMode(this.editor, syntax.mode) } - handleChange(e) { + handleChange (e) { this.value = this.editor.getValue() if (this.props.onChange) { this.props.onChange(e) } } - moveCursorTo(row, col) {} + moveCursorTo (row, col) {} - scrollToLine(event, num) { + scrollToLine (event, num) { const cursor = { line: num, ch: 1 @@ -598,15 +598,15 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } - focus() { + focus () { this.editor.focus() } - blur() { + blur () { this.editor.blur() } - reload() { + reload () { // Change event shouldn't be fired when switch note this.editor.off('change', this.changeHandler) this.value = this.props.value @@ -616,13 +616,13 @@ export default class CodeEditor extends React.Component { this.editor.refresh() } - setValue(value) { + setValue (value) { const cursor = this.editor.getCursor() this.editor.setValue(value) this.editor.setCursor(cursor) } - handleDropImage(dropEvent) { + handleDropImage (dropEvent) { dropEvent.preventDefault() const { storageKey, @@ -636,11 +636,11 @@ export default class CodeEditor extends React.Component { ) } - insertAttachmentMd(imageMd) { + insertAttachmentMd (imageMd) { this.editor.replaceSelection(imageMd) } - handlePaste(editor, e) { + handlePaste (editor, e) { const clipboardData = e.clipboardData const { storageKey, @@ -699,13 +699,13 @@ export default class CodeEditor extends React.Component { } } - handleScroll(e) { + handleScroll (e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handlePasteUrl(e, editor, pastedTxt) { + handlePasteUrl (e, editor, pastedTxt) { e.preventDefault() const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) @@ -728,8 +728,8 @@ export default class CodeEditor extends React.Component { } fetch(pastedTxt, { - method: 'get' - }) + method: 'get' + }) .then(response => { if (isImageReponse(response)) { return this.mapImageResponse(response, pastedTxt) @@ -745,13 +745,13 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml(e, editor, pastedHtml) { + handlePasteHtml (e, editor, pastedHtml) { e.preventDefault() const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - mapNormalResponse(response, pastedTxt) { + mapNormalResponse (response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { try { @@ -771,7 +771,7 @@ export default class CodeEditor extends React.Component { }) } - mapImageResponse(response, pastedTxt) { + mapImageResponse (response, pastedTxt) { return new Promise((resolve, reject) => { try { const url = response.url @@ -784,18 +784,18 @@ export default class CodeEditor extends React.Component { }) } - decodeResponse(response) { + decodeResponse (response) { const headers = response.headers - const _charset = headers.has('content-type') ? - this.extractContentTypeCharset(headers.get('content-type')) : - undefined + 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' + iconv.encodingExists(_charset) + ? _charset + : 'utf-8' resolve(iconv.decode(new Buffer(buff), charset).toString()) } catch (e) { reject(e) @@ -804,7 +804,7 @@ export default class CodeEditor extends React.Component { }) } - extractContentTypeCharset(contentType) { + extractContentTypeCharset (contentType) { return contentType .split(';') .filter(str => { @@ -815,27 +815,27 @@ export default class CodeEditor extends React.Component { })[0] } - render() { + render () { const { className, fontSize } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width - return ( < - div className = { + return (< + div className ={ className == null ? 'CodeEditor' : `CodeEditor ${className}` } - ref = 'root' - tabIndex = '-1' - style = { - { - fontFamily, - fontSize: fontSize, - width: width - } + ref= 'root' + tabIndex= '-1' + style= { + { + fontFamily, + fontSize: fontSize, + width: width } - onDrop = { + } + onDrop= { e => this.handleDropImage(e) } /> @@ -862,4 +862,4 @@ CodeEditor.defaultProps = { fontFamily: 'Monaco, Consolas', indentSize: 4, indentType: 'space' -} \ No newline at end of file +} From 8361106660fc78ef4cda5bc55cc04bf5e91c0ae6 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 14:07:35 +0000 Subject: [PATCH 015/159] Removing trailing spaces and added spaces in config --- browser/main/lib/ConfigManager.js | 6 +++--- browser/main/modals/PreferencesModal/UiTab.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 942ce93d..7973d95f 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -44,9 +44,9 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, - matchingPairs:'()[]{}\'\'""$$**``', - matchingTriples:'```"""\'\'\'', - explodingPairs:'[]{}``$$', + matchingPairs: '()[]{}\'\'""$$**``', + matchingTriples: '```"""\'\'\'', + explodingPairs: '[]{}``$$', switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE' scrollPastEnd: false, diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index ac020238..5b68d456 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -62,7 +62,7 @@ class UiTab extends React.Component { checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } - + const newConfig = { ui: { theme: this.refs.uiTheme.value, @@ -542,7 +542,7 @@ class UiTab extends React.Component {
-
+
{i18n.__('Matching character pairs')}
@@ -556,7 +556,7 @@ class UiTab extends React.Component {
-
+
{i18n.__('Matching character triples')}
@@ -570,7 +570,7 @@ class UiTab extends React.Component {
-
+
{i18n.__('Exploding character pairs')}
@@ -611,7 +611,7 @@ class UiTab extends React.Component { />
- +
{i18n.__('Code Block Theme')}
From 707dace3d065aaff5466d4a2f03d469a6e3f92a1 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 14:11:43 +0000 Subject: [PATCH 016/159] removing spaces after = --- browser/components/CodeEditor.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 9037c2e2..98651798 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -823,19 +823,19 @@ export default class CodeEditor extends React.Component { const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width return (< - div className ={ + div className={ className == null ? 'CodeEditor' : `CodeEditor ${className}` } - ref= 'root' - tabIndex= '-1' - style= { + ref='root' + tabIndex='-1' + style={ { fontFamily, fontSize: fontSize, width: width } } - onDrop= { + onDrop={ e => this.handleDropImage(e) } /> From b1d2c25ce50a3bf861f28e342f1b9970ea06c1c9 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 10 Nov 2018 00:05:45 +0100 Subject: [PATCH 017/159] when dropping an image, switch to editor and add it at the end of the file --- browser/components/MarkdownEditor.js | 24 ++++++++++++++++++++++++ browser/components/MarkdownPreview.js | 12 ++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 9c8a06d6..dbec1dc9 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -7,6 +7,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview' import eventEmitter from 'browser/main/lib/eventEmitter' import { findStorage } from 'browser/lib/findStorage' import ConfigManager from 'browser/main/lib/ConfigManager' +import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' class MarkdownEditor extends React.Component { constructor (props) { @@ -219,6 +220,28 @@ class MarkdownEditor extends React.Component { this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`) } + handleDropImage (dropEvent) { + dropEvent.preventDefault() + const { storageKey, noteKey } = this.props + + this.setState({ + status: 'CODE' + }, () => { + this.refs.code.focus() + + this.refs.code.editor.execCommand('goDocEnd') + this.refs.code.editor.execCommand('goLineEnd') + this.refs.code.editor.execCommand('newlineAndIndent') + + attachmentManagement.handleAttachmentDrop( + this.refs.code, + storageKey, + noteKey, + dropEvent + ) + }) + } + handleKeyUp (e) { const keyPressed = this.state.keyPressed keyPressed.delete(e.keyCode) @@ -308,6 +331,7 @@ class MarkdownEditor extends React.Component { customCSS={config.preview.customCSS} allowCustomCSS={config.preview.allowCustomCSS} lineThroughCheckbox={config.preview.lineThroughCheckbox} + onDrop={(e) => this.handleDropImage(e)} />
) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index d9ff7074..704ed9a0 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -206,7 +206,7 @@ export default class MarkdownPreview extends React.Component { this.saveAsHtmlHandler = () => this.handleSaveAsHtml() this.printHandler = () => this.handlePrint() - this.linkClickHandler = this.handlelinkClick.bind(this) + this.linkClickHandler = this.handleLinkClick.bind(this) this.initMarkdown = this.initMarkdown.bind(this) this.initMarkdown() } @@ -438,6 +438,8 @@ export default class MarkdownPreview extends React.Component { } componentDidMount () { + const { onDrop } = this.props + this.refs.root.setAttribute('sandbox', 'allow-scripts') this.refs.root.contentWindow.document.body.addEventListener( 'contextmenu', @@ -475,7 +477,7 @@ export default class MarkdownPreview extends React.Component { ) this.refs.root.contentWindow.document.addEventListener( 'drop', - this.preventImageDroppedHandler + onDrop || this.preventImageDroppedHandler ) this.refs.root.contentWindow.document.addEventListener( 'dragover', @@ -492,6 +494,8 @@ export default class MarkdownPreview extends React.Component { } componentWillUnmount () { + const { onDrop } = this.props + this.refs.root.contentWindow.document.body.removeEventListener( 'contextmenu', this.contextMenuHandler @@ -510,7 +514,7 @@ export default class MarkdownPreview extends React.Component { ) this.refs.root.contentWindow.document.removeEventListener( 'drop', - this.preventImageDroppedHandler + onDrop || this.preventImageDroppedHandler ) this.refs.root.contentWindow.document.removeEventListener( 'dragover', @@ -837,7 +841,7 @@ export default class MarkdownPreview extends React.Component { return new window.Notification(title, options) } - handlelinkClick (e) { + handleLinkClick (e) { e.preventDefault() e.stopPropagation() From 3679fbe3ea36b4331e1f22559728ecb4a84863ad Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 10 Nov 2018 00:18:06 +0100 Subject: [PATCH 018/159] fix lint errors --- browser/components/MarkdownEditor.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index dbec1dc9..07d2aaf0 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -225,21 +225,21 @@ class MarkdownEditor extends React.Component { const { storageKey, noteKey } = this.props this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() + status: 'CODE' + }, () => { + this.refs.code.focus() - this.refs.code.editor.execCommand('goDocEnd') - this.refs.code.editor.execCommand('goLineEnd') - this.refs.code.editor.execCommand('newlineAndIndent') + this.refs.code.editor.execCommand('goDocEnd') + this.refs.code.editor.execCommand('goLineEnd') + this.refs.code.editor.execCommand('newlineAndIndent') - attachmentManagement.handleAttachmentDrop( - this.refs.code, - storageKey, - noteKey, - dropEvent - ) - }) + attachmentManagement.handleAttachmentDrop( + this.refs.code, + storageKey, + noteKey, + dropEvent + ) + }) } handleKeyUp (e) { From 01891d46b3cba3fec7f3794542c405262e5bdd6c Mon Sep 17 00:00:00 2001 From: richardtks Date: Sun, 11 Nov 2018 00:15:22 +0800 Subject: [PATCH 019/159] Added the auto-updating numbered list to the middle of a list --- .../boostNewLineIndentContinueMarkdownList.js | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js b/extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js index aa766f6e..bc03e920 100644 --- a/extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js +++ b/extra_scripts/boost/boostNewLineIndentContinueMarkdownList.js @@ -44,9 +44,46 @@ ? match[2].replace('x', ' ') : (parseInt(match[3], 10) + 1) + match[4] replacements[i] = '\n' + indent + bullet + after + + if (bullet) incrementRemainingMarkdownListNumbers(cm, pos) } } - cm.replaceSelections(replacements) } + // Auto-updating Markdown list numbers when a new item is added to the + // middle of a list + function incrementRemainingMarkdownListNumbers(cm, pos) { + var startLine = pos.line, lookAhead = 0, skipCount = 0 + var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1] + + do { + lookAhead += 1 + var nextLineNumber = startLine + lookAhead + var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine) + + if (nextItem) { + var nextIndent = nextItem[1] + var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount) + var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber + + if (startIndent === nextIndent && !isNaN(nextNumber)) { + if (newNumber === nextNumber) itemNumber = nextNumber + 1 + if (newNumber > nextNumber) itemNumber = newNumber + 1 + cm.replaceRange( + nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]), + { + line: nextLineNumber, ch: 0 + }, { + line: nextLineNumber, ch: nextLine.length + }) + } else { + if (startIndent.length > nextIndent.length) return + // This doesn't run if the next line immediatley indents, as it is + // not clear of the users intention (new indented item or same level) + if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return + skipCount += 1 + } + } + } while (nextItem) + } }) From 3414e2daf0f1e50ff580049134c9595886709484 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Thu, 22 Nov 2018 12:22:49 +0000 Subject: [PATCH 020/159] Added warning when trying to print from InfoPanel --- browser/main/Detail/SnippetNoteDetail.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index afd81102..db862115 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -789,6 +789,7 @@ class SnippetNoteDetail extends React.Component { exportAsMd={this.showWarning} exportAsTxt={this.showWarning} type={note.type} + print={this.showWarning} />
From bf288fdaeb4dc64d4390edf9d35f38c3a38f624a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 14:55:29 +0100 Subject: [PATCH 021/159] fix pasting into fenced code block --- browser/components/CodeEditor.js | 84 +++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 130cc86e..0193d5cb 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -562,14 +562,17 @@ export default class CodeEditor extends React.Component { } handlePaste (editor, e) { + const { storageKey, noteKey, fetchUrlTitle } = this.props + const clipboardData = e.clipboardData - const { storageKey, noteKey } = this.props const dataTransferItem = clipboardData.items[0] const pastedTxt = clipboardData.getData('text') + const isURL = str => { const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ return matcher.test(str) } + const isInLinkTag = editor => { const startCursor = editor.getCursor('start') const prevChar = editor.getRange( @@ -584,30 +587,69 @@ export default class CodeEditor extends React.Component { return prevChar === '](' && nextChar === ')' } - const pastedHtml = clipboardData.getData('text/html') - if (pastedHtml !== '') { - this.handlePasteHtml(e, editor, pastedHtml) - } else if (dataTransferItem.type.match('image')) { - attachmentManagement.handlePastImageEvent( - this, - storageKey, - noteKey, - dataTransferItem - ) - } else if ( - this.props.fetchUrlTitle && - isURL(pastedTxt) && - !isInLinkTag(editor) - ) { - this.handlePasteUrl(e, editor, pastedTxt) + const isInFencedCodeBlock = editor => { + const cursor = editor.getCursor() + + let token = editor.getTokenAt(cursor) + if (token.state.fencedState) { + return true + } + + let line = line = cursor.line - 1 + while (line >= 0) { + token = editor.getTokenAt({ + ch: 3, + line + }) + + if (token.start === token.end) { + --line + } else if (token.type === 'comment') { + if (line > 0) { + token = editor.getTokenAt({ + ch: 3, + line: line - 1 + }) + + return token.type !== 'comment' + } else { + return true + } + return true + } else { + return false + } + } + + return false } + + if (isInFencedCodeBlock(editor)) { + this.handlePasteText(e, editor, pastedTxt) + } else { + const pastedHtml = clipboardData.getData('text/html') + if (pastedHtml !== '') { + this.handlePasteHtml(e, editor, pastedHtml) + } else if (dataTransferItem.type.match('image')) { + attachmentManagement.handlePastImageEvent( + this, + storageKey, + noteKey, + dataTransferItem + ) + } else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { + this.handlePasteUrl(e, editor, pastedTxt) + } + } + if (attachmentManagement.isAttachmentLink(pastedTxt)) { + e.preventDefault() + attachmentManagement .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) .then(modifiedText => { this.editor.replaceSelection(modifiedText) }) - e.preventDefault() } } @@ -663,6 +705,12 @@ export default class CodeEditor extends React.Component { editor.replaceSelection(markdown) } + handlePasteText (e, editor, pastedTxt) { + e.preventDefault() + + editor.replaceSelection(pastedTxt) + } + mapNormalResponse (response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { From aac075be061b438e269b8048f5189d8b87bde6d8 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 14:57:03 +0100 Subject: [PATCH 022/159] fix lint error --- browser/components/CodeEditor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 0193d5cb..d2fcb9c5 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -615,7 +615,6 @@ export default class CodeEditor extends React.Component { } else { return true } - return true } else { return false } From 9d81e4be2fe810c18003a99425ac1ed42ef2f665 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 16:17:01 +0100 Subject: [PATCH 023/159] undo change --- browser/main/lib/dataApi/attachmentManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index c88acfc5..59c91540 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -227,7 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ function fixLocalURLS (renderedHTML, storagePath) { - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:\\\/|%5C)[\\w.]+', 'g'), function (match) { + return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER)) }) From 64f7233bfc8124fc6d89e9c212718f3cdd4d02cb Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 16:58:11 +0100 Subject: [PATCH 024/159] fix regex to match :storage reference, added comment to explain what it does. --- browser/main/lib/dataApi/attachmentManagement.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 59c91540..73bf1303 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -227,7 +227,15 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ function fixLocalURLS (renderedHTML, storagePath) { - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?', 'g'), function (match) { + /* + A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`. + + - `STORAGE_FOLDER_PLACEHOLDER` will match `:storage` + - `(?:(?:\\\/|%5C)[\\w.]+)+` will match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg` + - `(?:\\\/|%5C)[\\w.]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg` + - `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows. + */ + return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[\\w.]+)+', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER)) }) From d95d282f393fe64ccfe0467cc6a2c8d5e6cb2fdd Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 17:09:54 +0100 Subject: [PATCH 025/159] disable editor's context menu when switch preview is using right click --- browser/components/CodeEditor.js | 18 ++++++++++++------ browser/components/MarkdownEditor.js | 1 + browser/components/MarkdownSplitEditor.js | 1 + 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 130cc86e..260b12f8 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -65,12 +65,16 @@ export default class CodeEditor extends React.Component { this.scrollToLineHandeler = this.scrollToLine.bind(this) this.formatTable = () => this.handleFormatTable() - this.contextMenuHandler = function (editor, event) { - const menu = buildEditorContextMenu(editor, event) - if (menu != null) { - setTimeout(() => menu.popup(remote.getCurrentWindow()), 30) + + if (props.switchPreview !== 'RIGHTCLICK') { + this.contextMenuHandler = function (editor, event) { + const menu = buildEditorContextMenu(editor, event) + if (menu != null) { + setTimeout(() => menu.popup(remote.getCurrentWindow()), 30) + } } } + this.editorActivityHandler = () => this.handleEditorActivity() this.turndownService = new TurndownService() @@ -137,7 +141,7 @@ export default class CodeEditor extends React.Component { } componentDidMount () { - const { rulers, enableRulers } = this.props + const { rulers, enableRulers, switchPreview } = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -241,7 +245,9 @@ export default class CodeEditor extends React.Component { this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) this.editor.on('paste', this.pasteHandler) - this.editor.on('contextmenu', this.contextMenuHandler) + if (switchPreview !== 'RIGHTCLICK') { + this.editor.on('contextmenu', this.contextMenuHandler) + } eventEmitter.on('top:search', this.searchHandler) eventEmitter.emit('code:init') diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 20ce9451..0c2e291e 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -278,6 +278,7 @@ class MarkdownEditor extends React.Component { onChange={(e) => this.handleChange(e)} onBlur={(e) => this.handleBlur(e)} spellCheck={config.editor.spellcheck} + switchPreview={config.editor.switchPreview} />
this.handleMouseDown(e)} >
From 629d4a82ae8ee4f6067eaf1b829d67aaf09b6f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Vi=E1=BB=87t=20H=C6=B0ng?= Date: Tue, 27 Nov 2018 13:58:06 +0700 Subject: [PATCH 026/159] fixed delete note not navigate to next note --- browser/main/NoteList/index.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 7bb52ccd..ba2c801f 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -652,14 +652,18 @@ class NoteList extends React.Component { }) ) .then((data) => { - data.forEach((item) => { - dispatch({ - type: 'DELETE_NOTE', - storageKey: item.storageKey, - noteKey: item.noteKey + const dispatchHandler = () => { + data.forEach((item) => { + dispatch({ + type: 'DELETE_NOTE', + storageKey: item.storageKey, + noteKey: item.noteKey + }) }) - }) + } + ee.once('list:next', dispatchHandler) }) + .then(() => ee.emit('list:next')) .catch((err) => { console.error('Cannot Delete note: ' + err) }) @@ -683,6 +687,7 @@ class NoteList extends React.Component { }) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE') }) + .then(() => ee.emit('list:next')) .catch((err) => { console.error('Notes could not go to trash: ' + err) }) From c33058ae2bf324fc9cd1c4ec033a4ac850a958e2 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 27 Nov 2018 17:42:04 +0000 Subject: [PATCH 027/159] Added custom warning messages. --- browser/main/Detail/InfoPanel.js | 8 ++++---- browser/main/Detail/InfoPanelTrashed.js | 6 +++--- browser/main/Detail/SnippetNoteDetail.js | 12 ++++++++++-- browser/main/NoteList/index.js | 17 ++++++++++++----- lib/main-menu.js | 8 ++++---- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/browser/main/Detail/InfoPanel.js b/browser/main/Detail/InfoPanel.js index 4ce610fa..15535186 100644 --- a/browser/main/Detail/InfoPanel.js +++ b/browser/main/Detail/InfoPanel.js @@ -70,22 +70,22 @@ class InfoPanel extends React.Component {
- - - - diff --git a/browser/main/Detail/InfoPanelTrashed.js b/browser/main/Detail/InfoPanelTrashed.js index db64a284..d4c8045d 100644 --- a/browser/main/Detail/InfoPanelTrashed.js +++ b/browser/main/Detail/InfoPanelTrashed.js @@ -31,17 +31,17 @@ const InfoPanelTrashed = ({
- - - diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index db862115..08cd50f1 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -633,11 +633,18 @@ class SnippetNoteDetail extends React.Component { if (infoPanel.style) infoPanel.style.display = infoPanel.style.display === 'none' ? 'inline' : 'none' } - showWarning () { + showWarning (e, msg) { + const warningMessage = (msg) => ({ + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' + })[msg] + dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__('md/text import is available only a markdown note.'), + detail: i18n.__(warningMessage(msg)), buttons: [i18n.__('OK')] }) } @@ -788,6 +795,7 @@ class SnippetNoteDetail extends React.Component { createdAt={formatDate(note.createdAt)} exportAsMd={this.showWarning} exportAsTxt={this.showWarning} + exportAsHtml={this.showWarning} type={note.type} print={this.showWarning} /> diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 7bb52ccd..02bdbcd2 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -64,8 +64,8 @@ class NoteList extends React.Component { this.focusHandler = () => { this.refs.list.focus() } - this.alertIfSnippetHandler = () => { - this.alertIfSnippet() + this.alertIfSnippetHandler = (event, msg) => { + this.alertIfSnippet(msg) } this.importFromFileHandler = this.importFromFile.bind(this) this.jumpNoteByHash = this.jumpNoteByHashHandler.bind(this) @@ -494,14 +494,21 @@ class NoteList extends React.Component { }) } - alertIfSnippet () { + alertIfSnippet (msg) { + const warningMessage = (msg) => ({ + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' + })[msg] + const targetIndex = this.getTargetIndex() if (this.notes[targetIndex].type === 'SNIPPET_NOTE') { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__('md/text import is available only a markdown note.'), - buttons: [i18n.__('OK'), i18n.__('Cancel')] + detail: i18n.__(warningMessage(msg)), + buttons: [i18n.__('OK')] }) } } diff --git a/lib/main-menu.js b/lib/main-menu.js index fed5eb15..2782ea8b 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -99,21 +99,21 @@ const file = { { label: 'Plain Text (.txt)', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'export-txt') mainWindow.webContents.send('export:save-text') } }, { label: 'MarkDown (.md)', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'export-md') mainWindow.webContents.send('export:save-md') } }, { label: 'HTML (.html)', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'export-html') mainWindow.webContents.send('export:save-html') } } @@ -159,7 +159,7 @@ const file = { label: 'Print', accelerator: 'CommandOrControl+P', click () { - mainWindow.webContents.send('list:isMarkdownNote') + mainWindow.webContents.send('list:isMarkdownNote', 'print') mainWindow.webContents.send('print') } }, From ef66e71febf98dbfcf827926e99e1105f5b4a891 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 27 Nov 2018 18:02:59 +0000 Subject: [PATCH 028/159] Solved some errors in identation --- browser/main/Detail/SnippetNoteDetail.js | 10 +++++----- browser/main/NoteList/index.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 08cd50f1..473ce9d0 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -635,12 +635,12 @@ class SnippetNoteDetail extends React.Component { showWarning (e, msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' })[msg] - + dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 02bdbcd2..49d063ca 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -496,11 +496,11 @@ class NoteList extends React.Component { alertIfSnippet (msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' - })[msg] + 'export-txt': 'Text export is available only as a markdown note', + 'export-md': 'Markdown export is available only as a markdown note.', + 'export-html': 'HTML export is available only as a markdown note.', + 'print': 'Print is available only as a markdown note.' + })[msg] const targetIndex = this.getTargetIndex() if (this.notes[targetIndex].type === 'SNIPPET_NOTE') { From c245855bbf996fbd23b63d8e0ff6b10af2826da6 Mon Sep 17 00:00:00 2001 From: Miguel Teixeira Date: Tue, 27 Nov 2018 20:28:57 +0000 Subject: [PATCH 029/159] Improved messages. --- browser/main/Detail/SnippetNoteDetail.js | 10 +++++----- browser/main/NoteList/index.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 473ce9d0..47e4de98 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -635,16 +635,16 @@ class SnippetNoteDetail extends React.Component { showWarning (e, msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' + 'export-txt': 'Text export', + 'export-md': 'Markdown export', + 'export-html': 'HTML export', + 'print': 'Print' })[msg] dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__(warningMessage(msg)), + detail: i18n.__(warningMessage(msg) + ' is available only as a markdown note.'), buttons: [i18n.__('OK')] }) } diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 49d063ca..51e7a41c 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -496,10 +496,10 @@ class NoteList extends React.Component { alertIfSnippet (msg) { const warningMessage = (msg) => ({ - 'export-txt': 'Text export is available only as a markdown note', - 'export-md': 'Markdown export is available only as a markdown note.', - 'export-html': 'HTML export is available only as a markdown note.', - 'print': 'Print is available only as a markdown note.' + 'export-txt': 'Text export', + 'export-md': 'Markdown export', + 'export-html': 'HTML export', + 'print': 'Print' })[msg] const targetIndex = this.getTargetIndex() @@ -507,7 +507,7 @@ class NoteList extends React.Component { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Sorry!'), - detail: i18n.__(warningMessage(msg)), + detail: i18n.__(warningMessage(msg) + ' is available only as a markdown note.'), buttons: [i18n.__('OK')] }) } From 9050035c7405236ea5a93384d58758f26b0d5633 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Wed, 28 Nov 2018 00:44:15 +0100 Subject: [PATCH 030/159] - add option to enable/disable smart paste - add shortcut to paste smartly - use electron's clipboard --- browser/components/CodeEditor.js | 150 ++++++++++-------- browser/components/MarkdownEditor.js | 2 + browser/components/MarkdownSplitEditor.js | 2 + browser/main/Detail/SnippetNoteDetail.js | 2 + browser/main/lib/ConfigManager.js | 6 +- .../main/lib/dataApi/attachmentManagement.js | 39 +++++ .../main/modals/PreferencesModal/HotkeyTab.js | 14 +- browser/main/modals/PreferencesModal/UiTab.js | 15 +- 8 files changed, 163 insertions(+), 67 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index d2fcb9c5..c7b6e385 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -13,7 +13,7 @@ import crypto from 'crypto' import consts from 'browser/lib/consts' import styles from '../components/CodeEditor.styl' import fs from 'fs' -const { ipcRenderer, remote } = require('electron') +const { ipcRenderer, remote, clipboard } = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' const spellcheck = require('browser/lib/spellcheck') const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') @@ -25,6 +25,10 @@ CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => (enableRulers ? rulers.map(ruler => ({ column: ruler })) : []) +function translateHotkey (hotkey) { + return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') +} + export default class CodeEditor extends React.Component { constructor (props) { super(props) @@ -56,7 +60,11 @@ export default class CodeEditor extends React.Component { noteKey ) } - this.pasteHandler = (editor, e) => this.handlePaste(editor, e) + this.pasteHandler = (editor, e) => { + e.preventDefault() + + this.handlePaste(editor, false) + } this.loadStyleHandler = e => { this.editor.refresh() } @@ -120,42 +128,8 @@ export default class CodeEditor extends React.Component { } } - updateTableEditorState () { - const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) - if (active) { - if (this.extraKeysMode !== 'editor') { - this.extraKeysMode = 'editor' - this.editor.setOption('extraKeys', this.editorKeyMap) - } - } else { - if (this.extraKeysMode !== 'default') { - this.extraKeysMode = 'default' - this.editor.setOption('extraKeys', this.defaultKeyMap) - this.tableEditor.resetSmartCursor() - } - } - } - - componentDidMount () { - const { rulers, enableRulers } = this.props - const expandSnippet = this.expandSnippet.bind(this) - eventEmitter.on('line:jump', this.scrollToLineHandeler) - - const defaultSnippet = [ - { - id: crypto.randomBytes(16).toString('hex'), - name: 'Dummy text', - prefix: ['lorem', 'ipsum'], - content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - } - ] - if (!fs.existsSync(consts.SNIPPET_FILE)) { - fs.writeFileSync( - consts.SNIPPET_FILE, - JSON.stringify(defaultSnippet, null, 4), - 'utf8' - ) - } + updateDefaultKeyMap () { + const { hotkey } = this.props this.defaultKeyMap = CodeMirror.normalizeKeyMap({ Tab: function (cm) { @@ -207,8 +181,51 @@ export default class CodeEditor extends React.Component { document.execCommand('copy') } return CodeMirror.Pass + }, + [translateHotkey(hotkey.pasteSmartly)]: cm => { + this.handlePaste(cm, true) } }) + } + + updateTableEditorState () { + const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) + if (active) { + if (this.extraKeysMode !== 'editor') { + this.extraKeysMode = 'editor' + this.editor.setOption('extraKeys', this.editorKeyMap) + } + } else { + if (this.extraKeysMode !== 'default') { + this.extraKeysMode = 'default' + this.editor.setOption('extraKeys', this.defaultKeyMap) + this.tableEditor.resetSmartCursor() + } + } + } + + componentDidMount () { + const { rulers, enableRulers } = this.props + const expandSnippet = this.expandSnippet.bind(this) + eventEmitter.on('line:jump', this.scrollToLineHandeler) + + const defaultSnippet = [ + { + id: crypto.randomBytes(16).toString('hex'), + name: 'Dummy text', + prefix: ['lorem', 'ipsum'], + content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + } + ] + if (!fs.existsSync(consts.SNIPPET_FILE)) { + fs.writeFileSync( + consts.SNIPPET_FILE, + JSON.stringify(defaultSnippet, null, 4), + 'utf8' + ) + } + + this.updateDefaultKeyMap() this.value = this.props.value this.editor = CodeMirror(this.refs.root, { @@ -473,6 +490,14 @@ export default class CodeEditor extends React.Component { this.editor.setOption('extraKeys', this.defaultKeyMap) } + if (prevProps.hotkey !== this.props.hotkey) { + this.updateDefaultKeyMap() + + if (this.extraKeysMode === 'default') { + this.editor.setOption('extraKeys', this.defaultKeyMap) + } + } + if (this.state.clientWidth !== this.refs.root.clientWidth) { this.setState({ clientWidth: this.refs.root.clientWidth @@ -561,12 +586,8 @@ export default class CodeEditor extends React.Component { this.editor.replaceSelection(imageMd) } - handlePaste (editor, e) { - const { storageKey, noteKey, fetchUrlTitle } = this.props - - const clipboardData = e.clipboardData - const dataTransferItem = clipboardData.items[0] - const pastedTxt = clipboardData.getData('text') + handlePaste (editor, forceSmartPaste) { + const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props const isURL = str => { const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ @@ -623,27 +644,34 @@ export default class CodeEditor extends React.Component { return false } + const pastedTxt = clipboard.readText() + if (isInFencedCodeBlock(editor)) { - this.handlePasteText(e, editor, pastedTxt) - } else { - const pastedHtml = clipboardData.getData('text/html') - if (pastedHtml !== '') { - this.handlePasteHtml(e, editor, pastedHtml) - } else if (dataTransferItem.type.match('image')) { - attachmentManagement.handlePastImageEvent( + this.handlePasteText(editor, pastedTxt) + } else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { + this.handlePasteUrl(editor, pastedTxt) + } else if (enableSmartPaste || forceSmartPaste) { + const image = clipboard.readImage() + if (!image.isEmpty()) { + attachmentManagement.handlePastNativeImage( this, storageKey, noteKey, - dataTransferItem + image ) - } else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { - this.handlePasteUrl(e, editor, pastedTxt) + } else { + const pastedHtml = clipboard.readHTML() + if (pastedHtml.length > 0) { + this.handlePasteHtml(editor, pastedHtml) + } else { + this.handlePasteText(editor, pastedTxt) + } } + } else { + this.handlePasteText(editor, pastedTxt) } if (attachmentManagement.isAttachmentLink(pastedTxt)) { - e.preventDefault() - attachmentManagement .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) .then(modifiedText => { @@ -658,8 +686,7 @@ export default class CodeEditor extends React.Component { } } - handlePasteUrl (e, editor, pastedTxt) { - e.preventDefault() + handlePasteUrl (editor, pastedTxt) { const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) @@ -698,15 +725,12 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml (e, editor, pastedHtml) { - e.preventDefault() + handlePasteHtml (editor, pastedHtml) { const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - handlePasteText (e, editor, pastedTxt) { - e.preventDefault() - + handlePasteText (editor, pastedTxt) { editor.replaceSelection(pastedTxt) } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 20ce9451..f1aa8d73 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -278,6 +278,8 @@ class MarkdownEditor extends React.Component { onChange={(e) => this.handleChange(e)} onBlur={(e) => this.handleBlur(e)} spellCheck={config.editor.spellcheck} + enableSmartPaste={config.editor.enableSmartPaste} + hotkey={config.hotkey} />
this.handleMouseDown(e)} >
diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 4a38ffe5..c839fb94 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -717,6 +717,8 @@ class SnippetNoteDetail extends React.Component { enableTableEditor={config.editor.enableTableEditor} onChange={(e) => this.handleCodeChange(index)(e)} ref={'code-' + index} + enableSmartPaste={config.editor.enableSmartPaste} + hotkey={config.hotkey} /> }
diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index d6b04d9b..c2ff9f7a 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -25,7 +25,8 @@ export const DEFAULT_CONFIG = { hotkey: { toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E', toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M', - deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace' + deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace', + pasteSmartly: OSX ? 'Command + Shift + V' : 'Ctrl + Shift + V' }, ui: { language: 'en', @@ -52,7 +53,8 @@ export const DEFAULT_CONFIG = { enableTableEditor: false, enableFrontMatterTitle: true, frontMatterTitleField: 'title', - spellcheck: false + spellcheck: false, + enableSmartPaste: false }, preview: { fontSize: '14', diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index c193eaf2..373efddc 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -316,6 +316,44 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem reader.readAsDataURL(blob) } +/** + * @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 {NativeImage} image The native image + */ +function handlePastNativeImage (codeEditor, storageKey, noteKey, image) { + 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 (!image) { + throw new Error('image has to be given') + } + + 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) + + const binaryData = image.toPNG() + fs.writeFileSync(imagePath, binaryData, 'binary') + + const imageReferencePath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, imageName) + const imageMd = generateAttachmentMarkdown(imageName, imageReferencePath, true) + codeEditor.insertAttachmentMd(imageMd) +} + /** * @description Returns all attachment paths of the given markdown * @param {String} markdownContent content in which the attachment paths should be found @@ -539,6 +577,7 @@ module.exports = { generateAttachmentMarkdown, handleAttachmentDrop, handlePastImageEvent, + handlePastNativeImage, getAttachmentsInMarkdownContent, getAbsolutePathsOfAttachmentsInContent, removeStorageAndNoteReferences, diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 7ad6f606..a0f6a739 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -79,7 +79,8 @@ class HotkeyTab extends React.Component { config.hotkey = { toggleMain: this.refs.toggleMain.value, toggleMode: this.refs.toggleMode.value, - deleteNote: this.refs.deleteNote.value + deleteNote: this.refs.deleteNote.value, + pasteSmartly: this.refs.pasteSmartly.value } this.setState({ config @@ -149,6 +150,17 @@ class HotkeyTab extends React.Component { />
+
+
{i18n.__('Paste Smartly')}
+
+ this.handleHotkeyChange(e)} + ref='pasteSmartly' + value={config.hotkey.pasteSmartly} + type='text' + /> +
+
+ +
+ +
+
+
+
{i18n.__('Show/Hide Menu Bar')}
+
+ this.handleHotkeyChange(e)} + ref='toggleMenuBar' + value={config.hotkey.toggleMenuBar} + type='text' + /> +
+
{i18n.__('Toggle Editor Mode')}
diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index a371cac8..263977ab 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -75,6 +75,7 @@ class UiTab extends React.Component { showTagsAlphabetically: this.refs.showTagsAlphabetically.checked, saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked, enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked, + showMenuBar: this.refs.showMenuBar.checked, disableDirectWrite: this.refs.uiD2w != null ? this.refs.uiD2w.checked : false @@ -238,6 +239,16 @@ class UiTab extends React.Component {
+
+ +
diff --git a/browser/main/Detail/TagSelect.js b/browser/main/Detail/TagSelect.js index d4411af9..caf0cf39 100644 --- a/browser/main/Detail/TagSelect.js +++ b/browser/main/Detail/TagSelect.js @@ -179,14 +179,14 @@ class TagSelect extends React.Component { } render () { - const { value, className, showTagsAlphabetically, tagConfig } = this.props + const { value, className, showTagsAlphabetically, coloredTags } = this.props const tagList = _.isArray(value) ? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => { return ( this.handleTagLabelClick(tag)}>#{tag} diff --git a/browser/components/ColorPicker.styl b/browser/components/ColorPicker.styl index 927a0da2..6e4aceb8 100644 --- a/browser/components/ColorPicker.styl +++ b/browser/components/ColorPicker.styl @@ -1,25 +1,37 @@ .colorPicker position fixed - z-index 10000 + z-index 2 display flex flex-direction column + +.cover + position fixed + top 0 + right 0 + bottom 0 + left 0 + .footer display flex justify-content center + z-index 2 align-items center - padding 5px & > button + button margin-left 10px .btn-cancel, .btn-confirm, .btn-reset - height 1.6em - border 1px solid #888888 - background-color #fff - font-size 15px + vertical-align middle + height 25px + margin-top 2.5px + colorDefaultButton() border-radius 2px + border $ui-border + padding 0 5px .btn-confirm background-color #1EC38B + &:hover + background-color darken(#1EC38B, 25%) diff --git a/browser/components/TagListItem.js b/browser/components/TagListItem.js index 64a74d63..9aa00a1d 100644 --- a/browser/components/TagListItem.js +++ b/browser/components/TagListItem.js @@ -10,8 +10,8 @@ import CSSModules from 'browser/lib/CSSModules' * @param {string} name * @param {Function} handleClickTagListItem * @param {Function} handleClickNarrowToTag -* @param {bool} isActive -* @param {bool} isRelated +* @param {boolean} isActive +* @param {boolean} isRelated * @param {string} bgColor tab backgroundColor */ @@ -24,7 +24,8 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand :
} ) diff --git a/resources/icon/icon-x-light.svg b/resources/icon/icon-x-light.svg new file mode 100644 index 00000000..39828f0b --- /dev/null +++ b/resources/icon/icon-x-light.svg @@ -0,0 +1,17 @@ + + + + x + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file From 071f7cb035c522ea5451362b24d5573a227f5bed Mon Sep 17 00:00:00 2001 From: tool root Date: Sun, 30 Dec 2018 16:20:19 +0900 Subject: [PATCH 107/159] rewrote the function code to MarkdownPreview.js #2644 #2662 --- browser/components/MarkdownPreview.js | 27 ++++++++++++++++++- .../main/lib/dataApi/attachmentManagement.js | 27 ------------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 9f190773..4c638dc9 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -398,6 +398,31 @@ export default class MarkdownPreview extends React.Component { } } + /** + * @description Convert special characters between three ``` + * @param {string[]} splitWithCodeTag Array of HTML strings separated by three ``` + * @returns {string} HTML in which special characters between three ``` have been converted + */ + escapeHtmlCharactersInCodeTag (splitWithCodeTag) { + for (let index = 0; index < splitWithCodeTag.length; index++) { + const codeTagRequired = (splitWithCodeTag[index] !== '\`\`\`' && index < splitWithCodeTag.length - 1) + if (codeTagRequired) { + splitWithCodeTag.splice((index + 1), 0, '\`\`\`') + } + } + let inCodeTag = false + let result = '' + for (let content of splitWithCodeTag) { + if (content === '\`\`\`') { + inCodeTag = !inCodeTag + } else if (inCodeTag) { + content = escapeHtmlCharacters(content) + } + result += content + } + return result + } + getScrollBarStyle () { const { theme } = this.props @@ -640,7 +665,7 @@ export default class MarkdownPreview extends React.Component { this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) if (sanitize === 'NONE') { const splitWithCodeTag = value.split('```') - value = attachmentManagement.escapeHtmlCharactersInCodeTag(splitWithCodeTag) + value = this.escapeHtmlCharactersInCodeTag(splitWithCodeTag) } const renderedHTML = this.markdown.render(value) attachmentManagement.migrateAttachments(value, storagePath, noteKey) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index d4cebded..6a0315f7 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -7,7 +7,6 @@ const fse = require('fs-extra') const escapeStringRegexp = require('escape-string-regexp') const sander = require('sander') import i18n from 'browser/lib/i18n' -import { escapeHtmlCharacters } from '../../../lib/utils' const STORAGE_FOLDER_PLACEHOLDER = ':storage' const DESTINATION_FOLDER = 'attachments' @@ -221,31 +220,6 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { } } -/** - * @description Convert special characters between ``` - * @param {string[]} splitWithCodeTag Array of HTML strings separated by ``` - * @returns {string} HTML in which special characters between ``` have been converted - */ -function escapeHtmlCharactersInCodeTag (splitWithCodeTag) { - for (let index = 0; index < splitWithCodeTag.length; index++) { - const codeTagRequired = (splitWithCodeTag[index] !== '\`\`\`' && index < splitWithCodeTag.length - 1) - if (codeTagRequired) { - splitWithCodeTag.splice((index + 1), 0, '\`\`\`') - } - } - let inCodeTag = false - let result = '' - for (let content of splitWithCodeTag) { - if (content === '\`\`\`') { - inCodeTag = !inCodeTag - } else if (inCodeTag) { - content = escapeHtmlCharacters(content) - } - result += content - } - return result -} - /** * @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 @@ -607,7 +581,6 @@ function handleAttachmentLinkPaste (storageKey, noteKey, linkText) { module.exports = { copyAttachment, - escapeHtmlCharactersInCodeTag, fixLocalURLS, generateAttachmentMarkdown, handleAttachmentDrop, From a9feddf6f69fdde9b0f5eb450678b5e4a017354c Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Sun, 30 Dec 2018 19:23:43 +0800 Subject: [PATCH 108/159] fix bug missing param colored tags in sorted list --- browser/components/NoteItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/NoteItem.js b/browser/components/NoteItem.js index 5e8d009f..625bb38d 100644 --- a/browser/components/NoteItem.js +++ b/browser/components/NoteItem.js @@ -43,7 +43,7 @@ const TagElementList = (tags, showTagsAlphabetically, coloredTags) => { } if (showTagsAlphabetically) { - return _.sortBy(tags).map(tag => TagElement({ tagName: tag })) + return _.sortBy(tags).map(tag => TagElement({ tagName: tag, color: coloredTags[tag] })) } else { return tags.map(tag => TagElement({ tagName: tag, color: coloredTags[tag] })) } From b4e4d7055fbefe44c0321220144013fdd6d54419 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Sun, 30 Dec 2018 20:49:26 +0800 Subject: [PATCH 109/159] improve style of color-picker --- browser/components/ColorPicker.styl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/components/ColorPicker.styl b/browser/components/ColorPicker.styl index 6e4aceb8..fbc1212a 100644 --- a/browser/components/ColorPicker.styl +++ b/browser/components/ColorPicker.styl @@ -25,10 +25,12 @@ vertical-align middle height 25px margin-top 2.5px - colorDefaultButton() border-radius 2px - border $ui-border + border none padding 0 5px + background-color $default-button-background + &:hover + background-color $default-button-background--hover .btn-confirm background-color #1EC38B &:hover From deb2cd0156b3bdc8ccb6fbaca3b2678e51f634b0 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Tue, 1 Jan 2019 16:23:15 +0800 Subject: [PATCH 110/159] new feature: auto detect snippet language only try to detect after pasting and mode has not been set and default snippet language is "Auto Detect" --- browser/components/CodeEditor.js | 97 ++++++++++++++++++- browser/lib/newNote.js | 4 +- browser/main/Detail/SnippetNoteDetail.js | 8 +- browser/main/modals/PreferencesModal/UiTab.js | 1 + package.json | 1 + 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index e9d6badc..6243cf5c 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types' import React from 'react' import _ from 'lodash' import CodeMirror from 'codemirror' +import hljs from 'highlight.js' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' @@ -37,6 +38,85 @@ function translateHotkey (hotkey) { return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') } +const languageMaps = { + brainfuck: 'Brainfuck', + cpp: 'C++', + cs: 'C#', + clojure: 'Clojure', + 'clojure-repl': 'ClojureScript', + cmake: 'CMake', + coffeescript: 'CoffeeScript', + crystal: 'Crystal', + css: 'CSS', + d: 'D', + dart: 'Dart', + delphi: 'Pascal', + diff: 'Diff', + django: 'Django', + dockerfile: 'Dockerfile', + ebnf: 'EBNF', + elm: 'Elm', + erlang: 'Erlang', + 'erlang-repl': 'Erlang', + fortran: 'Fortran', + fsharp: 'F#', + gherkin: 'Gherkin', + go: 'Go', + groovy: 'Groovy', + haml: 'HAML', + haskell: 'Haskell', + haxe: 'Haxe', + http: 'HTTP', + ini: 'toml', + java: 'Java', + javascript: 'JavaScript', + json: 'JSON', + julia: 'Julia', + kotlin: 'Kotlin', + less: 'LESS', + livescript: 'LiveScript', + lua: 'Lua', + markdown: 'Markdown', + mathematica: 'Mathematica', + nginx: 'Nginx', + nsis: 'NSIS', + objectivec: 'Objective-C', + ocaml: 'Ocaml', + perl: 'Perl', + php: 'PHP', + powershell: 'PowerShell', + properties: 'Properties files', + protobuf: 'ProtoBuf', + python: 'Python', + puppet: 'Puppet', + q: 'Q', + r: 'R', + ruby: 'Ruby', + rust: 'Rust', + sas: 'SAS', + scala: 'Scala', + scheme: 'Scheme', + scss: 'SCSS', + shell: 'Shell', + smalltalk: 'Smalltalk', + sml: 'SML', + sql: 'SQL', + stylus: 'Stylus', + swift: 'Swift', + tcl: 'Tcl', + tex: 'LaTex', + typescript: 'TypeScript', + twig: 'Twig', + vbnet: 'VB.NET', + vbscript: 'VBScript', + verilog: 'Verilog', + vhdl: 'VHDL', + xml: 'HTML', + xquery: 'XQuery', + yaml: 'YAML', + elixir: 'Elixir' +} + export default class CodeEditor extends React.Component { constructor (props) { super(props) @@ -274,7 +354,11 @@ export default class CodeEditor extends React.Component { extraKeys: this.defaultKeyMap }) - this.setMode(this.props.mode) + if (!this.props.mode && this.props.value) { + this.autoDetectLanguage(this.props.value) + } else { + this.setMode(this.props.mode) + } this.editor.on('focus', this.focusHandler) this.editor.on('blur', this.blurHandler) @@ -644,7 +728,7 @@ export default class CodeEditor extends React.Component { } setMode (mode) { - let syntax = CodeMirror.findModeByName(convertModeName(mode)) + let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text')) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') this.editor.setOption('mode', syntax.mime) @@ -796,6 +880,11 @@ export default class CodeEditor extends React.Component { this.editor.replaceSelection(imageMd) } + autoDetectLanguage (content) { + const res = hljs.highlightAuto(content, Object.keys(languageMaps)) + this.setMode(languageMaps[res.language]) + } + handlePaste (editor, forceSmartPaste) { const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props @@ -892,6 +981,10 @@ export default class CodeEditor extends React.Component { this.handlePasteText(editor, pastedTxt) } } + + if (!this.props.mode) { + this.autoDetectLanguage(editor.doc.getValue()) + } } handleScroll (e) { diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index 9511f847..d8ef196f 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -46,6 +46,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params, tags = params.tagname.split(' ') } + const defaultLanguage = config.editor.snippetDefaultLanguage === 'Auto Detect' ? null : config.editor.snippetDefaultLanguage + return dataApi .createNote(storage, { type: 'SNIPPET_NOTE', @@ -56,7 +58,7 @@ export function createSnippetNote (storage, folder, dispatch, location, params, snippets: [ { name: '', - mode: config.editor.snippetDefaultLanguage || 'text', + mode: defaultLanguage, content: '', linesHighlighted: [] } diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index d8b5798d..12545335 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -599,12 +599,14 @@ class SnippetNoteDetail extends React.Component { } addSnippet () { - const { config } = this.props + const { config: { editor: { snippetDefaultLanguage } } } = this.props const { note } = this.state + const defaultLanguage = snippetDefaultLanguage === 'Auto Detect' ? null : snippetDefaultLanguage + note.snippets = note.snippets.concat([{ name: '', - mode: config.editor.snippetDefaultLanguage || 'text', + mode: defaultLanguage, content: '', linesHighlighted: [] }]) @@ -696,8 +698,6 @@ class SnippetNoteDetail extends React.Component { const viewList = note.snippets.map((snippet, index) => { const isActive = this.state.snippetIndex === index - let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode)) - if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') return
this.handleUIChange(e)} > + { _.sortBy(CodeMirror.modeInfo.map(mode => mode.name)).map(name => ()) } diff --git a/package.json b/package.json index f12360a0..578e2c7b 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "flowchart.js": "^1.6.5", "font-awesome": "^4.3.0", "fs-extra": "^5.0.0", + "highlight.js": "^9.13.1", "i18n-2": "^0.7.2", "iconv-lite": "^0.4.19", "immutable": "^3.8.1", From 2a5da746c7c6490f16409eed26067ca9728c075c Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Tue, 1 Jan 2019 18:39:39 +0900 Subject: [PATCH 111/159] add translation about content menu --- locales/ja.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/locales/ja.json b/locales/ja.json index 355bd0a0..5eb6b3cb 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -26,6 +26,9 @@ "Storage Locations": "ストレージ", "Add Storage Location": "ストレージロケーションを追加", "Add Folder": "フォルダを追加", + "Create new folder": "新規フォルダ作成", + "Folder name": "フォルダ名", + "Create": "作成", "Select Folder": "フォルダを選択", "Open Storage folder": "ストレージフォルダを開く", "Unlink": "リンク解除", @@ -122,7 +125,19 @@ "French": "フランス語", "Show \"Saved to Clipboard\" notification when copying": "クリップボードコピー時に \"クリップボードに保存\" 通知を表示する", "All Notes": "すべてのノート", + "Pin to Top": "一番上にピン留め", + "Remove pin": "ピン削除", + "Clone Note": "ノート複製", + "Copy Note Link": "ノートのリンクをコピー", + "Publish Blog": "ブログを公開", "Starred": "スター付き", + "Empty Trash": "ゴミ箱を空にする", + "Restore Note": "ノート復元", + "Rename Folder": "フォルダの名称変更", + "Export Folder": "フォルダの書き出し", + "Export as txt": ".txtで書き出す", + "Export as md": ".mdで書き出す", + "Delete Folder": "フォルダ削除", "Are you sure to ": "本当に ", " delete": "このフォルダを", "this folder?": "削除しますか?", @@ -184,6 +199,7 @@ "Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup", "Location": "ロケーション", "Add": "追加", + "Export Storage": "ストレージの書き出し", "Unlink Storage": "ストレージのリンクを解除", "Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "リンクの解除ではBoostnoteからリンクされたストレージを削除しますが、データは削除されません。データを削除する場合はご自身でハードドライブからフォルダを削除してください。", "Editor Rulers": "罫線", From 2bbe39120a04dd378e952975ba5ed0adb81b28ec Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Tue, 1 Jan 2019 23:10:29 +0900 Subject: [PATCH 112/159] use FullscreenButton instead of current button --- browser/main/Detail/SnippetNoteDetail.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index d8b5798d..8b3db151 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -20,6 +20,7 @@ import _ from 'lodash' import {findNoteTitle} from 'browser/lib/findNoteTitle' import convertModeName from 'browser/lib/convertModeName' import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' +import FullscreenButton from './FullscreenButton' import TrashButton from './TrashButton' import RestoreButton from './RestoreButton' import PermanentDeleteButton from './PermanentDeleteButton' @@ -796,11 +797,7 @@ class SnippetNoteDetail extends React.Component { isActive={note.isStarred} /> - + this.handleFullScreenButton(e)} /> this.handleTrashButtonClick(e)} /> From fce89fe8befa684c94b89c005bb5b5ec2b6de910 Mon Sep 17 00:00:00 2001 From: Douglas Caina Date: Tue, 1 Jan 2019 22:20:23 +0100 Subject: [PATCH 113/159] Update pt-BR.json ## Description There was a misspelling on the word "escluir", the correct one is "excluir", as you can see on [google translate](https://translate.google.com/?source=osdd#view=home&op=translate&sl=pt&tl=en&text=excluir) ## Type of changes - :white_circle: Bug fix (Change that fixed an issue) - :white_circle: Breaking change (Change that can cause existing functionality to change) - :white_circle: Improvement (Change that improves the code. Maybe performance or development improvement) - :white_circle: Feature (Change that adds new functionality) - :white_circle: Documentation change (Change that modifies documentation. Maybe typo fixes) ## Checklist: - :white_circle: My code follows [the project code style](docs/code_style.md) - :white_circle: I have written test for my code and it has been tested - :white_circle: All existing tests have been passed - :white_circle: I have attached a screenshot/video to visualize my change if possible --- locales/pt-BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 6b3126cc..273466eb 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -33,7 +33,7 @@ "White": "Branco", "Solarized Dark": "Escuro Solarizado", "Dark": "Escuro", - "Show a confirmation dialog when deleting notes": "Mostrar um diálogo de confirmação ao escluir notas", + "Show a confirmation dialog when deleting notes": "Mostrar um diálogo de confirmação ao excluir notas", "Editor Theme": "Tema do Editor", "Editor Font Size": "Tamanho da Fonte do Editor", "Editor Font Family": "Família da Fonte do Editor", From 0a0c1c45a16d438cdb14d22cdd6f1ebbd67d0545 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Tue, 1 Jan 2019 22:00:30 +0900 Subject: [PATCH 114/159] translate tooltip texts into Japanese --- locales/ja.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/locales/ja.json b/locales/ja.json index 9cfc2986..832bc308 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -8,7 +8,10 @@ "to create a new note": "ノートを新規に作成", "Toggle Mode": "モード切替", "Add tag...": "タグを追加...", + "Star": "お気に入り", + "Fullscreen": "全画面", "Trash": "ゴミ箱", + "Info": "情報", "MODIFICATION DATE": "修正日", "Words": "ワード", "Letters": "文字", From 5cf4a0e09d47522f4f727885755a9dc92900fc2a Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Wed, 2 Jan 2019 02:49:10 +0900 Subject: [PATCH 115/159] localize StarButton for Japanese --- browser/main/Detail/StarButton.js | 2 +- browser/main/Detail/StarButton.styl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/browser/main/Detail/StarButton.js b/browser/main/Detail/StarButton.js index d74809cd..8000970d 100644 --- a/browser/main/Detail/StarButton.js +++ b/browser/main/Detail/StarButton.js @@ -54,7 +54,7 @@ class StarButton extends React.Component { : '../resources/icon/icon-star.svg' } /> - {i18n.__('Star')} + {i18n.__('Star')} ) } diff --git a/browser/main/Detail/StarButton.styl b/browser/main/Detail/StarButton.styl index d5fd755b..e9c523e9 100644 --- a/browser/main/Detail/StarButton.styl +++ b/browser/main/Detail/StarButton.styl @@ -21,6 +21,11 @@ opacity 0 transition 0.1s +.tooltip:lang(ja) + @extend .tooltip + right 103px + width 70px + .root--active @extend .root transition 0.15s From caa6c8d4b94045ad2f31f645f63fe77c3db162eb Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Wed, 2 Jan 2019 11:01:37 +0900 Subject: [PATCH 116/159] localize FullscreenButton for Japanese --- browser/main/Detail/FullscreenButton.js | 2 +- browser/main/Detail/FullscreenButton.styl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/main/Detail/FullscreenButton.js b/browser/main/Detail/FullscreenButton.js index ee212603..3a86a751 100644 --- a/browser/main/Detail/FullscreenButton.js +++ b/browser/main/Detail/FullscreenButton.js @@ -11,7 +11,7 @@ const FullscreenButton = ({ }) => ( ) diff --git a/browser/main/Detail/FullscreenButton.styl b/browser/main/Detail/FullscreenButton.styl index cc1a8dff..133577f3 100644 --- a/browser/main/Detail/FullscreenButton.styl +++ b/browser/main/Detail/FullscreenButton.styl @@ -17,6 +17,10 @@ opacity 0 transition 0.1s +.tooltip:lang(ja) + @extend .tooltip + right 35px + body[data-theme="dark"] .control-fullScreenButton topBarButtonDark() \ No newline at end of file From d0f5ec8adae8dae5d00b8a80a62757bab4e20665 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Wed, 2 Jan 2019 11:03:40 +0900 Subject: [PATCH 117/159] fix FullscreenButton --- browser/main/Detail/FullscreenButton.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/browser/main/Detail/FullscreenButton.js b/browser/main/Detail/FullscreenButton.js index 3a86a751..bd76447c 100644 --- a/browser/main/Detail/FullscreenButton.js +++ b/browser/main/Detail/FullscreenButton.js @@ -5,15 +5,17 @@ import styles from './FullscreenButton.styl' import i18n from 'browser/lib/i18n' const OSX = global.process.platform === 'darwin' -const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B' const FullscreenButton = ({ onClick -}) => ( - -) +}) => { + const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B' + return ( + + ) +} FullscreenButton.propTypes = { onClick: PropTypes.func.isRequired From b05ba64db8c1b3c32e22fc520fd939f78e487000 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Wed, 2 Jan 2019 11:07:15 +0900 Subject: [PATCH 118/159] localize TrashButton for Japanese --- browser/main/Detail/TrashButton.js | 2 +- browser/main/Detail/TrashButton.styl | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/main/Detail/TrashButton.js b/browser/main/Detail/TrashButton.js index 473c2d0b..d26be66e 100644 --- a/browser/main/Detail/TrashButton.js +++ b/browser/main/Detail/TrashButton.js @@ -11,7 +11,7 @@ const TrashButton = ({ onClick={(e) => onClick(e)} > - {i18n.__('Trash')} + {i18n.__('Trash')} ) diff --git a/browser/main/Detail/TrashButton.styl b/browser/main/Detail/TrashButton.styl index 7c7af878..a82cfa6b 100644 --- a/browser/main/Detail/TrashButton.styl +++ b/browser/main/Detail/TrashButton.styl @@ -17,6 +17,10 @@ opacity 0 transition 0.1s +.tooltip:lang(ja) + @extend .tooltip + right 46px + .control-trashButton--in-trash top 60px topBarButtonRight() From e6a927e5afcf5298d2bc679470d04ec2be7be8b3 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Wed, 2 Jan 2019 11:10:07 +0900 Subject: [PATCH 119/159] localize ToggleModeButton for Japanese --- browser/main/Detail/ToggleModeButton.js | 2 +- browser/main/Detail/ToggleModeButton.styl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/browser/main/Detail/ToggleModeButton.js b/browser/main/Detail/ToggleModeButton.js index 7c026429..fcbaab34 100644 --- a/browser/main/Detail/ToggleModeButton.js +++ b/browser/main/Detail/ToggleModeButton.js @@ -14,7 +14,7 @@ const ToggleModeButton = ({
onClick('EDITOR_PREVIEW')}>
- {i18n.__('Toggle Mode')} + {i18n.__('Toggle Mode')}
) diff --git a/browser/main/Detail/ToggleModeButton.styl b/browser/main/Detail/ToggleModeButton.styl index 73f5acbd..2b47b932 100644 --- a/browser/main/Detail/ToggleModeButton.styl +++ b/browser/main/Detail/ToggleModeButton.styl @@ -40,6 +40,11 @@ opacity 0 transition 0.1s +.tooltip:lang(ja) + @extend .tooltip + left -8px + width 70px + body[data-theme="dark"] .control-fullScreenButton topBarButtonDark() From 04fdb67fc978e0d85d502cd7daa9839cd39b82f5 Mon Sep 17 00:00:00 2001 From: Rahul Gurung Date: Wed, 2 Jan 2019 12:38:26 +0530 Subject: [PATCH 120/159] #2194 - fixes Hotkey not working --- browser/main/lib/ConfigManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 81165777..25e6e918 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -208,7 +208,7 @@ function assignConfigValues (originalConfig, rcConfig) { function rewriteHotkey (config) { const keys = [...Object.keys(config.hotkey)] keys.forEach(key => { - config.hotkey[key] = config.hotkey[key].replace(/Cmd/g, 'Command') + config.hotkey[key] = config.hotkey[key].replace(/Cmd\s/g, 'Command ') config.hotkey[key] = config.hotkey[key].replace(/Opt\s/g, 'Option ') }) return config From 082a078b511d96458dfe0b4a6e5c3a73cf0f3cf2 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Fri, 4 Jan 2019 21:55:58 +0800 Subject: [PATCH 121/159] check config before auto detect language --- browser/components/CodeEditor.js | 6 ++++-- browser/main/Detail/SnippetNoteDetail.js | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 6243cf5c..48634993 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -354,7 +354,7 @@ export default class CodeEditor extends React.Component { extraKeys: this.defaultKeyMap }) - if (!this.props.mode && this.props.value) { + if (!this.props.mode && this.props.value && this.props.autoDetect) { this.autoDetectLanguage(this.props.value) } else { this.setMode(this.props.mode) @@ -982,7 +982,7 @@ export default class CodeEditor extends React.Component { } } - if (!this.props.mode) { + if (!this.props.mode && this.props.autoDetect) { this.autoDetectLanguage(editor.doc.getValue()) } } @@ -1184,6 +1184,7 @@ CodeEditor.propTypes = { onBlur: PropTypes.func, onChange: PropTypes.func, readOnly: PropTypes.bool, + autoDetect: PropTypes.bool, spellCheck: PropTypes.bool } @@ -1195,5 +1196,6 @@ CodeEditor.defaultProps = { fontFamily: 'Monaco, Consolas', indentSize: 4, indentType: 'space', + autoDetect: false, spellCheck: false } diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 12545335..e9b0ee37 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -674,6 +674,8 @@ class SnippetNoteDetail extends React.Component { const storageKey = note.storage const folderKey = note.folder + const autoDetect = config.editor.snippetDefaultLanguage === 'Auto Detect' + let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 let editorIndentSize = parseInt(config.editor.indentSize, 10) @@ -713,7 +715,7 @@ class SnippetNoteDetail extends React.Component { storageKey={storageKey} /> : }
From 406e230ed3192cc05988bcac29ec7806def4e321 Mon Sep 17 00:00:00 2001 From: Foo-x Date: Thu, 10 Jan 2019 23:26:47 +0900 Subject: [PATCH 122/159] Fix bullet position of LaTeX equation in list --- browser/components/markdown.styl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index da767a9f..304fa1ee 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -61,6 +61,8 @@ body // allow inline line breaks .katex white-space initial + .katex .katex-html + display inline-flex .katex .mfrac>.vlist>span:nth-child(2) top 0 !important .katex-error From aac13dcdcabb2f975cf7a27b64dae24a37c27517 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 08:26:31 -0800 Subject: [PATCH 123/159] Updated build.md to include directions for checking out PRs --- docs/build.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/build.md b/docs/build.md index eaf94666..f60361aa 100644 --- a/docs/build.md +++ b/docs/build.md @@ -31,6 +31,34 @@ $ yarn run dev > 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.) +## Accessing code used in Pull Requests +Visit the page for the pull request and look at the end of the url for the PR number +
+https://github.com/BoostIO/Boostnote/pull/2794
+
+In the following, replace with that number (no brackets). +For the above url, you would replace with 2794 + +_If you do not have a local copy the master branch yet_ +``` +git clone https://github.com/BoostIO/Boostnote.git +cd Boostnote +git fetch origin pull/2612/head: +git checkout +``` + +_If you already have the master branch_ +``` +git fetch origin pull/2612/head: +git checkout +``` + +_To compile and run the code_ +``` +yarn +yarn build +``` + ## Deploy We use Grunt to automate deployment. From 5e9bd2fd2d7a66671e7c91051366ed53984ba4d4 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 08:34:14 -0800 Subject: [PATCH 124/159] escaped angle brackets --- docs/build.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build.md b/docs/build.md index f60361aa..91176611 100644 --- a/docs/build.md +++ b/docs/build.md @@ -36,8 +36,8 @@ Visit the page for the pull request and look at the end of the url for the PR nu
 https://github.com/BoostIO/Boostnote/pull/2794
 
-In the following, replace with that number (no brackets). -For the above url, you would replace with 2794 +In the following, replace \ with that number (no brackets). +For the above url, you would replace \ with 2794 _If you do not have a local copy the master branch yet_ ``` From 7b8fb5644089c07f2d6300f07bd9bded4b92e91f Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 08:38:18 -0800 Subject: [PATCH 125/159] fixed error in url --- docs/build.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build.md b/docs/build.md index 91176611..36aea9a7 100644 --- a/docs/build.md +++ b/docs/build.md @@ -43,13 +43,13 @@ _If you do not have a local copy the master branch yet_ ``` git clone https://github.com/BoostIO/Boostnote.git cd Boostnote -git fetch origin pull/2612/head: +git fetch origin pull//head: git checkout ``` _If you already have the master branch_ ``` -git fetch origin pull/2612/head: +git fetch origin pull//head: git checkout ``` From 6fb72bd44ab4428b0a66ad4a52502e7fddacfb2e Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 09:46:51 -0800 Subject: [PATCH 126/159] minor typo --- docs/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.md b/docs/build.md index 36aea9a7..08a1b5dc 100644 --- a/docs/build.md +++ b/docs/build.md @@ -39,7 +39,7 @@ https://github.com/BoostIO/Boostnote/pull/2794 In the following, replace \ with that number (no brackets). For the above url, you would replace \ with 2794 -_If you do not have a local copy the master branch yet_ +_If you do not have a local copy of the master branch yet_ ``` git clone https://github.com/BoostIO/Boostnote.git cd Boostnote From e298739cb9021a18136ab6bfded44604258b6b07 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 12:34:25 -0800 Subject: [PATCH 127/159] fixed typo --- docs/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.md b/docs/build.md index 08a1b5dc..095f8628 100644 --- a/docs/build.md +++ b/docs/build.md @@ -56,7 +56,7 @@ git checkout _To compile and run the code_ ``` yarn -yarn build +yarn run dev ``` ## Deploy From 47d7cef2148d0307080303efbe82268523a80c0b Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 15:50:33 -0800 Subject: [PATCH 128/159] Update copywrite year 2018->2019 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index aba9b92a..d1eb710b 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ Issues on Boostnote can be funded by anyone and the money will be distributed to * Website: https://boostnote.io * Newsletters: https://boostnote.io/#subscribe * [Development](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md): Development configurations for Boostnote. -* Copyright (C) 2016 - 2018 BoostIO, Inc. +* Copyright (C) 2016 - 2019 BoostIO, Inc. #### License From a8e601e5e00d94eb726581f1bb49296bbfd16136 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 16:09:25 -0800 Subject: [PATCH 129/159] Update InfoTab.js --- browser/main/modals/PreferencesModal/InfoTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js index d618fa22..dafabb02 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.js +++ b/browser/main/modals/PreferencesModal/InfoTab.js @@ -134,7 +134,7 @@ class InfoTab extends React.Component { >{i18n.__('Development')}
{i18n.__(' : Development configurations for Boostnote.')}
  • - {i18n.__('Copyright (C) 2017 - 2018 BoostIO')} + {i18n.__('Copyright (C) 2017 - 2019 BoostIO')}
  • {i18n.__('License: GPL v3')} From a0bfd9e497a3b742b47977033f4f0c026fbd8e77 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 17:58:19 -0800 Subject: [PATCH 130/159] Disables updates in development mode --- lib/main-app.js | 11 ++++++++--- package.json | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/main-app.js b/lib/main-app.js index f25d07d2..7a31d282 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -2,7 +2,8 @@ const electron = require('electron') const app = electron.app const Menu = electron.Menu const ipc = electron.ipcMain -const GhReleases = require('electron-gh-releases') +const GhReleases = require('electron-gh-releases') +const isDev = require('electron-is-dev') //isDev is True if running in development mode. Overide by setting ELECTRON_IS_DEV environment variable to 1 // electron.crashReporter.start() var ipcServer = null @@ -35,6 +36,10 @@ const updater = new GhReleases(ghReleasesOpts) // Check for updates // `status` returns true if there is a new update available function checkUpdate () { + if (isDev){ //Prevents app from attempting to update when in dev mode. + console.log("Updates are disabled in Development mode, see main-app.js") + return true + } if (process.platform === 'linux' || isUpdateReady) { return true } @@ -94,12 +99,12 @@ app.on('ready', function () { // Check update every day setInterval(function () { - checkUpdate() + if(!isDev) checkUpdate() }, 1000 * 60 * 60 * 24) // Check update after 10 secs to prevent file locking of Windows setTimeout(() => { - checkUpdate() + if(!isDev) checkUpdate() ipc.on('update-check', function (event, msg) { if (isUpdateReady) { diff --git a/package.json b/package.json index 95b4ecd7..568d3852 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "codemirror-mode-elixir": "^1.1.1", "electron-config": "^1.0.0", "electron-gh-releases": "^2.0.4", + "electron-is-dev": "^1.0.1", "escape-string-regexp": "^1.0.5", "file-uri-to-path": "^1.0.0", "file-url": "^2.0.2", From fa77cda0b473bf3a3e5389aa64c9e84a92c2739e Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 17:59:04 -0800 Subject: [PATCH 131/159] whitespace --- lib/main-app.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/main-app.js b/lib/main-app.js index 7a31d282..c5645940 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -2,8 +2,8 @@ const electron = require('electron') const app = electron.app const Menu = electron.Menu const ipc = electron.ipcMain -const GhReleases = require('electron-gh-releases') -const isDev = require('electron-is-dev') //isDev is True if running in development mode. Overide by setting ELECTRON_IS_DEV environment variable to 1 +const GhReleases = require('electron-gh-releases') +const isDev = require('electron-is-dev') // isDev is True if running in development mode. Overide by setting ELECTRON_IS_DEV environment variable to 1 // electron.crashReporter.start() var ipcServer = null @@ -36,8 +36,8 @@ const updater = new GhReleases(ghReleasesOpts) // Check for updates // `status` returns true if there is a new update available function checkUpdate () { - if (isDev){ //Prevents app from attempting to update when in dev mode. - console.log("Updates are disabled in Development mode, see main-app.js") + if (isDev) { // Prevents app from attempting to update when in dev mode. + console.log('Updates are disabled in Development mode, see main-app.js') return true } if (process.platform === 'linux' || isUpdateReady) { @@ -99,12 +99,12 @@ app.on('ready', function () { // Check update every day setInterval(function () { - if(!isDev) checkUpdate() + if (!isDev) checkUpdate() }, 1000 * 60 * 60 * 24) // Check update after 10 secs to prevent file locking of Windows setTimeout(() => { - if(!isDev) checkUpdate() + if (!isDev) checkUpdate() ipc.on('update-check', function (event, msg) { if (isUpdateReady) { From 951a126d63e7ff3abda89e1dcbc7eec3e2d59a38 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sat, 12 Jan 2019 18:51:03 -0800 Subject: [PATCH 132/159] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 7472c9eb..2d1ab131 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ GPL-3.0 Boostnote - an open source note-taking app made for programmers just like you. -Copyright (C) 2017 - 2018 BoostIO +Copyright (C) 2017 - 2019 BoostIO This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 038154c4415cccca429c2d8c353822e07805e610 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sun, 13 Jan 2019 17:21:30 -0800 Subject: [PATCH 133/159] Removed uneeded "electron-is-dev" package --- lib/main-app.js | 2 +- package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/main-app.js b/lib/main-app.js index c5645940..57d6b26c 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -3,7 +3,7 @@ const app = electron.app const Menu = electron.Menu const ipc = electron.ipcMain const GhReleases = require('electron-gh-releases') -const isDev = require('electron-is-dev') // isDev is True if running in development mode. Overide by setting ELECTRON_IS_DEV environment variable to 1 +const isDev = 'ELECTRON_IS_DEV' in process.env // electron.crashReporter.start() var ipcServer = null diff --git a/package.json b/package.json index 568d3852..95b4ecd7 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "codemirror-mode-elixir": "^1.1.1", "electron-config": "^1.0.0", "electron-gh-releases": "^2.0.4", - "electron-is-dev": "^1.0.1", "escape-string-regexp": "^1.0.5", "file-uri-to-path": "^1.0.0", "file-url": "^2.0.2", From 99b53f4a55c6957796b345d8f6a5a19ddb3f73d7 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sun, 13 Jan 2019 18:21:12 -0800 Subject: [PATCH 134/159] added filter. --- browser/main/lib/dataApi/init.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/main/lib/dataApi/init.js b/browser/main/lib/dataApi/init.js index 7f81e90b..adab0232 100644 --- a/browser/main/lib/dataApi/init.js +++ b/browser/main/lib/dataApi/init.js @@ -4,6 +4,7 @@ const resolveStorageData = require('./resolveStorageData') const resolveStorageNotes = require('./resolveStorageNotes') const consts = require('browser/lib/consts') const path = require('path') +const fs = require('fs') const CSON = require('@rokt33r/season') /** * @return {Object} all storages and notes @@ -19,11 +20,14 @@ const CSON = require('@rokt33r/season') * 2. legacy * 3. empty directory */ + function init () { const fetchStorages = function () { let rawStorages try { rawStorages = JSON.parse(window.localStorage.getItem('storages')) + // Remove storages who's location is inaccesible. + rawStorages = rawStorages.filter(storage => fs.existsSync(storage.path)) if (!_.isArray(rawStorages)) throw new Error('Cached data is not valid.') } catch (e) { console.warn('Failed to parse cached data from localStorage', e) From 8d817066e85f8841036dff8d122694ca27bee5a1 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sun, 13 Jan 2019 18:35:24 -0800 Subject: [PATCH 135/159] Added a few more checks in related methods to prevent simular errors --- browser/main/lib/dataApi/init.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/init.js b/browser/main/lib/dataApi/init.js index adab0232..5149c263 100644 --- a/browser/main/lib/dataApi/init.js +++ b/browser/main/lib/dataApi/init.js @@ -40,6 +40,7 @@ function init () { const fetchNotes = function (storages) { const findNotesFromEachStorage = storages + .filter(storage => fs.existsSync(storage.path)) .map((storage) => { return resolveStorageNotes(storage) .then((notes) => { @@ -54,7 +55,7 @@ function init () { }) } }) - if (unknownCount > 0) { + if (unknownCount > 0 && fs.existsSync(storage.path)) { CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) } return notes From f566b567be5ab80da1889313a3a46532b4e773b8 Mon Sep 17 00:00:00 2001 From: roottool Date: Wed, 16 Jan 2019 01:52:44 +0900 Subject: [PATCH 136/159] add zoom-in and zoom-out action of images #1448 --- browser/components/MarkdownPreview.js | 74 +++++++++++++++++++++++++++ browser/components/markdown.styl | 1 + 2 files changed, 75 insertions(+) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 4c638dc9..3a2be4ec 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -832,6 +832,80 @@ export default class MarkdownPreview extends React.Component { ) } ) + + document.querySelectorAll('iframe').forEach(item => { + const rect = item.getBoundingClientRect() + const imgList = item.contentWindow.document.body.querySelectorAll('img') + for (const img of imgList) { + img.onclick = function () { + const zoomImg = document.createElement('img') + const magnification = document.body.clientWidth / img.width + const top = document.body.clientHeight / 2 - img.height * magnification / 2 + // TODO: DRY and const animationSpeed + zoomImg.src = img.src + zoomImg.style = ` + position: absolute; + top: ${top}px; + left: 0; + width: 100%; + height: ${img.height * magnification}px; + ` + zoomImg.animate([ + { + top: `${img.y + rect.top}px`, + left: `${img.x + rect.left}px`, + width: `${img.width}px`, + height: `${img.height}px` + }, + { + top: `${top}px`, + left: 0, + width: `100%`, + height: `${img.height * magnification}px` + } + ], 300) + + const overlay = document.createElement('div') + overlay.style = ` + background-color: rgba(0,0,0,0.5); + cursor: zoom-out; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: ${document.body.clientHeight}px; + z-index: 100; + ` + overlay.onclick = function () { + zoomImg.style = ` + position: absolute; + top: ${img.y + rect.top}px; + left: ${img.x + rect.left}px; + width: ${img.width}px; + height: ${img.height}px; + ` + const zoomOutImgAnimation = zoomImg.animate([ + { + top: `${top}px`, + left: 0, + width: `100%`, + height: `${img.height * magnification}px` + }, + { + top: `${img.y + rect.top}px`, + left: `${img.x + rect.left}px`, + width: `${img.width}px`, + height: `${img.height}px` + } + ], 300) + zoomOutImgAnimation.onfinish = () => overlay.remove() + } + + overlay.appendChild(zoomImg) + document.body.appendChild(overlay) + } + } + }) } focus () { diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index da767a9f..89c26032 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -163,6 +163,7 @@ p white-space pre-line word-wrap break-word img + cursor zoom-in max-width 100% strong, b font-weight bold From 72df418953336c0c6edd79f2fe12e2a8a50db051 Mon Sep 17 00:00:00 2001 From: roottool Date: Wed, 16 Jan 2019 18:10:28 +0900 Subject: [PATCH 137/159] DRY my code #1448 --- browser/components/MarkdownPreview.js | 53 ++++++++++++--------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 3a2be4ec..c3b31665 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -837,11 +837,24 @@ export default class MarkdownPreview extends React.Component { const rect = item.getBoundingClientRect() const imgList = item.contentWindow.document.body.querySelectorAll('img') for (const img of imgList) { - img.onclick = function () { - const zoomImg = document.createElement('img') + img.onclick = () => { const magnification = document.body.clientWidth / img.width const top = document.body.clientHeight / 2 - img.height * magnification / 2 - // TODO: DRY and const animationSpeed + const animationSpeed = 300 + const originalImgRect = { + top: `${img.y + rect.top}px`, + left: `${img.x + rect.left}px`, + width: `${img.width}px`, + height: `${img.height}px` + } + const zoomInImgRect = { + top: `${top}px`, + left: 0, + width: `100%`, + height: `${img.height * magnification}px` + } + + const zoomImg = document.createElement('img') zoomImg.src = img.src zoomImg.style = ` position: absolute; @@ -851,19 +864,9 @@ export default class MarkdownPreview extends React.Component { height: ${img.height * magnification}px; ` zoomImg.animate([ - { - top: `${img.y + rect.top}px`, - left: `${img.x + rect.left}px`, - width: `${img.width}px`, - height: `${img.height}px` - }, - { - top: `${top}px`, - left: 0, - width: `100%`, - height: `${img.height * magnification}px` - } - ], 300) + originalImgRect, + zoomInImgRect + ], animationSpeed) const overlay = document.createElement('div') overlay.style = ` @@ -876,7 +879,7 @@ export default class MarkdownPreview extends React.Component { height: ${document.body.clientHeight}px; z-index: 100; ` - overlay.onclick = function () { + overlay.onclick = () => { zoomImg.style = ` position: absolute; top: ${img.y + rect.top}px; @@ -885,19 +888,9 @@ export default class MarkdownPreview extends React.Component { height: ${img.height}px; ` const zoomOutImgAnimation = zoomImg.animate([ - { - top: `${top}px`, - left: 0, - width: `100%`, - height: `${img.height * magnification}px` - }, - { - top: `${img.y + rect.top}px`, - left: `${img.x + rect.left}px`, - width: `${img.width}px`, - height: `${img.height}px` - } - ], 300) + zoomInImgRect, + originalImgRect + ], animationSpeed) zoomOutImgAnimation.onfinish = () => overlay.remove() } From ea5970ab1cc62c596bd254fa820b005e5700570b Mon Sep 17 00:00:00 2001 From: roottool Date: Wed, 16 Jan 2019 22:47:42 +0900 Subject: [PATCH 138/159] change a magnification by image size #1448 --- browser/components/MarkdownPreview.js | 39 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index c3b31665..7a74c4f6 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -838,30 +838,39 @@ export default class MarkdownPreview extends React.Component { const imgList = item.contentWindow.document.body.querySelectorAll('img') for (const img of imgList) { img.onclick = () => { - const magnification = document.body.clientWidth / img.width - const top = document.body.clientHeight / 2 - img.height * magnification / 2 - const animationSpeed = 300 + const widthMagnification = document.body.clientWidth / img.width + const heightMagnification = document.body.clientHeight / img.height + const baseOnWidth = widthMagnification < heightMagnification + const magnification = baseOnWidth ? widthMagnification : heightMagnification + + const zoomImgWidth = img.width * magnification + const zoomImgHeight = img.height * magnification + const zoomInImgTop = document.body.clientHeight / 2 - zoomImgHeight / 2 + const zoomInImgLeft = document.body.clientWidth / 2 - zoomImgWidth / 2 + const originalImgTop = img.y + rect.top + const originalImgLeft = img.x + rect.left const originalImgRect = { - top: `${img.y + rect.top}px`, - left: `${img.x + rect.left}px`, + top: `${originalImgTop}px`, + left: `${originalImgLeft}px`, width: `${img.width}px`, height: `${img.height}px` } const zoomInImgRect = { - top: `${top}px`, - left: 0, - width: `100%`, - height: `${img.height * magnification}px` + top: `${baseOnWidth ? zoomInImgTop : 0}px`, + left: `${baseOnWidth ? 0 : zoomInImgLeft}px`, + width: `${zoomImgWidth}px`, + height: `${zoomImgHeight}px` } + const animationSpeed = 300 const zoomImg = document.createElement('img') zoomImg.src = img.src zoomImg.style = ` position: absolute; - top: ${top}px; - left: 0; - width: 100%; - height: ${img.height * magnification}px; + top: ${baseOnWidth ? zoomInImgTop : 0}px; + left: ${baseOnWidth ? 0 : zoomInImgLeft}px; + width: ${zoomImgWidth}; + height: ${zoomImgHeight}px; ` zoomImg.animate([ originalImgRect, @@ -882,8 +891,8 @@ export default class MarkdownPreview extends React.Component { overlay.onclick = () => { zoomImg.style = ` position: absolute; - top: ${img.y + rect.top}px; - left: ${img.x + rect.left}px; + top: ${originalImgTop}px; + left: ${originalImgLeft}px; width: ${img.width}px; height: ${img.height}px; ` From 294bf742cdd2b9dfbd589e991cd7687490047d41 Mon Sep 17 00:00:00 2001 From: roottool Date: Wed, 16 Jan 2019 23:14:21 +0900 Subject: [PATCH 139/159] change variable name #1448 --- browser/components/MarkdownPreview.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 7a74c4f6..abaebad3 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -845,8 +845,8 @@ export default class MarkdownPreview extends React.Component { const zoomImgWidth = img.width * magnification const zoomImgHeight = img.height * magnification - const zoomInImgTop = document.body.clientHeight / 2 - zoomImgHeight / 2 - const zoomInImgLeft = document.body.clientWidth / 2 - zoomImgWidth / 2 + const zoomImgTop = document.body.clientHeight / 2 - zoomImgHeight / 2 + const zoomImgLeft = document.body.clientWidth / 2 - zoomImgWidth / 2 const originalImgTop = img.y + rect.top const originalImgLeft = img.x + rect.left const originalImgRect = { @@ -856,8 +856,8 @@ export default class MarkdownPreview extends React.Component { height: `${img.height}px` } const zoomInImgRect = { - top: `${baseOnWidth ? zoomInImgTop : 0}px`, - left: `${baseOnWidth ? 0 : zoomInImgLeft}px`, + top: `${baseOnWidth ? zoomImgTop : 0}px`, + left: `${baseOnWidth ? 0 : zoomImgLeft}px`, width: `${zoomImgWidth}px`, height: `${zoomImgHeight}px` } @@ -867,8 +867,8 @@ export default class MarkdownPreview extends React.Component { zoomImg.src = img.src zoomImg.style = ` position: absolute; - top: ${baseOnWidth ? zoomInImgTop : 0}px; - left: ${baseOnWidth ? 0 : zoomInImgLeft}px; + top: ${baseOnWidth ? zoomImgTop : 0}px; + left: ${baseOnWidth ? 0 : zoomImgLeft}px; width: ${zoomImgWidth}; height: ${zoomImgHeight}px; ` From 3fe45e9cbb9ef8a13c5ee21f7bf7ba1f6fe8dc45 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Wed, 16 Jan 2019 16:09:07 -0800 Subject: [PATCH 140/159] Added cheatsheets to menu --- lib/main-menu.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/main-menu.js b/lib/main-menu.js index 05921347..dcd85217 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -383,6 +383,27 @@ const help = { { label: 'Changelog', click () { shell.openExternal('https://github.com/BoostIO/boost-releases') } + }, + { + label: 'Cheatsheets', + submenu: [ + { + label: 'Markdown', + click () { shell.openExternal('https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet') } + }, + { + label: 'Latex', + click () { shell.openExternal('https://katex.org/docs/supported.html') } + }, + { + label: 'HTML', + click () { shell.openExternal('https://htmlcheatsheet.com/') } + }, + { + label: 'Boostnote', + click () { shell.openExternal('https://github.com/TobseF/boostnote-markdown-cheatsheet/blob/master/BOOSTNOTE_MARKDOWN_CHEAT_SHEET.md') } + } + ] } ] } From eea01f10ace4d8393875ede77a868c84ea1a1661 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Thu, 17 Jan 2019 17:00:07 -0800 Subject: [PATCH 141/159] Added catch for exceptions, removed uneeded duplicate test. --- browser/main/lib/dataApi/init.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/browser/main/lib/dataApi/init.js b/browser/main/lib/dataApi/init.js index 5149c263..0dbcc182 100644 --- a/browser/main/lib/dataApi/init.js +++ b/browser/main/lib/dataApi/init.js @@ -55,8 +55,12 @@ function init () { }) } }) - if (unknownCount > 0 && fs.existsSync(storage.path)) { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + if (unknownCount > 0) { + try { + CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + } catch (e) { + console.log('Error writting boostnote.json: ' + e + ' from init.js') + } } return notes }) From c3a980836ad74ced7a0738b65bcc3ded674bdc33 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Sun, 20 Jan 2019 21:04:56 -0800 Subject: [PATCH 142/159] change to isDev --- lib/main-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main-app.js b/lib/main-app.js index 57d6b26c..7225dd9b 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -3,7 +3,7 @@ const app = electron.app const Menu = electron.Menu const ipc = electron.ipcMain const GhReleases = require('electron-gh-releases') -const isDev = 'ELECTRON_IS_DEV' in process.env +const isDev = 'ELECTRON_IS_DEV' in process.env && process.env.NODE_ENV !== 'production' // electron.crashReporter.start() var ipcServer = null From b32865488e427f116067d9451f27e5e49d64470a Mon Sep 17 00:00:00 2001 From: Nguyen Viet Hung Date: Tue, 22 Jan 2019 16:07:44 +0700 Subject: [PATCH 143/159] fixed app body background (#2791) --- browser/main/global.styl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/main/global.styl b/browser/main/global.styl index e04060c2..d864993d 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -97,6 +97,7 @@ modalBackColor = white body[data-theme="dark"] + background-color $ui-dark-backgroundColor ::-webkit-scrollbar-thumb background-color rgba(0, 0, 0, 0.3) .ModalBase @@ -148,6 +149,7 @@ body[data-theme="dark"] z-index modalZIndex + 5 body[data-theme="solarized-dark"] + background-color $ui-solarized-dark-backgroundColor ::-webkit-scrollbar-thumb background-color rgba(0, 0, 0, 0.3) .ModalBase @@ -157,6 +159,7 @@ body[data-theme="solarized-dark"] color: $ui-solarized-dark-text-color body[data-theme="monokai"] + background-color $ui-monokai-backgroundColor ::-webkit-scrollbar-thumb background-color rgba(0, 0, 0, 0.3) .ModalBase @@ -166,6 +169,7 @@ body[data-theme="monokai"] color: $ui-monokai-text-color body[data-theme="dracula"] + background-color $ui-dracula-backgroundColor ::-webkit-scrollbar-thumb background-color rgba(0, 0, 0, 0.3) .ModalBase From 667ece7d3f3f52ae8e468b727da68a7531f38fe9 Mon Sep 17 00:00:00 2001 From: Milo Todt Date: Tue, 22 Jan 2019 19:59:27 -0800 Subject: [PATCH 144/159] Update main-app.js --- lib/main-app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main-app.js b/lib/main-app.js index 7225dd9b..88e44294 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -3,7 +3,7 @@ const app = electron.app const Menu = electron.Menu const ipc = electron.ipcMain const GhReleases = require('electron-gh-releases') -const isDev = 'ELECTRON_IS_DEV' in process.env && process.env.NODE_ENV !== 'production' +const isDev = process.env.NODE_ENV !== 'production' // electron.crashReporter.start() var ipcServer = null From 33fb03066edc3dcc2af2754eac6c0471ceac4a88 Mon Sep 17 00:00:00 2001 From: roottool Date: Fri, 25 Jan 2019 20:06:43 +0900 Subject: [PATCH 145/159] Reduced the count of calculation --- browser/components/MarkdownPreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index abaebad3..8ee90e37 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -845,8 +845,8 @@ export default class MarkdownPreview extends React.Component { const zoomImgWidth = img.width * magnification const zoomImgHeight = img.height * magnification - const zoomImgTop = document.body.clientHeight / 2 - zoomImgHeight / 2 - const zoomImgLeft = document.body.clientWidth / 2 - zoomImgWidth / 2 + const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2 + const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2 const originalImgTop = img.y + rect.top const originalImgLeft = img.x + rect.left const originalImgRect = { From 4cce52f9ce34c85a4feedb92a3f83e67c22931c9 Mon Sep 17 00:00:00 2001 From: roottool Date: Fri, 25 Jan 2019 23:17:05 +0900 Subject: [PATCH 146/159] changed default editor font #1995 --- browser/main/lib/ConfigManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 059589d0..8512ce26 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -41,7 +41,7 @@ export const DEFAULT_CONFIG = { theme: 'base16-light', keyMap: 'sublime', fontSize: '14', - fontFamily: win ? 'Segoe UI' : 'Monaco, Consolas', + fontFamily: win ? 'Consolas' : 'Monaco', indentType: 'space', indentSize: '2', enableRulers: false, From 1683d63f33337f87386e9c28f3b439eefff84bc4 Mon Sep 17 00:00:00 2001 From: roottool Date: Sat, 26 Jan 2019 01:03:37 +0900 Subject: [PATCH 147/159] Modified indent --- browser/components/MarkdownPreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 8ee90e37..e93312ef 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -845,8 +845,8 @@ export default class MarkdownPreview extends React.Component { const zoomImgWidth = img.width * magnification const zoomImgHeight = img.height * magnification - const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2 - const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2 + const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2 + const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2 const originalImgTop = img.y + rect.top const originalImgLeft = img.x + rect.left const originalImgRect = { From 4b1469748be941259adc040cb29c0150498e360c Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Sat, 26 Jan 2019 12:25:05 +0900 Subject: [PATCH 148/159] v0.11.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 95b4ecd7..c448264d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "boost", "productName": "Boostnote", - "version": "0.11.12", + "version": "0.11.13", "main": "index.js", "description": "Boostnote", "license": "GPL-3.0", From 79254a562fa7bf093ae68c554dee6c0963217eba Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Sat, 26 Jan 2019 12:29:20 +0900 Subject: [PATCH 149/159] Update contributors --- package.json | 5 +++-- readme.md | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c448264d..62a0ced8 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "storage", "electron" ], - "author": "Dick Choi (https://github.com/Rokt33r)", + "author": "Junyoung Choi (https://github.com/Rokt33r)", "contributors": [ "Kazu Yokomizo (https://github.com/kazup01)", "dojineko (https://github.com/dojineko)", @@ -41,7 +41,8 @@ "Yoshihisa Mochihara (https://github.com/yosmoc)", "Mike Resoli (https://github.com/mikeres0)", "tjado (https://github.com/tejado)", - "Sota Sugiura (https://github.com/sota1235)" + "Sota Sugiura (https://github.com/sota1235)", + "Milo Todt (https://github.com/MiloTodt)" ], "bugs": { "url": "https://github.com/BoostIO/Boostnote/issues" diff --git a/readme.md b/readme.md index d1eb710b..5657cb0a 100644 --- a/readme.md +++ b/readme.md @@ -12,6 +12,7 @@

    ## Authors & Maintainers + - [Rokt33r](https://github.com/rokt33r) - [Sosuke](https://github.com/sosukesuzuki) - [Kazz](https://github.com/kazup01) @@ -23,7 +24,7 @@ Thank you to all the people who have contributed to Boostnote! ## Supporting Boostnote -Boostnote is an open source project. It's an independent project with its ongoing development made possible thanks to the support by our amazing backers. +Boostnote is an open source project. It's an independent project with its ongoing development made possible thanks to the support by our amazing backers. Issues on Boostnote can be funded by anyone and the money will be distributed to contributors and maintainers. If you use Boostnote please consider becoming a backer: From 74535c9cba3eec9b037f85d8c3d03220747cc518 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Sat, 26 Jan 2019 12:30:22 +0900 Subject: [PATCH 150/159] Update yarn.lock --- yarn.lock | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 604880e5..c6fce749 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4337,6 +4337,11 @@ he@^1.1.1: version "1.1.1" resolved "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" +highlight.js@^9.13.1: + version "9.13.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" + integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== + highlight.js@^9.3.0: version "9.12.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" @@ -4652,6 +4657,11 @@ invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2: dependencies: loose-envify "^1.0.0" +invert-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-color/-/invert-color-2.0.0.tgz#894ab1f7494a6e45f5e74c2f99ec042cd67dd23e" + integrity sha512-9s6IATlhOAr0/0MPUpLdMpk81ixIu8IqwPwORssXBauFT/4ff/iyEOcojd0UYuPwkDbJvL1+blIZGhqVIaAm5Q== + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -6316,9 +6326,10 @@ mousetrap-global-bind@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mousetrap-global-bind/-/mousetrap-global-bind-1.1.0.tgz#cd7de9222bd0646fa2e010d54c84a74c26a88edd" -mousetrap@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.1.tgz#2a085f5c751294c75e7e81f6ec2545b29cbf42d9" +mousetrap@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.2.tgz#caadd9cf886db0986fb2fee59a82f6bd37527587" + integrity sha512-jDjhi7wlHwdO6q6DS7YRmSHcuI+RVxadBkLt3KHrhd3C2b+w5pKefg3oj5beTcHZyVFA9Aksf+yEE1y5jxUjVA== ms@2.0.0: version "2.0.0" From a113b99de0eb37f3f2ac09fa09a55d007e882097 Mon Sep 17 00:00:00 2001 From: ehhc Date: Sat, 26 Jan 2019 17:21:53 +0100 Subject: [PATCH 151/159] Strange url-handling reverted + tests modified so that they might find that issue in the future + test modified so that they both contain tests for posix and windows path separator -> fixes #2834 --- .../main/lib/dataApi/attachmentManagement.js | 15 +- tests/dataApi/attachmentManagement.test.js | 161 +++++++++++------- 2 files changed, 105 insertions(+), 71 deletions(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 6a0315f7..1d1d36ac 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -227,15 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ function fixLocalURLS (renderedHTML, storagePath) { - /* - A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`. - - - `STORAGE_FOLDER_PLACEHOLDER` will match `:storage` - - `(?:(?:\\\/|%5C)[\\w.]+)+` will match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg` - - `(?:\\\/|%5C)[\\w.]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg` - - `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows. - */ - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[\\w.]+)+', 'g'), function (match) { + return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?"', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER)) }) @@ -429,7 +421,10 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { * @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) + return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) { + var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') + return match.replace(encodedPathSeparators, path.sep) + }).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) } /** diff --git a/tests/dataApi/attachmentManagement.test.js b/tests/dataApi/attachmentManagement.test.js index a4cc8082..470df436 100644 --- a/tests/dataApi/attachmentManagement.test.js +++ b/tests/dataApi/attachmentManagement.test.js @@ -171,6 +171,7 @@ it('should replace the all ":storage" path with the actual storage path', functi it('should replace the ":storage" path with the actual storage path when they have different path separators', function () { const storageFolder = systemUnderTest.DESTINATION_FOLDER + const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' const testInput = '\n' + ' \n' + @@ -179,10 +180,10 @@ it('should replace the ":storage" path with the actual storage path when they ha ' \n' + '

    Headline

    \n' + '

    \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

    \n' + '

    \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

    \n' + ' \n' + '' @@ -195,10 +196,10 @@ it('should replace the ":storage" path with the actual storage path when they ha ' \n' + '

    Headline

    \n' + '

    \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

    \n' + '

    \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

    \n' + ' \n' + '' @@ -251,28 +252,17 @@ it('should test that getAttachmentsInMarkdownContent finds all attachments when it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function () { const dummyStoragePath = 'dummyStoragePath' - const testInput = - '\n' + - ' \n' + - ' //header\n' + - ' \n' + - ' \n' + - '

    Headline

    \n' + - '

    \n' + - ' dummyImage.png\n' + - '

    \n' + - '

    \n' + - ' dummyPDF.pdf\n' + - '

    \n' + - '

    \n' + - ' dummyImage2.jpg\n' + - '

    \n' + - ' \n' + - '' + const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + const testInput = '"# Test\n' + + '\n' + + '![Screenshot1](:storage' + path.win32.sep + noteKey + path.win32.sep + '0.6r4zdgc22xp.png)\n' + + '![Screenshot2](:storage' + path.posix.sep + noteKey + path.posix.sep + '0.q2i4iw0fyx.pdf)\n' + + '![Screenshot3](:storage' + path.win32.sep + noteKey + path.posix.sep + 'd6c5ee92.jpg)"' + const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent(testInput, dummyStoragePath) - const expected = [dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '0.6r4zdgc22xp.png', - dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '0.q2i4iw0fyx.pdf', - dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + 'd6c5ee92.jpg'] + const expected = [dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.6r4zdgc22xp.png', + dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.q2i4iw0fyx.pdf', + dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + 'd6c5ee92.jpg'] expect(actual).toEqual(expect.arrayContaining(expected)) }) @@ -287,13 +277,13 @@ it('should remove the all ":storage" and noteKey references', function () { ' \n' + '

    Headline

    \n' + '

    \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

    \n' + '

    \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

    \n' + '

    \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

    \n' + ' \n' + '' @@ -323,8 +313,8 @@ it('should make sure that "removeStorageAndNoteReferences" works with markdown c const noteKey = 'noteKey' const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + noteKey + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + noteKey + path.sep + 'pdf.pdf](pdf})' + '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + noteKey + path.win32.sep + 'image.jpg](imageName}) \n' + + '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + noteKey + path.posix.sep + 'pdf.pdf](pdf})' const expectedOutput = 'Test input' + @@ -476,8 +466,8 @@ it('should test that moveAttachments returns a correct modified content version' const newNoteKey = 'newNoteKey' const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNoteKey + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNoteKey + path.sep + 'pdf.pdf](pdf})' + '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNoteKey + path.win32.sep + 'image.jpg](imageName}) \n' + + '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNoteKey + path.posix.sep + 'pdf.pdf](pdf})' const expectedOutput = 'Test input' + '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + 'image.jpg](imageName}) \n' + @@ -492,8 +482,8 @@ it('should test that cloneAttachments modifies the content of the new note corre const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'} const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})' + '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNote.key + path.win32.sep + 'image.jpg](imageName}) \n' + + '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})' newNote.content = testInput findStorage.findStorage = jest.fn() findStorage.findStorage.mockReturnValue({path: 'dummyStoragePath'}) @@ -516,8 +506,8 @@ it('should test that cloneAttachments finds all attachments and copies them to t const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'MARKDOWN_NOTE'} const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + oldNote.key + path.sep + 'pdf.pdf](pdf})' + '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNote.key + path.win32.sep + 'image.jpg](imageName}) \n' + + '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})' oldNote.content = testInput newNote.content = testInput @@ -566,14 +556,22 @@ it('should test that isAttachmentLink works correctly', function () { expect(systemUnderTest.isAttachmentLink('text')).toBe(false) expect(systemUnderTest.isAttachmentLink('text [linkText](link)')).toBe(false) expect(systemUnderTest.isAttachmentLink('text ![linkText](link)')).toBe(false) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf )')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + 'noteKey' + path.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) + expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf )')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) + expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) + expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf )')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) + expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) + expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) }) it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function () { @@ -581,7 +579,7 @@ it('should test that handleAttachmentLinkPaste copies the attachments to the new findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' const storageKey = 'storageKey' const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') @@ -596,12 +594,53 @@ it('should test that handleAttachmentLinkPaste copies the attachments to the new }) }) -it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist', function () { +it('should test that handleAttachmentLinkPaste copies the attachments to the new location - win32 path', function () { const dummyStorage = {path: 'dummyStoragePath'} findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' + const storageKey = 'storageKey' + const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + + sander.exists = jest.fn(() => Promise.resolve(true)) + systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + + return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(() => { + expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) + expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePath, storageKey, newNoteKey) + }) +}) + +it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist - win32 path', function () { + const dummyStorage = {path: 'dummyStoragePath'} + findStorage.findStorage = jest.fn(() => dummyStorage) + const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' + const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' + const storageKey = 'storageKey' + const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + + sander.exists = jest.fn(() => Promise.resolve(false)) + systemUnderTest.copyAttachment = jest.fn() + systemUnderTest.generateFileNotFoundMarkdown = jest.fn() + + return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(() => { + expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) + expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) + expect(systemUnderTest.copyAttachment).not.toHaveBeenCalled() + }) +}) + +it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist -- posix', function () { + const dummyStorage = {path: 'dummyStoragePath'} + findStorage.findStorage = jest.fn(() => dummyStorage) + const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' + const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' const storageKey = 'storageKey' const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') @@ -622,8 +661,8 @@ it('should test that handleAttachmentLinkPaste copies multiple attachments if mu findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf) ..' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'img.jpg)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) ..' + + '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' const storageKey = 'storageKey' const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg') @@ -647,7 +686,7 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyNewFileName = 'dummyNewFileName' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' const expectedText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileName + ')' const storageKey = 'storageKey' @@ -667,8 +706,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyNewFileNameOne = 'dummyNewFileName' const dummyNewFileNameTwo = 'dummyNewFileNameTwo' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf) ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'img.jpg)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) ' + + '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' const expectedText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileNameOne + ') ' + '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileNameTwo + ')' const storageKey = 'storageKey' @@ -689,8 +728,8 @@ it('should test that handleAttachmentLinkPaste calls the copy method correct if findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf) ..' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'img.jpg)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) ..' + + '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' const storageKey = 'storageKey' const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg') @@ -716,7 +755,7 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt.png](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf)' + const pasteText = 'text ![alt.png](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' const storageKey = 'storageKey' const fileNotFoundMD = 'file not found' const expectedPastText = 'text ' + fileNotFoundMD @@ -735,8 +774,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf) ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'img.jpg)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) ' + + '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' const storageKey = 'storageKey' const fileNotFoundMD = 'file not found' const expectedPastText = 'text ' + fileNotFoundMD + ' ' + fileNotFoundMD @@ -757,8 +796,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyFoundFileName = 'dummyFileName' const fileNotFoundMD = 'file not found' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf) .. ' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'img.jpg)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) .. ' + + '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' const storageKey = 'storageKey' const expectedPastText = 'text ' + fileNotFoundMD + ' .. ![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyFoundFileName + ')' @@ -781,8 +820,8 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyFoundFileName = 'dummyFileName' const fileNotFoundMD = 'file not found' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'pdf.pdf) .. ' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + pastedNoteKey + path.sep + 'img.jpg)' + const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) .. ' + + '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' const storageKey = 'storageKey' const expectedPastText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyFoundFileName + ') .. ' + fileNotFoundMD From 127da402567ec59177f230ccf73391e8ed2487c0 Mon Sep 17 00:00:00 2001 From: ehhc Date: Sat, 26 Jan 2019 17:37:04 +0100 Subject: [PATCH 152/159] possibly fix for the broken test... --- browser/main/lib/dataApi/attachmentManagement.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 1d1d36ac..0ee4c03b 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -423,8 +423,8 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { function removeStorageAndNoteReferences (input, noteKey) { return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') - return match.replace(encodedPathSeparators, path.sep) - }).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) + return match.replace(encodedPathSeparators, path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) + }) } /** From 472496d59c3791c46d2be5aaa37d44416e7497ab Mon Sep 17 00:00:00 2001 From: ehhc Date: Sat, 26 Jan 2019 17:53:31 +0100 Subject: [PATCH 153/159] possibly fix for the broken test... --- browser/main/lib/dataApi/attachmentManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 0ee4c03b..14f8468c 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -422,7 +422,7 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { */ function removeStorageAndNoteReferences (input, noteKey) { return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) { - var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') + var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep) + '|' + path.win32.sep + '|' + path.posix.sep, 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) }) } From 7165c4550b4f722363723fdaa3ae44f3af011163 Mon Sep 17 00:00:00 2001 From: ehhc Date: Sat, 26 Jan 2019 18:16:39 +0100 Subject: [PATCH 154/159] possibly fix for the broken test... --- browser/main/lib/dataApi/attachmentManagement.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 14f8468c..fb3beac1 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -422,8 +422,12 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { */ function removeStorageAndNoteReferences (input, noteKey) { return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) { - var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep) + '|' + path.win32.sep + '|' + path.posix.sep, 'g') - return match.replace(encodedPathSeparators, path.sep).replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) + const temp = match + .replace(new RegExp(mdurl.encode(path.win32.sep), 'g'), path.sep) + .replace(new RegExp(mdurl.encode(path.posix.sep), 'g'), path.sep) + .replace(new RegExp(escapeStringRegexp(path.win32.sep), 'g'), path.sep) + .replace(new RegExp(escapeStringRegexp(path.posix.sep), 'g'), path.sep) + return temp.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) }) } From 5e87ec262790c7fb3ef90915bfa0b00fe195ac93 Mon Sep 17 00:00:00 2001 From: roottool Date: Sun, 27 Jan 2019 22:26:31 +0900 Subject: [PATCH 155/159] Specified MarkdownPreview class --- browser/components/MarkdownPreview.js | 133 +++++++++++++------------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index e93312ef..a6819ce9 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -833,81 +833,80 @@ export default class MarkdownPreview extends React.Component { } ) - document.querySelectorAll('iframe').forEach(item => { - const rect = item.getBoundingClientRect() - const imgList = item.contentWindow.document.body.querySelectorAll('img') - for (const img of imgList) { - img.onclick = () => { - const widthMagnification = document.body.clientWidth / img.width - const heightMagnification = document.body.clientHeight / img.height - const baseOnWidth = widthMagnification < heightMagnification - const magnification = baseOnWidth ? widthMagnification : heightMagnification + const markdownPreviewIframe = document.querySelector('.MarkdownPreview') + const rect = markdownPreviewIframe.getBoundingClientRect() + const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('img') + for (const img of imgList) { + img.onclick = () => { + const widthMagnification = document.body.clientWidth / img.width + const heightMagnification = document.body.clientHeight / img.height + const baseOnWidth = widthMagnification < heightMagnification + const magnification = baseOnWidth ? widthMagnification : heightMagnification - const zoomImgWidth = img.width * magnification - const zoomImgHeight = img.height * magnification - const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2 - const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2 - const originalImgTop = img.y + rect.top - const originalImgLeft = img.x + rect.left - const originalImgRect = { - top: `${originalImgTop}px`, - left: `${originalImgLeft}px`, - width: `${img.width}px`, - height: `${img.height}px` - } - const zoomInImgRect = { - top: `${baseOnWidth ? zoomImgTop : 0}px`, - left: `${baseOnWidth ? 0 : zoomImgLeft}px`, - width: `${zoomImgWidth}px`, - height: `${zoomImgHeight}px` - } - const animationSpeed = 300 + const zoomImgWidth = img.width * magnification + const zoomImgHeight = img.height * magnification + const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2 + const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2 + const originalImgTop = img.y + rect.top + const originalImgLeft = img.x + rect.left + const originalImgRect = { + top: `${originalImgTop}px`, + left: `${originalImgLeft}px`, + width: `${img.width}px`, + height: `${img.height}px` + } + const zoomInImgRect = { + top: `${baseOnWidth ? zoomImgTop : 0}px`, + left: `${baseOnWidth ? 0 : zoomImgLeft}px`, + width: `${zoomImgWidth}px`, + height: `${zoomImgHeight}px` + } + const animationSpeed = 300 - const zoomImg = document.createElement('img') - zoomImg.src = img.src + const zoomImg = document.createElement('img') + zoomImg.src = img.src + zoomImg.style = ` + position: absolute; + top: ${baseOnWidth ? zoomImgTop : 0}px; + left: ${baseOnWidth ? 0 : zoomImgLeft}px; + width: ${zoomImgWidth}; + height: ${zoomImgHeight}px; + ` + zoomImg.animate([ + originalImgRect, + zoomInImgRect + ], animationSpeed) + + const overlay = document.createElement('div') + overlay.style = ` + background-color: rgba(0,0,0,0.5); + cursor: zoom-out; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: ${document.body.clientHeight}px; + z-index: 100; + ` + overlay.onclick = () => { zoomImg.style = ` position: absolute; - top: ${baseOnWidth ? zoomImgTop : 0}px; - left: ${baseOnWidth ? 0 : zoomImgLeft}px; - width: ${zoomImgWidth}; - height: ${zoomImgHeight}px; + top: ${originalImgTop}px; + left: ${originalImgLeft}px; + width: ${img.width}px; + height: ${img.height}px; ` - zoomImg.animate([ - originalImgRect, - zoomInImgRect + const zoomOutImgAnimation = zoomImg.animate([ + zoomInImgRect, + originalImgRect ], animationSpeed) - - const overlay = document.createElement('div') - overlay.style = ` - background-color: rgba(0,0,0,0.5); - cursor: zoom-out; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: ${document.body.clientHeight}px; - z-index: 100; - ` - overlay.onclick = () => { - zoomImg.style = ` - position: absolute; - top: ${originalImgTop}px; - left: ${originalImgLeft}px; - width: ${img.width}px; - height: ${img.height}px; - ` - const zoomOutImgAnimation = zoomImg.animate([ - zoomInImgRect, - originalImgRect - ], animationSpeed) - zoomOutImgAnimation.onfinish = () => overlay.remove() - } - - overlay.appendChild(zoomImg) - document.body.appendChild(overlay) + zoomOutImgAnimation.onfinish = () => overlay.remove() } + + overlay.appendChild(zoomImg) + document.body.appendChild(overlay) } - }) + } } focus () { From a85a27f2252fd4acb6e3b24b01d08f62718864cf Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 29 Jan 2019 16:05:30 +0100 Subject: [PATCH 156/159] - fix bad regex - improve test - fix missing 'e' in some functions name --- browser/components/CodeEditor.js | 2 +- .../main/lib/dataApi/attachmentManagement.js | 18 +++++++++++----- tests/dataApi/attachmentManagement.test.js | 21 +++++++++++++------ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 48634993..01a54aac 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -964,7 +964,7 @@ export default class CodeEditor extends React.Component { } else { const image = clipboard.readImage() if (!image.isEmpty()) { - attachmentManagement.handlePastNativeImage( + attachmentManagement.handlePasteNativeImage( this, storageKey, noteKey, diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index fb3beac1..407fa659 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -227,7 +227,15 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ function fixLocalURLS (renderedHTML, storagePath) { - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?"', 'g'), function (match) { + /* + A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`. + + - `STORAGE_FOLDER_PLACEHOLDER` will match `:storage` + - `(?:(?:\\\/|%5C)[-.\\w]+)+` will match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg` + - `(?:\\\/|%5C)[-.\\w]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg` + - `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows. + */ + return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[-.\\w]+)+', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER)) }) @@ -279,7 +287,7 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { * @param {String} noteKey Key of the current note * @param {DataTransferItem} dataTransferItem Part of the past-event */ -function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) { +function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) { if (!codeEditor) { throw new Error('codeEditor has to be given') } @@ -323,7 +331,7 @@ function handlePastImageEvent (codeEditor, storageKey, noteKey, dataTransferItem * @param {String} noteKey Key of the current note * @param {NativeImage} image The native image */ -function handlePastNativeImage (codeEditor, storageKey, noteKey, image) { +function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { if (!codeEditor) { throw new Error('codeEditor has to be given') } @@ -583,8 +591,8 @@ module.exports = { fixLocalURLS, generateAttachmentMarkdown, handleAttachmentDrop, - handlePastImageEvent, - handlePastNativeImage, + handlePasteImageEvent, + handlePasteNativeImage, getAttachmentsInMarkdownContent, getAbsolutePathsOfAttachmentsInContent, removeStorageAndNoteReferences, diff --git a/tests/dataApi/attachmentManagement.test.js b/tests/dataApi/attachmentManagement.test.js index 470df436..53558de2 100644 --- a/tests/dataApi/attachmentManagement.test.js +++ b/tests/dataApi/attachmentManagement.test.js @@ -128,6 +128,7 @@ it('should test that copyAttachment don\'t uses a random file name if not intend it('should replace the all ":storage" path with the actual storage path', function () { const storageFolder = systemUnderTest.DESTINATION_FOLDER + const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' const testInput = '\n' + ' \n' + @@ -136,14 +137,18 @@ it('should replace the all ":storage" path with the actual storage path', functi ' \n' + '

    Headline

    \n' + '

    \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

    \n' + '

    \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

    \n' + '

    \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

    \n' + + '
    \n' +
    +    '            \n' +
    +    '            \n' +
    +    '        
    \n' + ' \n' + '' const storagePath = '<>' @@ -155,14 +160,18 @@ it('should replace the all ":storage" path with the actual storage path', functi ' \n' + '

    Headline

    \n' + '

    \n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

    \n' + '

    \n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

    \n' + '

    \n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

    \n' + + '
    \n' +
    +    '            \n' +
    +    '            \n' +
    +    '        
    \n' + ' \n' + '' const actual = systemUnderTest.fixLocalURLS(testInput, storagePath) From b1c6c0442ffeecd9153c030eea669f19d73accce Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 29 Jan 2019 16:49:51 +0100 Subject: [PATCH 157/159] fix guardrails errors --- browser/components/CodeEditor.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 01a54aac..6ad294ed 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -888,10 +888,7 @@ export default class CodeEditor extends React.Component { handlePaste (editor, forceSmartPaste) { const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props - const isURL = str => { - const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ - return matcher.test(str) - } + const isURL = str => /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str) const isInLinkTag = editor => { const startCursor = editor.getCursor('start') @@ -1109,7 +1106,7 @@ export default class CodeEditor extends React.Component { iconv.encodingExists(_charset) ? _charset : 'utf-8' - resolve(iconv.decode(new Buffer(buff), charset).toString()) + resolve(iconv.decode(Buffer.from(buff), charset).toString()) } catch (e) { reject(e) } From 806139091c78129480a2c5ecfed29e14158da2b3 Mon Sep 17 00:00:00 2001 From: Aaron-Bird <25508292+Aaron-Bird@users.noreply.github.com> Date: Thu, 31 Jan 2019 01:08:07 +0800 Subject: [PATCH 158/159] fix: GIFs don't animate --- browser/main/lib/dataApi/attachmentManagement.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index fb3beac1..aed4a58d 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -258,8 +258,9 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { const originalFileName = path.basename(filePath) const fileType = file['type'] const isImage = fileType.startsWith('image') + const isGif = fileType.endsWith('gif') let promise - if (isImage) { + if (isImage && !isGif) { promise = fixRotate(file).then(base64data => { return copyAttachment({type: 'base64', data: base64data, sourceFilePath: filePath}, storageKey, noteKey) }) From 62ab444b29f6d43de24abdedfb25dfc7f920aa10 Mon Sep 17 00:00:00 2001 From: Katsuki Date: Thu, 31 Jan 2019 19:33:43 +0900 Subject: [PATCH 159/159] Fix grammer error --- browser/main/modals/PreferencesModal/Crowdfunding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/Crowdfunding.js b/browser/main/modals/PreferencesModal/Crowdfunding.js index f6389cd8..f94ee5ca 100644 --- a/browser/main/modals/PreferencesModal/Crowdfunding.js +++ b/browser/main/modals/PreferencesModal/Crowdfunding.js @@ -34,7 +34,7 @@ class Crowdfunding extends React.Component {

    {i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}


    {i18n.__('### We believe Meritocracy')}

    -

    {i18n.__('We think developers who has skill and did great things must be rewarded properly.')}

    +

    {i18n.__('We think developers who have skills and do great things must be rewarded properly.')}

    {i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}

    {i18n.__('It sometimes looks like exploitation.')}

    {i18n.__('We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.')}