From 4242e0d3290a1241627ad36193fd228fb8a82d1b Mon Sep 17 00:00:00 2001 From: Benny O Date: Sat, 13 Oct 2018 19:18:47 +0800 Subject: [PATCH 01/30] Add missing translation for zh-tw --- locales/zh-TW.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 531f43223..c499de546 100755 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -149,6 +149,16 @@ "Only allow secure html tags (recommended)": "只允許安全的 HTML 標籤 (建議)", "Allow styles": "允許樣式", "Allow dangerous html tags": "允許危險的 HTML 標籤", - "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.", - "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠" + "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "將文本箭頭轉換為完整符號。 ⚠ 注意這或會影響 Markdown 的 HTML 注釋。", + "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", + "Default New Note":"預設新筆記類型", + "Show only related tags": "只顯示相關標籤", + "Snippet Default Language": "程式碼片段預設語言", + "Disable Direct Write (It will be applied after restarting)": "停用直接編輯 (重啟後生效)", + "Enable smart table editor": "啟用智能表格編輯器", + "Enable smart quotes": "啟用智能引號", + "Allow line through checkbox": "替標示為完成的選框添加刪除線", + "Custom CSS": "自定義 CSS", + "Allow custom CSS for preview": "允許預覽自定義 CSS", + "Render newlines in Markdown paragraphs as
":"在 Markdown 段落中使用
換行" } From 28007a33a01346c1cb89b2ec9ac92a1b9d67f500 Mon Sep 17 00:00:00 2001 From: Benny O Date: Sat, 13 Oct 2018 19:19:48 +0800 Subject: [PATCH 02/30] Add missing translation for zh-cn --- locales/zh-CN.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/locales/zh-CN.json b/locales/zh-CN.json index a04cffdd4..919d53813 100755 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -205,7 +205,16 @@ "Rename": "重命名", "Folder Name": "文件夹名称", "No tags":"无标签", - "Render newlines in Markdown paragraphs as
":"在 Markdown 段落中使用
换行", - "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.", - "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠" + "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "将文本箭头转换为完整符号。 ⚠ 注意这或会影响 Markdown 的 HTML 注释。", + "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", + "Default New Note":"预设新笔记类型", + "Show only related tags": "只显示相关标签", + "Snippet Default Language": "程式码片段预设语言", + "Disable Direct Write (It will be applied after restarting)": "停用直接编辑 (重启后生效)", + "Enable smart table editor": "启用智能表格编辑器", + "Enable smart quotes": "启用智能引号", + "Allow line through checkbox": "替标示为完成的选框添加删除线", + "Custom CSS": "自定义 CSS", + "Allow custom CSS for preview": "允许预览自定义 CSS", + "Render newlines in Markdown paragraphs as
":"在 Markdown 段落中使用
换行" } From 7fb1a06e1e8666fbecc86ffa0b42fbd49e0cfb97 Mon Sep 17 00:00:00 2001 From: Daniel Mouritzen Date: Sat, 27 Oct 2018 18:18:51 +0200 Subject: [PATCH 03/30] Add ~ and _ to autoclosing brackets --- browser/components/CodeEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 9953f9bfa..11d45ff34 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -214,7 +214,7 @@ export default class CodeEditor extends React.Component { foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], autoCloseBrackets: { - pairs: '()[]{}\'\'""$$**``', + pairs: '()[]{}\'\'""$$**``~~__', triples: '```"""\'\'\'', explode: '[]{}``$$', override: true From d6fe0df24fc546d2d7e440377886e67185da64f0 Mon Sep 17 00:00:00 2001 From: Daniel Mouritzen Date: Sat, 27 Oct 2018 18:22:03 +0200 Subject: [PATCH 04/30] Add ~ and _ to autoclosing brackets --- browser/main/modals/PreferencesModal/SnippetEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index 4ce5dc34b..c72293e44 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -28,7 +28,7 @@ class SnippetEditor extends React.Component { foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], autoCloseBrackets: { - pairs: '()[]{}\'\'""$$**``', + pairs: '()[]{}\'\'""$$**``~~__', triples: '```"""\'\'\'', explode: '[]{}``$$', override: true From 817b74cc7fcbbc94c0f6f92ffd9dfafe8f8d6a2c Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sun, 11 Nov 2018 11:18:57 +0800 Subject: [PATCH 05/30] Update locales/zh-TW.json Co-Authored-By: opw0011 --- locales/zh-TW.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/zh-TW.json b/locales/zh-TW.json index c499de546..b80b42879 100755 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -149,7 +149,7 @@ "Only allow secure html tags (recommended)": "只允許安全的 HTML 標籤 (建議)", "Allow styles": "允許樣式", "Allow dangerous html tags": "允許危險的 HTML 標籤", - "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "將文本箭頭轉換為完整符號。 ⚠ 注意這或會影響 Markdown 的 HTML 注釋。", + "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "將文本箭頭轉換為完整符號。 ⚠ 注意這會影響 Markdown 的 HTML 注釋。", "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Default New Note":"預設新筆記類型", "Show only related tags": "只顯示相關標籤", From 0904c62a2d2193bfe1c6207244fed46c35463fe7 Mon Sep 17 00:00:00 2001 From: Ivan Chen Date: Sun, 11 Nov 2018 11:19:38 +0800 Subject: [PATCH 06/30] Update locales/zh-CN.json Co-Authored-By: opw0011 --- locales/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 919d53813..88a969b33 100755 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -205,7 +205,7 @@ "Rename": "重命名", "Folder Name": "文件夹名称", "No tags":"无标签", - "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "将文本箭头转换为完整符号。 ⚠ 注意这或会影响 Markdown 的 HTML 注释。", + "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "将文本箭头转换为完整符号。 ⚠ 注意这会影响 Markdown 的 HTML 注释。", "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", "Default New Note":"预设新笔记类型", "Show only related tags": "只显示相关标签", From 7c1cd50defdde200eeb8300fc7336ac47f77a6d0 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 13:12:22 -0500 Subject: [PATCH 07/30] Add structure for exporting PDFs --- browser/components/MarkdownPreview.js | 9 +++++++++ browser/main/Detail/InfoPanel.js | 8 +++++++- browser/main/Detail/InfoPanelTrashed.js | 7 ++++--- browser/main/Detail/MarkdownNoteDetail.js | 6 ++++++ browser/main/NoteList/index.js | 1 + lib/main-menu.js | 7 +++++++ locales/da.json | 1 + locales/de.json | 1 + locales/en.json | 1 + locales/es-ES.json | 1 + locales/fa.json | 1 + locales/fr.json | 1 + locales/hu.json | 1 + locales/it.json | 1 + locales/ja.json | 1 + locales/ko.json | 1 + locales/no.json | 1 + locales/pl.json | 1 + locales/pt-BR.json | 1 + locales/pt-PT.json | 1 + locales/ru.json | 1 + locales/sq.json | 1 + locales/th.json | 1 + locales/tr.json | 1 + locales/zh-CN.json | 1 + locales/zh-TW.json | 1 + 26 files changed, 54 insertions(+), 4 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 17d2cb82d..87d1d02a0 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -205,6 +205,7 @@ export default class MarkdownPreview extends React.Component { this.saveAsTextHandler = () => this.handleSaveAsText() this.saveAsMdHandler = () => this.handleSaveAsMd() this.saveAsHtmlHandler = () => this.handleSaveAsHtml() + this.saveAsPdfHandler = () => this.handleSaveAsPdf() this.printHandler = () => this.handlePrint() this.linkClickHandler = this.handlelinkClick.bind(this) @@ -349,6 +350,12 @@ export default class MarkdownPreview extends React.Component { }) } + handleSaveAsPdf () { + this.exportAsDocument('pdf', (noteContent, exportTasks) => { + // Return pdf source + }) + } + handlePrint () { this.refs.root.contentWindow.print() } @@ -460,6 +467,7 @@ export default class MarkdownPreview extends React.Component { eventEmitter.on('export:save-text', this.saveAsTextHandler) eventEmitter.on('export:save-md', this.saveAsMdHandler) eventEmitter.on('export:save-html', this.saveAsHtmlHandler) + eventEmitter.on('export:save-pdf', this.saveAsPdfHandler) eventEmitter.on('print', this.printHandler) } @@ -495,6 +503,7 @@ export default class MarkdownPreview extends React.Component { eventEmitter.off('export:save-text', this.saveAsTextHandler) eventEmitter.off('export:save-md', this.saveAsMdHandler) eventEmitter.off('export:save-html', this.saveAsHtmlHandler) + eventEmitter.off('export:save-pdf', this.saveAsPdfHandler) eventEmitter.off('print', this.printHandler) } diff --git a/browser/main/Detail/InfoPanel.js b/browser/main/Detail/InfoPanel.js index 15535186a..8fe0a8554 100644 --- a/browser/main/Detail/InfoPanel.js +++ b/browser/main/Detail/InfoPanel.js @@ -14,7 +14,7 @@ class InfoPanel extends React.Component { render () { const { - storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, wordCount, letterCount, type, print + storageName, folderName, noteLink, updatedAt, createdAt, exportAsMd, exportAsTxt, exportAsHtml, exportAsPdf, wordCount, letterCount, type, print } = this.props return (
@@ -85,6 +85,11 @@ class InfoPanel extends React.Component {

{i18n.__('.html')}

+ + - @@ -61,7 +61,8 @@ InfoPanelTrashed.propTypes = { createdAt: PropTypes.string.isRequired, exportAsMd: PropTypes.func.isRequired, exportAsTxt: PropTypes.func.isRequired, - exportAsHtml: PropTypes.func.isRequired + exportAsHtml: PropTypes.func.isRequired, + exportAsPdf: PropTypes.func.isRequired } export default CSSModules(InfoPanelTrashed, styles) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index b4e7a5b34..08d8bfbdf 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -190,6 +190,10 @@ class MarkdownNoteDetail extends React.Component { ee.emit('export:save-html') } + exportAsPdf () { + ee.emit('export:save-pdf') + } + handleKeyDown (e) { switch (e.keyCode) { // tab key @@ -411,6 +415,7 @@ class MarkdownNoteDetail extends React.Component { exportAsHtml={this.exportAsHtml} exportAsMd={this.exportAsMd} exportAsTxt={this.exportAsTxt} + exportAsPdf={this.exportAsPdf} />
@@ -476,6 +481,7 @@ class MarkdownNoteDetail extends React.Component { exportAsMd={this.exportAsMd} exportAsTxt={this.exportAsTxt} exportAsHtml={this.exportAsHtml} + exportAsPdf={this.exportAsPdf} wordCount={note.content.split(' ').length} letterCount={note.content.replace(/\r?\n/g, '').length} type={note.type} diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 31efaede5..b5748fd4d 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -500,6 +500,7 @@ class NoteList extends React.Component { 'export-txt': 'Text export', 'export-md': 'Markdown export', 'export-html': 'HTML export', + 'export-pdf': 'PDF export', 'print': 'Print' })[msg] diff --git a/lib/main-menu.js b/lib/main-menu.js index eb08273a4..012c9ea37 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -116,6 +116,13 @@ const file = { mainWindow.webContents.send('list:isMarkdownNote', 'export-html') mainWindow.webContents.send('export:save-html') } + }, + { + label: 'PDF (.pdf)', + click () { + mainWindow.webContents.send('list:isMarkdownNote', 'export-pdf') + mainWindow.webContents.send('export:save-pdf') + } } ] }, diff --git a/locales/da.json b/locales/da.json index da5843f31..5d1a392b8 100644 --- a/locales/da.json +++ b/locales/da.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Print", "Your preferences for Boostnote": "Your preferences for Boostnote", "Storage Locations": "Storage Locations", diff --git a/locales/de.json b/locales/de.json index 1b90ab635..9177adec6 100644 --- a/locales/de.json +++ b/locales/de.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Drucken", "Your preferences for Boostnote": "Boostnote Einstellungen", "Storage Locations": "Speicherverwaltung", diff --git a/locales/en.json b/locales/en.json index fe7931da5..831789c1e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -19,6 +19,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Print", "Your preferences for Boostnote": "Your preferences for Boostnote", "Help": "Help", diff --git a/locales/es-ES.json b/locales/es-ES.json index 8b2da1b79..3bd2cb494 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Imprimir", "Your preferences for Boostnote": "Tus preferencias para Boostnote", "Storage Locations": "Almacenamientos", diff --git a/locales/fa.json b/locales/fa.json index 18bef679d..8f702767a 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "پرینت", "Your preferences for Boostnote": "تنظیمات شما برای boostnote", "Storage Locations": "ذخیره سازی", diff --git a/locales/fr.json b/locales/fr.json index ea5a1c057..35d2f3c8b 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Imprimer", "Your preferences for Boostnote": "Vos préférences pour Boostnote", "Storage Locations": "Stockages", diff --git a/locales/hu.json b/locales/hu.json index 77bdb2abf..e76f06c9f 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -19,6 +19,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Nyomtatás", "Your preferences for Boostnote": "Boostnote beállításaid", "Help": "Súgó", diff --git a/locales/it.json b/locales/it.json index 05f454f36..69ddd380f 100644 --- a/locales/it.json +++ b/locales/it.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Stampa", "Your preferences for Boostnote": "Le tue preferenze per Boostnote", "Storage Locations": "Posizioni", diff --git a/locales/ja.json b/locales/ja.json index e33bbaa63..b96183813 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -19,6 +19,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "印刷", "Your preferences for Boostnote": "Boostnoteの個人設定", "Help": "ヘルプ", diff --git a/locales/ko.json b/locales/ko.json index 9a8bf8c75..59ffe7b9c 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "인쇄", "Your preferences for Boostnote": "Boostnote 설정", "Storage Locations": "저장소", diff --git a/locales/no.json b/locales/no.json index 2d6c92f54..66109a34b 100644 --- a/locales/no.json +++ b/locales/no.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Print", "Your preferences for Boostnote": "Your preferences for Boostnote", "Storage Locations": "Storage Locations", diff --git a/locales/pl.json b/locales/pl.json index 68719aef0..577f46e68 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -19,6 +19,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Drukuj", "Help": "Pomoc", "Your preferences for Boostnote": "Twoje ustawienia dla Boostnote", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 6b3126cce..6072b0236 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Imprimir", "Your preferences for Boostnote": "Suas preferências para o Boostnote", "Storage Locations": "Armazenamentos", diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 774919a25..5beb18dd4 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Imprimir", "Your preferences for Boostnote": "As tuas definiçōes para Boostnote", "Storage Locations": "Locais de Armazenamento", diff --git a/locales/ru.json b/locales/ru.json index 793e15116..c4e9e1a20 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Print", "Your preferences for Boostnote": "Настройки Boostnote", "Storage Locations": "Хранилища", diff --git a/locales/sq.json b/locales/sq.json index e4cc01ac9..80f737a34 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Print", "Your preferences for Boostnote": "Your preferences for Boostnote", "Storage Locations": "Storage Locations", diff --git a/locales/th.json b/locales/th.json index 49d8e7cd7..358d49ab4 100644 --- a/locales/th.json +++ b/locales/th.json @@ -19,6 +19,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "พิมพ์", "Your preferences for Boostnote": "การตั้งค่าของคุณสำหรับ Boostnote", "Help": "ช่วยเหลือ", diff --git a/locales/tr.json b/locales/tr.json index 03a9791de..077fa1e4e 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "Yazdır", "Your preferences for Boostnote": "Boostnote tercihleriniz", "Storage Locations": "Saklama Alanları", diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 2e12323e3..8e5537d26 100755 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "打印", "Your preferences for Boostnote": "个性设置", "Storage Locations": "本地存储", diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 93a8afd51..add73073b 100755 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -18,6 +18,7 @@ ".md": ".md", ".txt": ".txt", ".html": ".html", + ".pdf": ".pdf", "Print": "列印", "Your preferences for Boostnote": "Boostnote 偏好設定", "Storage Locations": "儲存空間", From c1deeaf5f759e590c91e125061b5d9c1f8702c37 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 14:40:52 -0500 Subject: [PATCH 08/30] Added PDF error to SnippetNoteDetail --- browser/main/Detail/SnippetNoteDetail.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 4a5076da5..2e93ea25a 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -650,6 +650,7 @@ class SnippetNoteDetail extends React.Component { 'export-txt': 'Text export', 'export-md': 'Markdown export', 'export-html': 'HTML export', + 'export-pdf': 'PDF export', 'print': 'Print' })[msg] @@ -757,6 +758,7 @@ class SnippetNoteDetail extends React.Component { exportAsMd={this.showWarning} exportAsTxt={this.showWarning} exportAsHtml={this.showWarning} + exportAsPdf={this.showWarning} /> @@ -808,6 +810,7 @@ class SnippetNoteDetail extends React.Component { exportAsMd={this.showWarning} exportAsTxt={this.showWarning} exportAsHtml={this.showWarning} + exportAsPdf={this.showWarning} type={note.type} print={this.showWarning} /> From 33d0a9d3b3b67a474ea9f00d903fa059a7b21180 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 15:49:41 -0500 Subject: [PATCH 09/30] extract html contentformatter --- browser/components/MarkdownPreview.js | 106 ++++++++++++++------------ 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 87d1d02a0..3c8b7c543 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -23,7 +23,7 @@ import i18n from 'browser/lib/i18n' import fs from 'fs' import ConfigManager from '../main/lib/ConfigManager' -const { remote, shell } = require('electron') +const { remote, shell, BrowserWindow } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') const { app } = remote @@ -295,64 +295,70 @@ export default class MarkdownPreview extends React.Component { this.exportAsDocument('md') } - handleSaveAsHtml () { - this.exportAsDocument('html', (noteContent, exportTasks) => { - const { - fontFamily, - fontSize, - codeBlockFontFamily, - lineNumber, - codeBlockTheme, - scrollPastEnd, - theme, - allowCustomCSS, - customCSS - } = this.getStyleParams() - - const inlineStyles = buildStyle( - fontFamily, - fontSize, - codeBlockFontFamily, - lineNumber, - scrollPastEnd, - theme, - allowCustomCSS, - customCSS - ) - let body = this.markdown.render(noteContent) - const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] - files.forEach(file => { - if (global.process.platform === 'win32') { - file = file.replace('file:///', '') - } else { - file = file.replace('file://', '') - } - exportTasks.push({ - src: file, - dst: 'css' - }) - }) + htmlContentFormatter (noteContent, exportTasks) { + const { + fontFamily, + fontSize, + codeBlockFontFamily, + lineNumber, + codeBlockTheme, + scrollPastEnd, + theme, + allowCustomCSS, + customCSS + } = this.getStyleParams() - let styles = '' - files.forEach(file => { - styles += `` + const inlineStyles = buildStyle( + fontFamily, + fontSize, + codeBlockFontFamily, + lineNumber, + scrollPastEnd, + theme, + allowCustomCSS, + customCSS + ) + let body = this.markdown.render(noteContent) + const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] + files.forEach(file => { + if (global.process.platform === 'win32') { + file = file.replace('file:///', '') + } else { + file = file.replace('file://', '') + } + exportTasks.push({ + src: file, + dst: 'css' }) + }) - return ` - - - - - ${styles} - - ${body} - ` + let styles = '' + files.forEach(file => { + styles += `` }) + + return ` + + + + + ${styles} + + ${body} + ` + } + + handleSaveAsHtml () { + this.exportAsDocument('html', (noteContent, exportTasks) => this.htmlContentFormatter(noteContent, exportTasks)) } handleSaveAsPdf () { this.exportAsDocument('pdf', (noteContent, exportTasks) => { // Return pdf source + // const doc = new jsPDF() + // doc.fromHTML(this.markdown.render(noteContent), 0, 0) + // return doc.output() + }) } From 0a7fd0288cd80fffbdbcc0f30098db43aba499e5 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 16:14:28 -0500 Subject: [PATCH 10/30] Use promises for outputFormatter --- browser/main/lib/dataApi/exportNote.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js index b358e5481..c65697308 100755 --- a/browser/main/lib/dataApi/exportNote.js +++ b/browser/main/lib/dataApi/exportNote.js @@ -44,13 +44,16 @@ function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatt if (outputFormatter) { exportedData = outputFormatter(exportedData, exportTasks) + } else { + exportedData = Promise.resolve(exportedData) } const tasks = prepareTasks(exportTasks, storagePath, path.dirname(targetPath)) return Promise.all(tasks.map((task) => copyFile(task.src, task.dst))) - .then(() => { - return saveToFile(exportedData, targetPath) + .then(() => exportedData) + .then(data => { + return saveToFile(data, targetPath) }).catch((err) => { rollbackExport(tasks) throw err From 660a27850f26193d7d8487515ce117d65c29f8fe Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 16:39:41 -0500 Subject: [PATCH 11/30] Generate PDF through an Electron BrowserWindow --- browser/components/MarkdownPreview.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 3c8b7c543..da022a989 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -23,7 +23,7 @@ import i18n from 'browser/lib/i18n' import fs from 'fs' import ConfigManager from '../main/lib/ConfigManager' -const { remote, shell, BrowserWindow } = require('electron') +const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') const { app } = remote @@ -349,16 +349,21 @@ export default class MarkdownPreview extends React.Component { } handleSaveAsHtml () { - this.exportAsDocument('html', (noteContent, exportTasks) => this.htmlContentFormatter(noteContent, exportTasks)) + this.exportAsDocument('html', (noteContent, exportTasks) => Promise.resolve(this.htmlContentFormatter(noteContent, exportTasks))) } handleSaveAsPdf () { this.exportAsDocument('pdf', (noteContent, exportTasks) => { - // Return pdf source - // const doc = new jsPDF() - // doc.fromHTML(this.markdown.render(noteContent), 0, 0) - // return doc.output() - + const printout = new remote.BrowserWindow({show: false}) + printout.loadURL('data:text/html;charset=UTF-8,'+this.htmlContentFormatter(noteContent, exportTasks)) + return new Promise((resolve, reject) => { + printout.webContents.on('did-finish-load', () => { + printout.webContents.printToPDF({}, (err, data) => { + if (err) reject(err) + else resolve(data) + }) + }) + }) }) } From dceed7d84d1cdcfba2d92fa0c3ee694045e519ae Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 16:51:59 -0500 Subject: [PATCH 12/30] Fixes exportFolder by making it actually wait for each exportNote --- browser/main/lib/dataApi/exportFolder.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/browser/main/lib/dataApi/exportFolder.js b/browser/main/lib/dataApi/exportFolder.js index 771f77dcd..8f15b147c 100644 --- a/browser/main/lib/dataApi/exportFolder.js +++ b/browser/main/lib/dataApi/exportFolder.js @@ -43,19 +43,18 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) { .then(function exportNotes (data) { const { storage, notes } = data - notes + return Promise.all(notes .filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE') - .forEach(note => { + .map(note => { const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`) - exportNote(note.key, storage.path, note.content, notePath, null) + return exportNote(note.key, storage.path, note.content, notePath, null) }) - - return { + ).then(() => ({ storage, folderKey, fileType, exportDir - } + })) }) } From ce9f76fa639d11ce52929418ca10a1e8833d5429 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 16:55:05 -0500 Subject: [PATCH 13/30] fixed code style error --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index da022a989..1eee50052 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -355,7 +355,7 @@ export default class MarkdownPreview extends React.Component { handleSaveAsPdf () { this.exportAsDocument('pdf', (noteContent, exportTasks) => { const printout = new remote.BrowserWindow({show: false}) - printout.loadURL('data:text/html;charset=UTF-8,'+this.htmlContentFormatter(noteContent, exportTasks)) + printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks)) return new Promise((resolve, reject) => { printout.webContents.on('did-finish-load', () => { printout.webContents.printToPDF({}, (err, data) => { From b546b9cbe75bcfd19537f1593930bd65332837f5 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 3 Dec 2018 17:04:04 -0500 Subject: [PATCH 14/30] InfoPanel automatically adjusts its width --- browser/main/Detail/InfoPanel.styl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/Detail/InfoPanel.styl b/browser/main/Detail/InfoPanel.styl index 1f774174c..e9f7f59b9 100644 --- a/browser/main/Detail/InfoPanel.styl +++ b/browser/main/Detail/InfoPanel.styl @@ -15,7 +15,7 @@ right 25px position absolute padding 20px 25px 0 25px - width 300px + // width 300px overflow auto background-color $ui-noteList-backgroundColor box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1) From a2e050b8c5ea5c75e9df7f5b0a1b952b293decf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Vi=E1=BB=87t=20H=C6=B0ng?= Date: Sat, 19 Jan 2019 17:03:59 +0700 Subject: [PATCH 15/30] added jump to line on click preview --- browser/components/MarkdownEditor.js | 10 +++++++++ browser/components/MarkdownPreview.js | 29 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index e31548d0b..c22b2e09d 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -32,6 +32,7 @@ class MarkdownEditor extends React.Component { componentDidMount () { this.value = this.refs.code.value eventEmitter.on('editor:lock', this.lockEditorCode) + eventEmitter.on('editor:focus', this.focusEditor.bind(this)) } componentDidUpdate () { @@ -47,6 +48,15 @@ class MarkdownEditor extends React.Component { componentWillUnmount () { this.cancelQueue() eventEmitter.off('editor:lock', this.lockEditorCode) + eventEmitter.off('editor:focus', this.focusEditor.bind(this)) + } + + focusEditor () { + this.setState({ + status: 'CODE' + }, () => { + this.refs.code.focus() + }) } queueRendering (value) { diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 4c638dc94..ee40a7759 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -192,6 +192,19 @@ const defaultCodeBlockFontFamily = [ 'source-code-pro', 'monospace' ] + +// return the line number of the line that used to generate the specified element +// return -1 if the line is not found +function getSourceLineNumberByElement (element) { + let isHasLineNumber = element.dataset.line !== undefined + let parent = element + while (!isHasLineNumber && parent.parentElement !== null) { + parent = parent.parentElement + isHasLineNumber = parent.dataset.line !== undefined + } + return parent.dataset.line !== undefined ? parseInt(parent.dataset.line) : -1 +} + export default class MarkdownPreview extends React.Component { constructor (props) { super(props) @@ -271,6 +284,22 @@ export default class MarkdownPreview extends React.Component { if (config.editor.switchPreview === 'RIGHTCLICK' && e.buttons === 2 && config.editor.type === 'SPLIT') { eventEmitter.emit('topbar:togglemodebutton', 'CODE') } + if (e.ctrlKey) { + if (config.editor.type === 'SPLIT') { + const clickElement = e.target + const lineNumber = getSourceLineNumberByElement(clickElement) + if (lineNumber !== -1) { + eventEmitter.emit('line:jump', lineNumber) + } + } else { + const clickElement = e.target + const lineNumber = getSourceLineNumberByElement(clickElement) + if (lineNumber !== -1) { + eventEmitter.emit('editor:focus') + eventEmitter.emit('line:jump', lineNumber) + } + } + } if (e.target != null) { switch (e.target.tagName) { case 'A': From 5d38937f34a6ca34b9875ab06421a76ad68c4e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Vi=E1=BB=87t=20H=C6=B0ng?= Date: Wed, 23 Jan 2019 00:20:50 +0700 Subject: [PATCH 16/30] scroll selected line to middle of the editor --- browser/components/CodeEditor.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 486349934..05da120b8 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -835,6 +835,9 @@ export default class CodeEditor extends React.Component { ch: 1 } this.editor.setCursor(cursor) + const top = this.editor.charCoords({line: num, ch: 0}, 'local').top + const middleHeight = this.editor.getScrollerElement().offsetHeight / 2 + this.editor.scrollTo(null, top - middleHeight - 5) } focus () { From d144a5884ac06edfb2afe1c56cf01267e46c287f Mon Sep 17 00:00:00 2001 From: David Dreher Date: Wed, 6 Feb 2019 22:11:35 +0100 Subject: [PATCH 17/30] fix for issue #2859: Cloning a note will now also copy the properties description, snippets, tags and isStarred --- browser/main/NoteList/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 33a0adf08..9d62e7171 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -716,7 +716,11 @@ class NoteList extends React.Component { folder: folder.key, title: firstNote.title + ' ' + i18n.__('copy'), content: firstNote.content, - linesHighlighted: firstNote.linesHighlighted + linesHighlighted: firstNote.linesHighlighted, + description: firstNote.description, + snippets: firstNote.snippets, + tags: firstNote.tags, + isStarred: firstNote.isStarred }) .then((note) => { attachmentManagement.cloneAttachments(firstNote, note) From 4d727b0af7a00193b046cb9c20e53a3dd74aaa49 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 11 Feb 2019 10:39:51 -0500 Subject: [PATCH 18/30] destroy window after printing --- browser/components/MarkdownPreview.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 1eee50052..6307abf40 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -361,6 +361,7 @@ export default class MarkdownPreview extends React.Component { printout.webContents.printToPDF({}, (err, data) => { if (err) reject(err) else resolve(data) + printout.destroy() }) }) }) From bc24acd057d06a691af93c701e0da839cb1fbd20 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 11 Feb 2019 11:10:10 -0500 Subject: [PATCH 19/30] add targetDir parameter to outputFormatter --- browser/components/MarkdownPreview.js | 11 ++++++----- browser/main/lib/dataApi/exportNote.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 6307abf40..7a814202c 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -295,7 +295,7 @@ export default class MarkdownPreview extends React.Component { this.exportAsDocument('md') } - htmlContentFormatter (noteContent, exportTasks) { + htmlContentFormatter (noteContent, exportTasks, targetDir) { const { fontFamily, fontSize, @@ -339,6 +339,7 @@ export default class MarkdownPreview extends React.Component { return ` + @@ -349,13 +350,13 @@ export default class MarkdownPreview extends React.Component { } handleSaveAsHtml () { - this.exportAsDocument('html', (noteContent, exportTasks) => Promise.resolve(this.htmlContentFormatter(noteContent, exportTasks))) + this.exportAsDocument('html', (noteContent, exportTasks, targetDir) => Promise.resolve(this.htmlContentFormatter(noteContent, exportTasks, targetDir))) } handleSaveAsPdf () { - this.exportAsDocument('pdf', (noteContent, exportTasks) => { - const printout = new remote.BrowserWindow({show: false}) - printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks)) + this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => { + const printout = new remote.BrowserWindow({show: true}) + printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir)) return new Promise((resolve, reject) => { printout.webContents.on('did-finish-load', () => { printout.webContents.printToPDF({}, (err, data) => { diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js index c65697308..75c451c13 100755 --- a/browser/main/lib/dataApi/exportNote.js +++ b/browser/main/lib/dataApi/exportNote.js @@ -43,7 +43,7 @@ function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatt ) if (outputFormatter) { - exportedData = outputFormatter(exportedData, exportTasks) + exportedData = outputFormatter(exportedData, exportTasks, path.dirname(targetPath)) } else { exportedData = Promise.resolve(exportedData) } From 177888b1592c6c74787893277484433b106c8e78 Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 11 Feb 2019 11:10:33 -0500 Subject: [PATCH 20/30] disable webSecurity to render files to pdf --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 7a814202c..10836a0ff 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -355,7 +355,7 @@ export default class MarkdownPreview extends React.Component { handleSaveAsPdf () { this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => { - const printout = new remote.BrowserWindow({show: true}) + const printout = new remote.BrowserWindow({show: true, webPreferences: {webSecurity: false}}) printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir)) return new Promise((resolve, reject) => { printout.webContents.on('did-finish-load', () => { From 116244384e73917868cab3da2b7ef70afb487a84 Mon Sep 17 00:00:00 2001 From: Daniel Mouritzen Date: Mon, 4 Mar 2019 08:37:29 +0100 Subject: [PATCH 21/30] Add ~ and _ to autoclosing brackets --- 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 5558b3bd1..05f3d822e 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -47,7 +47,7 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, - matchingPairs: '()[]{}\'\'""$$**``', + matchingPairs: '()[]{}\'\'""$$**``~~__', matchingTriples: '```"""\'\'\'', explodingPairs: '[]{}``$$', switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' From 2a0906d88e7e7bca1c146c11428504de40a4c49a Mon Sep 17 00:00:00 2001 From: Evan Miller Date: Mon, 18 Mar 2019 10:02:20 -0400 Subject: [PATCH 22/30] Rehid printout BrowserWindow --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 10836a0ff..d5df8de48 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -355,7 +355,7 @@ export default class MarkdownPreview extends React.Component { handleSaveAsPdf () { this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => { - const printout = new remote.BrowserWindow({show: true, webPreferences: {webSecurity: false}}) + const printout = new remote.BrowserWindow({show: false, webPreferences: {webSecurity: false}}) printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir)) return new Promise((resolve, reject) => { printout.webContents.on('did-finish-load', () => { From b5b56f7af1ffe0ed83902bffd0fda9d146c4b350 Mon Sep 17 00:00:00 2001 From: Nguyen Viet Hung Date: Thu, 21 Mar 2019 05:26:53 +1300 Subject: [PATCH 23/30] Refactor code editor by moving the expand snippet out to a separate file (#2864) * refactored CodeEditor by moving Snippet out * fixed typo --- browser/components/CodeEditor.js | 174 ++---------------- browser/lib/CMLanguageList.js | 78 ++++++++ browser/lib/SnippetManager.js | 91 +++++++++ .../modals/PreferencesModal/SnippetEditor.js | 5 +- 4 files changed, 188 insertions(+), 160 deletions(-) create mode 100644 browser/lib/CMLanguageList.js create mode 100644 browser/lib/SnippetManager.js diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 8b06cd722..0ddfd5c93 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -14,18 +14,14 @@ import { 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 styles from '../components/CodeEditor.styl' -import fs from 'fs' const { ipcRenderer, remote, clipboard } = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' const spellcheck = require('browser/lib/spellcheck') const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') import TurndownService from 'turndown' -import { - gfm -} from 'turndown-plugin-gfm' +import {languageMaps} from '../lib/CMLanguageList' +import snippetManager from '../lib/SnippetManager' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' @@ -38,85 +34,6 @@ 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) @@ -228,7 +145,8 @@ export default class CodeEditor extends React.Component { updateDefaultKeyMap () { const { hotkey } = this.props - const expandSnippet = this.expandSnippet.bind(this) + const self = this + const expandSnippet = snippetManager.expandSnippet this.defaultKeyMap = CodeMirror.normalizeKeyMap({ Tab: function (cm) { @@ -252,10 +170,12 @@ export default class CodeEditor extends React.Component { cursor.ch > 1 ) { // text expansion on tab key if the char before is alphabet - const snippets = JSON.parse( - fs.readFileSync(consts.SNIPPET_FILE, 'utf8') + const wordBeforeCursor = self.getWordBeforeCursor( + line, + cursor.line, + cursor.ch ) - if (expandSnippet(line, cursor, cm, snippets) === false) { + if (expandSnippet(wordBeforeCursor, cursor, cm) === false) { if (tabs) { cm.execCommand('insertTab') } else { @@ -310,22 +230,7 @@ export default class CodeEditor extends React.Component { const { rulers, enableRulers } = this.props 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' - ) - } - + snippetManager.init() this.updateDefaultKeyMap() this.value = this.props.value @@ -520,61 +425,12 @@ export default class CodeEditor extends React.Component { this.initialHighlighting() } - expandSnippet (line, cursor, cm, snippets) { - 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) { - if (snippets[i].content.indexOf(templateCursorString) !== -1) { - const snippetLines = snippets[i].content.split('\n') - let cursorLineNumber = 0 - let cursorLinePosition = 0 - - let cursorIndex - for (let j = 0; j < snippetLines.length; j++) { - cursorIndex = snippetLines[j].indexOf(templateCursorString) - - if (cursorIndex !== -1) { - cursorLineNumber = j - cursorLinePosition = cursorIndex - - break - } - } - - cm.replaceRange( - snippets[i].content.replace(templateCursorString, ''), - wordBeforeCursor.range.from, - wordBeforeCursor.range.to - ) - cm.setCursor({ - line: cursor.line + cursorLineNumber, - ch: cursorLinePosition + cursor.ch - wordBeforeCursor.text.length - }) - } else { - cm.replaceRange( - snippets[i].content, - wordBeforeCursor.range.from, - wordBeforeCursor.range.to - ) - } - return true - } - } - - return false - } - getWordBeforeCursor (line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n/ - // to prevent the word to expand is long that will crash the whole app + // to prevent the word is long that will crash the whole app // the safeStop is there to stop user to expand words that longer than 20 chars const safeStop = 20 @@ -584,7 +440,7 @@ export default class CodeEditor extends React.Component { if (!emptyChars.test(currentChar)) { wordBeforeCursor = currentChar + wordBeforeCursor } else if (wordBeforeCursor.length >= safeStop) { - throw new Error('Your snippet trigger is too long !') + throw new Error('Stopped after 20 loops for safety reason !') } else { break } @@ -747,14 +603,14 @@ export default class CodeEditor extends React.Component { } incrementLines (start, linesAdded, linesRemoved, editor) { - let highlightedLines = editor.options.linesHighlighted + const highlightedLines = editor.options.linesHighlighted const totalHighlightedLines = highlightedLines.length - let offset = linesAdded - linesRemoved + const offset = linesAdded - linesRemoved // Store new items to be added as we're changing the lines - let newLines = [] + const newLines = [] let i = totalHighlightedLines diff --git a/browser/lib/CMLanguageList.js b/browser/lib/CMLanguageList.js new file mode 100644 index 000000000..0a8652a62 --- /dev/null +++ b/browser/lib/CMLanguageList.js @@ -0,0 +1,78 @@ +export 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' +} diff --git a/browser/lib/SnippetManager.js b/browser/lib/SnippetManager.js new file mode 100644 index 000000000..2a2ff856f --- /dev/null +++ b/browser/lib/SnippetManager.js @@ -0,0 +1,91 @@ +import crypto from 'crypto' +import fs from 'fs' +import consts from './consts' + +class SnippetManager { + constructor () { + this.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.' + } + ] + this.snippets = [] + this.expandSnippet = this.expandSnippet.bind(this) + this.init = this.init.bind(this) + this.assignSnippet = this.assignSnippet.bind(this) + } + + init () { + if (fs.existsSync(consts.SNIPPET_FILE)) { + try { + this.snippets = JSON.parse( + fs.readFileSync(consts.SNIPPET_FILE, { encoding: 'UTF-8' }) + ) + } catch (error) { + console.log('Error while parsing snippet file') + } + return + } + fs.writeFileSync( + consts.SNIPPET_FILE, + JSON.stringify(this.defaultSnippet, null, 4), + 'utf8' + ) + this.snippets = this.defaultSnippet + } + + assignSnippets (snippets) { + this.snippets = snippets + } + + expandSnippet (wordBeforeCursor, cursor, cm) { + const templateCursorString = ':{}' + for (let i = 0; i < this.snippets.length; i++) { + if (this.snippets[i].prefix.indexOf(wordBeforeCursor.text) === -1) { + continue + } + if (this.snippets[i].content.indexOf(templateCursorString) !== -1) { + const snippetLines = this.snippets[i].content.split('\n') + let cursorLineNumber = 0 + let cursorLinePosition = 0 + + let cursorIndex + for (let j = 0; j < snippetLines.length; j++) { + cursorIndex = snippetLines[j].indexOf(templateCursorString) + + if (cursorIndex !== -1) { + cursorLineNumber = j + cursorLinePosition = cursorIndex + + break + } + } + + cm.replaceRange( + this.snippets[i].content.replace(templateCursorString, ''), + wordBeforeCursor.range.from, + wordBeforeCursor.range.to + ) + cm.setCursor({ + line: cursor.line + cursorLineNumber, + ch: cursorLinePosition + cursor.ch - wordBeforeCursor.text.length + }) + } else { + cm.replaceRange( + this.snippets[i].content, + wordBeforeCursor.range.from, + wordBeforeCursor.range.to + ) + } + return true + } + + return false + } +} + +const manager = new SnippetManager() +export default manager diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index 071f265fe..e95afdcf1 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -4,6 +4,7 @@ import _ from 'lodash' import styles from './SnippetTab.styl' import CSSModules from 'browser/lib/CSSModules' import dataApi from 'browser/main/lib/dataApi' +import snippetManager from '../../../lib/SnippetManager' const defaultEditorFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace'] const buildCMRulers = (rulers, enableRulers) => @@ -64,7 +65,9 @@ class SnippetEditor extends React.Component { } saveSnippet () { - dataApi.updateSnippet(this.snippet).catch((err) => { throw err }) + dataApi.updateSnippet(this.snippet) + .then(snippets => snippetManager.assignSnippets(snippets)) + .catch((err) => { throw err }) } render () { From bff081a263ba2bc86ea0ce8534c622d41058c1c1 Mon Sep 17 00:00:00 2001 From: Nguyen Viet Hung Date: Thu, 21 Mar 2019 05:27:51 +1300 Subject: [PATCH 24/30] fixed copy button (#2914) --- browser/components/MarkdownPreview.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index fcde3c1e8..63d825f43 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -756,6 +756,8 @@ export default class MarkdownPreview extends React.Component { copyIcon.innerHTML = '' copyIcon.onclick = e => { + e.preventDefault() + e.stopPropagation() copy(content) if (showCopyNotification) { this.notify('Saved to Clipboard!', { From fee966996fc78dd8b95d213a74f6e0b556347170 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Fri, 22 Mar 2019 15:33:29 +0900 Subject: [PATCH 25/30] Fix wrong binding (#2940) --- browser/lib/SnippetManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/lib/SnippetManager.js b/browser/lib/SnippetManager.js index 2a2ff856f..70f9b400c 100644 --- a/browser/lib/SnippetManager.js +++ b/browser/lib/SnippetManager.js @@ -15,7 +15,7 @@ class SnippetManager { this.snippets = [] this.expandSnippet = this.expandSnippet.bind(this) this.init = this.init.bind(this) - this.assignSnippet = this.assignSnippet.bind(this) + this.assignSnippets = this.assignSnippets.bind(this) } init () { From 2f00cec52bf9ab7fd1195d65d91cbbe347ab98db Mon Sep 17 00:00:00 2001 From: Ryo Shibayama Date: Sat, 16 Mar 2019 18:23:14 +0900 Subject: [PATCH 26/30] Upgrade Travis CI node and npm versions --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d9267f770..90548ee91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: node_js node_js: - - 7 + - 8 script: - npm run lint && npm run test - yarn jest - - 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then npm install -g grunt npm@5.2 && grunt pre-build; fi' + - 'if [[ ${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} = "master" ]]; then npm install -g grunt npm@6.4 && grunt pre-build; fi' after_success: - openssl aes-256-cbc -K $encrypted_440d7f9a3c38_key -iv $encrypted_440d7f9a3c38_iv -in .snapcraft/travis_snapcraft.cfg -out .snapcraft/snapcraft.cfg -d From 70e57cf738a16d8d566c58aa98b14a61fc1d5537 Mon Sep 17 00:00:00 2001 From: David Dreher Date: Thu, 21 Mar 2019 17:46:48 +0100 Subject: [PATCH 27/30] jumpNoteByHashHandler will now try to find the location for the note that will be selected --- browser/main/NoteList/index.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index e18160158..60e11a796 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -259,13 +259,23 @@ class NoteList extends React.Component { } jumpNoteByHashHandler (event, noteHash) { + const { data } = this.props + // first argument event isn't used. if (this.notes === null || this.notes.length === 0) { return } const selectedNoteKeys = [noteHash] - this.focusNote(selectedNoteKeys, noteHash, '/home') + + let locationToSelect = '/home' + const notesByHash = data.noteMap.map((note) => note).filter((note) => note.key === noteHash) + if (notesByHash.length > 0) { + const note = notesByHash[0] + locationToSelect = '/storages/' + note.storage + '/folders/' + note.folder + } + + this.focusNote(selectedNoteKeys, noteHash, locationToSelect) ee.emit('list:moved') } From 91d04b99d1aea1a9814761b7010eb57e2cb95abd Mon Sep 17 00:00:00 2001 From: David Dreher Date: Fri, 22 Mar 2019 21:56:49 +0100 Subject: [PATCH 28/30] change filter function to find, find will match the first note with requested key --- browser/main/NoteList/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 60e11a796..912a7abb1 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -269,10 +269,9 @@ class NoteList extends React.Component { const selectedNoteKeys = [noteHash] let locationToSelect = '/home' - const notesByHash = data.noteMap.map((note) => note).filter((note) => note.key === noteHash) - if (notesByHash.length > 0) { - const note = notesByHash[0] - locationToSelect = '/storages/' + note.storage + '/folders/' + note.folder + const noteByHash = data.noteMap.map((note) => note).find(note => { return note.key === noteHash }) + if (noteByHash !== undefined) { + locationToSelect = '/storages/' + noteByHash.storage + '/folders/' + noteByHash.folder } this.focusNote(selectedNoteKeys, noteHash, locationToSelect) From 5b99132f6630dc41b28b68707361a2829f698185 Mon Sep 17 00:00:00 2001 From: Nguyen Viet Hung Date: Sat, 23 Mar 2019 14:51:28 +0100 Subject: [PATCH 29/30] Adjust find function Co-Authored-By: dredav --- browser/main/NoteList/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 912a7abb1..f0626fb9b 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -269,7 +269,7 @@ class NoteList extends React.Component { const selectedNoteKeys = [noteHash] let locationToSelect = '/home' - const noteByHash = data.noteMap.map((note) => note).find(note => { return note.key === noteHash }) + const noteByHash = data.noteMap.map((note) => note).find(note => note.key === noteHash) if (noteByHash !== undefined) { locationToSelect = '/storages/' + noteByHash.storage + '/folders/' + noteByHash.folder } From 22d494d3f13a0d271432ee33da63c9c6683fa201 Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Sun, 24 Feb 2019 10:46:57 +0100 Subject: [PATCH 30/30] README: removed mobile app mention (#2828) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5657cb0ad..511f5a37e 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@ ![Boostnote app screenshot](./resources/repository/top.png)

Note-taking app for programmers.

-
Apps available for Mac, Windows, Linux, Android, and iOS.
+
Apps available for Mac, Windows and Linux.
Built with Electron, React + Redux, Webpack, and CSSModules.