diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 463f0a164..530258a3c 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' import ConfigManager from '../main/lib/ConfigManager' const { remote, shell } = require('electron') @@ -40,7 +42,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 ( @@ -800,6 +803,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 b7f219b87..4d03e4121 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -416,6 +416,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%) @@ -475,6 +495,14 @@ body[data-theme="dark"] border-color themeDarkBorder background-color themeDarkPreview + 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 @@ -510,6 +538,14 @@ body[data-theme="solarized-dark"] border-color themeDarkBorder background-color $ui-solarized-dark-noteDetail-backgroundColor + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-solarized-dark-noteDetail-backgroundColor + .prev, .next + color $ui-solarized-dark-button--active-color + background-color $ui-solarized-dark-button-backgroundColor + themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%) themeMonokaiTableHead = themeMonokaiTableEven @@ -538,6 +574,7 @@ body[data-theme="monokai"] border-right solid 1px themeMonokaiTableBorder kbd background-color themeDarkBackground + dl border-color themeDarkBorder background-color themeMonokaiTableHead @@ -547,6 +584,14 @@ body[data-theme="monokai"] border-color themeDarkBorder background-color $ui-monokai-noteDetail-backgroundColor + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-monokai-noteDetail-backgroundColor + .prev, .next + color $ui-monokai-button--active-color + background-color $ui-monokai-button-backgroundColor + themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%) themeDraculaTableHead = themeDraculaTableEven @@ -575,6 +620,7 @@ body[data-theme="dracula"] border-right solid 1px themeDraculaTableBorder kbd background-color themeDarkBackground + dl border-color themeDarkBorder background-color themeDraculaTableHead @@ -583,3 +629,11 @@ body[data-theme="dracula"] dd border-color themeDarkBorder background-color $ui-dracula-noteDetail-backgroundColor + + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-dracula-noteDetail-backgroundColor + .prev, .next + color $ui-dracula-button--active-color + background-color $ui-dracula-button-backgroundColor diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 2a7b66b01..e115e045c 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -152,6 +152,21 @@ class Markdown {
${token.content}
` }, + 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 => { return `
           ${token.fileName}
diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js
index c193eaf2e..73bf13037 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))
   })
diff --git a/package.json b/package.json
index 1fb8e3808..c770677e0 100644
--- a/package.json
+++ b/package.json
@@ -99,8 +99,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 4b24d45d6..d1645cfd8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2749,6 +2749,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"
@@ -5324,6 +5328,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"
@@ -5805,6 +5813,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"
@@ -7241,6 +7255,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"
@@ -7420,6 +7441,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"
@@ -7427,6 +7452,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"
@@ -7492,6 +7521,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"