Boostnote: Suggestion: Preview and editor in one via HyperMD

Created on 24 May 2018  路  6Comments  路  Source: BoostIO/Boostnote


Issuehunt badges

Just found HyperMD WYSIWYG Markdown editor which seems pretty good: instead of rendering the note in another split, it applies the formating on the raw code, thus splitting the screen is not needed. I can imagine this as a replacment of the current render; if the user don't wants to see the formated preview, then it can be disabled (as you can try it out on the demo by switching from _HyperMD mode_ to _Normal_ on the top-left corner of the site).

It is based on CodeMirror thus the currently used addons would work.

A side effect of this is that the searching could highlight the text in preview mode too (I'm not sure about this, it's just a tip).




IssueHunt Summary

Sponsors (Total: $60.00)

Become a sponsor now!

Or submit a pull request to get the deposits!

Tips


discussion funded on issuehunt

Most helpful comment

HyperMD is based on CodeMirror. It's really easy to integrate

tt

The project is too big for my poor network, I did a swallow clone and tried adding HyperMD.

No switch yet.

diff --git a/lib/main.html b/lib/main.html
index 22527d9..8ad7a88 100644
--- a/lib/main.html
+++ b/lib/main.html
@@ -7,6 +7,9 @@
   <link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css" media="screen" charset="utf-8">
   <link rel="shortcut icon" href="../resources/favicon.ico">
   <link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
+  <link rel="stylesheet" href="../node_modules/hypermd/mode/hypermd.css">
+  <link rel="stylesheet" href="../node_modules/hypermd/theme/hypermd-light.css">
+  <link rel="stylesheet" href="../node_modules/katex/dist/katex.min.css">
   <link rel="stylesheet" href="../node_modules/codemirror/addon/dialog/dialog.css">
   <title>Boostnote</title>

@@ -101,6 +104,12 @@

   <script src="../node_modules/codemirror/addon/dialog/dialog.js"></script>
   <script src="../node_modules/codemirror/addon/display/rulers.js"></script>
+  
+  <script src="../node_modules/codemirror/mode/markdown/markdown.js"></script>
+  <script src="../node_modules/codemirror/mode/gfm/gfm.js"></script>
+  <script src="../node_modules/hypermd/ai1.js"></script>
+  <script src="../node_modules/katex/dist/katex.min.js"></script>
+  <script src="../node_modules/hypermd/powerpack/fold-math-with-katex.js"></script>

   <script src="../node_modules/raphael/raphael.min.js"></script>
   <script src="../node_modules/flowchart.js/release/flowchart.min.js"></script>
diff --git a/package.json b/package.json
index 54c02a8..5a62f67 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
     "flowchart.js": "^1.6.5",
     "font-awesome": "^4.3.0",
     "fs-extra": "^5.0.0",
+    "hypermd": "^0.3.6",
     "i18n-2": "^0.7.2",
     "iconv-lite": "^0.4.19",
     "immutable": "^3.8.1",
diff --git a/webpack-skeleton.js b/webpack-skeleton.js
index 525ef60..6a1af8a 100644
--- a/webpack-skeleton.js
+++ b/webpack-skeleton.js
@@ -48,6 +48,7 @@ var config = {
       'react-dom': 'var ReactDOM',
       'react-redux': 'var ReactRedux',
       'codemirror': 'var CodeMirror',
+      'hypermd': 'var HyperMD',
       'redux': 'var Redux',
       'raphael': 'var Raphael',
       'flowchart': 'var flowchart',
diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js
index 91e7683..8d5de3b 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 * as HyperMD from 'hypermd'
 import 'codemirror-mode-elixir'
 import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
 import convertModeName from 'browser/lib/convertModeName'
@@ -10,6 +11,7 @@ import iconv from 'iconv-lite'
 import crypto from 'crypto'
 import consts from 'browser/lib/consts'
 import fs from 'fs'
+import path from 'path'
 const { ipcRenderer } = require('electron')

 CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -273,7 +275,9 @@ export default class CodeEditor extends React.Component {
       this.setMode(this.props.mode)
     }
     if (prevProps.theme !== this.props.theme) {
-      this.editor.setOption('theme', this.props.theme)
+      // Yet only 'hypermd-light' theme works perfectly with HyperMD
+      let realTheme = /hypermd/.test(this.editor.options.mode) ? 'hypermd-light' : this.props.theme
+      this.editor.setOption('theme', realTheme)
       // editor should be refreshed after css loaded
     }
     if (prevProps.fontSize !== this.props.fontSize) {
@@ -315,6 +319,45 @@ export default class CodeEditor extends React.Component {
     let syntax = CodeMirror.findModeByName(convertModeName(mode))
     if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')

+    if (/markdown|gfm/i.test(mode)) {
+      let cm = this.editor
+
+      cm.setOption('mode', 'hypermd')
+      HyperMD.switchToHyperMD(cm)
+
+      // mode loader for Markdown code blocks
+      cm.setOption('hmdModeLoader', '../node_modules/codemirror/') 
+      
+      // BoostNote supports inserting files already. No HyperMD's business
+      cm.setOption('hmdInsertFile', false)
+
+      // Patch HyperMD's Image/Link URL Resolver
+      // to deal with the urls that begin with ":storage"
+      if (!cm.hmd.ReadLink.resolve.BoostNotePatched) {
+        // not patched? make a patch
+
+        const { STORAGE_FOLDER_PLACEHOLDER, DESTINATION_FOLDER } = attachmentManagement
+        const findStorage = require('browser/lib/findStorage')
+        const targetStorage = findStorage.findStorage(this.props.storageKey)
+
+        let oldFn = cm.hmd.ReadLink.resolve
+        let newFn = (url, baseurl) => {
+          if (url.indexOf(STORAGE_FOLDER_PLACEHOLDER) === 0) {
+            // the url begins with ":storage"
+            // convert to local file system path
+            return 'file:///' + path.join(targetStorage.path, DESTINATION_FOLDER, url.slice(STORAGE_FOLDER_PLACEHOLDER.length))
+          }
+          // otherwise, use HyperMD default algorithm to resovle URL
+          return oldFn.apply(this, arguments)
+        }
+
+        newFn.BoostNotePatched = true
+        cm.hmd.ReadLink.resolve = newFn
+      }
+
+      return
+    }
+
     this.editor.setOption('mode', syntax.mime)
     CodeMirror.autoLoadMode(this.editor, syntax.mode)
   }

All 6 comments

Benefits:

  • Scroll sync is not an issue anymore (#1811)

Disadvanteges:

  • Markdown is defacto, additional markup languages are not welcome (Asciidoc, ReStructuredText, etc.)

Related issue: #236

HyperMD is based on CodeMirror. It's really easy to integrate

tt

The project is too big for my poor network, I did a swallow clone and tried adding HyperMD.

No switch yet.

diff --git a/lib/main.html b/lib/main.html
index 22527d9..8ad7a88 100644
--- a/lib/main.html
+++ b/lib/main.html
@@ -7,6 +7,9 @@
   <link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css" media="screen" charset="utf-8">
   <link rel="shortcut icon" href="../resources/favicon.ico">
   <link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
+  <link rel="stylesheet" href="../node_modules/hypermd/mode/hypermd.css">
+  <link rel="stylesheet" href="../node_modules/hypermd/theme/hypermd-light.css">
+  <link rel="stylesheet" href="../node_modules/katex/dist/katex.min.css">
   <link rel="stylesheet" href="../node_modules/codemirror/addon/dialog/dialog.css">
   <title>Boostnote</title>

@@ -101,6 +104,12 @@

   <script src="../node_modules/codemirror/addon/dialog/dialog.js"></script>
   <script src="../node_modules/codemirror/addon/display/rulers.js"></script>
+  
+  <script src="../node_modules/codemirror/mode/markdown/markdown.js"></script>
+  <script src="../node_modules/codemirror/mode/gfm/gfm.js"></script>
+  <script src="../node_modules/hypermd/ai1.js"></script>
+  <script src="../node_modules/katex/dist/katex.min.js"></script>
+  <script src="../node_modules/hypermd/powerpack/fold-math-with-katex.js"></script>

   <script src="../node_modules/raphael/raphael.min.js"></script>
   <script src="../node_modules/flowchart.js/release/flowchart.min.js"></script>
diff --git a/package.json b/package.json
index 54c02a8..5a62f67 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
     "flowchart.js": "^1.6.5",
     "font-awesome": "^4.3.0",
     "fs-extra": "^5.0.0",
+    "hypermd": "^0.3.6",
     "i18n-2": "^0.7.2",
     "iconv-lite": "^0.4.19",
     "immutable": "^3.8.1",
diff --git a/webpack-skeleton.js b/webpack-skeleton.js
index 525ef60..6a1af8a 100644
--- a/webpack-skeleton.js
+++ b/webpack-skeleton.js
@@ -48,6 +48,7 @@ var config = {
       'react-dom': 'var ReactDOM',
       'react-redux': 'var ReactRedux',
       'codemirror': 'var CodeMirror',
+      'hypermd': 'var HyperMD',
       'redux': 'var Redux',
       'raphael': 'var Raphael',
       'flowchart': 'var flowchart',
diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js
index 91e7683..8d5de3b 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 * as HyperMD from 'hypermd'
 import 'codemirror-mode-elixir'
 import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
 import convertModeName from 'browser/lib/convertModeName'
@@ -10,6 +11,7 @@ import iconv from 'iconv-lite'
 import crypto from 'crypto'
 import consts from 'browser/lib/consts'
 import fs from 'fs'
+import path from 'path'
 const { ipcRenderer } = require('electron')

 CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
@@ -273,7 +275,9 @@ export default class CodeEditor extends React.Component {
       this.setMode(this.props.mode)
     }
     if (prevProps.theme !== this.props.theme) {
-      this.editor.setOption('theme', this.props.theme)
+      // Yet only 'hypermd-light' theme works perfectly with HyperMD
+      let realTheme = /hypermd/.test(this.editor.options.mode) ? 'hypermd-light' : this.props.theme
+      this.editor.setOption('theme', realTheme)
       // editor should be refreshed after css loaded
     }
     if (prevProps.fontSize !== this.props.fontSize) {
@@ -315,6 +319,45 @@ export default class CodeEditor extends React.Component {
     let syntax = CodeMirror.findModeByName(convertModeName(mode))
     if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')

+    if (/markdown|gfm/i.test(mode)) {
+      let cm = this.editor
+
+      cm.setOption('mode', 'hypermd')
+      HyperMD.switchToHyperMD(cm)
+
+      // mode loader for Markdown code blocks
+      cm.setOption('hmdModeLoader', '../node_modules/codemirror/') 
+      
+      // BoostNote supports inserting files already. No HyperMD's business
+      cm.setOption('hmdInsertFile', false)
+
+      // Patch HyperMD's Image/Link URL Resolver
+      // to deal with the urls that begin with ":storage"
+      if (!cm.hmd.ReadLink.resolve.BoostNotePatched) {
+        // not patched? make a patch
+
+        const { STORAGE_FOLDER_PLACEHOLDER, DESTINATION_FOLDER } = attachmentManagement
+        const findStorage = require('browser/lib/findStorage')
+        const targetStorage = findStorage.findStorage(this.props.storageKey)
+
+        let oldFn = cm.hmd.ReadLink.resolve
+        let newFn = (url, baseurl) => {
+          if (url.indexOf(STORAGE_FOLDER_PLACEHOLDER) === 0) {
+            // the url begins with ":storage"
+            // convert to local file system path
+            return 'file:///' + path.join(targetStorage.path, DESTINATION_FOLDER, url.slice(STORAGE_FOLDER_PLACEHOLDER.length))
+          }
+          // otherwise, use HyperMD default algorithm to resovle URL
+          return oldFn.apply(this, arguments)
+        }
+
+        newFn.BoostNotePatched = true
+        cm.hmd.ReadLink.resolve = newFn
+      }
+
+      return
+    }
+
     this.editor.setOption('mode', syntax.mime)
     CodeMirror.autoLoadMode(this.editor, syntax.mode)
   }

@boostio funded this issue with $40. See it on IssueHunt

If I may add a nice to have thing would be the "Suggestions dropdown" like Notion:

screenshot 2018-12-14 at 14 42 34

@floweringmind has funded $20.00 to this issue.


Was this page helpful?
0 / 5 - 0 ratings

Related issues

louiealmeda picture louiealmeda  路  3Comments

Petroochio picture Petroochio  路  3Comments

N2ITN picture N2ITN  路  3Comments

DanielRamosAcosta picture DanielRamosAcosta  路  3Comments

croulibri picture croulibri  路  3Comments