├── docs ├── .gitkeep ├── CNAME ├── assets │ ├── icon.png │ ├── background.jpg │ ├── site-shot.png │ ├── export-shot.png │ ├── f-multisource.png │ ├── logo-github.png │ ├── screen-shot.png │ ├── gh-fork-ribbon.min.css │ ├── index.js │ └── site.css ├── images │ └── mditor.png ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── slide │ ├── css │ │ └── bundle.css.map │ ├── font │ │ ├── 674f50d287a8c48dc19ba404d20fe713.eot │ │ ├── a48ac41620cd818c5020d0f4302489ff.ttf │ │ ├── af7ae505a9eed503f8b8e6982036873e.woff2 │ │ ├── b06871f281fee6b241d60582ae9369b9.ttf │ │ └── fee66e712a8a08eef5805a46892932ad.woff │ └── index.html ├── update.json ├── javascripts │ └── scale.fix.js ├── doc │ ├── slide.md │ └── slide.html └── stylesheets │ └── github-light.css ├── packages ├── desktop │ ├── docs │ │ ├── .gitkeep │ │ ├── CNAME │ │ ├── assets │ │ │ ├── icon.png │ │ │ ├── background.jpg │ │ │ ├── export-shot.png │ │ │ ├── logo-github.png │ │ │ ├── screen-shot.png │ │ │ ├── site-shot.png │ │ │ ├── f-multisource.png │ │ │ ├── gh-fork-ribbon.min.css │ │ │ ├── index.js │ │ │ └── site.css │ │ ├── images │ │ │ └── mditor.png │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ │ ├── update.json │ │ ├── slide │ │ │ ├── font │ │ │ │ ├── 674f50d287a8c48dc19ba404d20fe713.eot │ │ │ │ ├── a48ac41620cd818c5020d0f4302489ff.ttf │ │ │ │ ├── b06871f281fee6b241d60582ae9369b9.ttf │ │ │ │ ├── af7ae505a9eed503f8b8e6982036873e.woff2 │ │ │ │ └── fee66e712a8a08eef5805a46892932ad.woff │ │ │ └── index.html │ │ ├── javascripts │ │ │ └── scale.fix.js │ │ ├── doc │ │ │ ├── slide.md │ │ │ └── slide.html │ │ └── stylesheets │ │ │ └── github-light.css │ ├── test │ │ ├── e2e │ │ │ ├── .gitkeep │ │ │ └── demo.test.js │ │ ├── unit │ │ │ ├── .gitkeep │ │ │ └── demo.test.js │ │ └── driver.js │ ├── bin │ │ ├── start.sh │ │ ├── restart.sh │ │ ├── prepublish.sh │ │ ├── stop.sh │ │ ├── clear.sh │ │ ├── test.sh │ │ ├── build.sh │ │ ├── e2e.sh │ │ ├── watch.sh │ │ ├── unit.sh │ │ ├── release.sh │ │ ├── info.plist │ │ └── release.js │ ├── server.local.yml.rename │ ├── src │ │ ├── convert │ │ │ ├── html.css │ │ │ ├── border.css │ │ │ ├── tmpl.html │ │ │ ├── slide.html │ │ │ └── index.js │ │ ├── common │ │ │ ├── sleep.js │ │ │ ├── yaml.js │ │ │ ├── mkdirp.js │ │ │ ├── fs.js │ │ │ ├── blob2base64.js │ │ │ ├── blob2buffer.js │ │ │ └── store.js │ │ ├── menu │ │ │ ├── index.js │ │ │ ├── view.js │ │ │ ├── help.js │ │ │ ├── window.js │ │ │ ├── contextmenu.js │ │ │ ├── edit.js │ │ │ ├── main.js │ │ │ └── file.js │ │ ├── window │ │ │ ├── index.html │ │ │ ├── drapable.js │ │ │ ├── index.less │ │ │ └── index.js │ │ ├── preference │ │ │ ├── tmpl.md │ │ │ └── index.js │ │ ├── recent │ │ │ └── index.js │ │ ├── i18n │ │ │ ├── index.js │ │ │ └── locales │ │ │ │ ├── zh.yml │ │ │ │ └── en.yml │ │ ├── update │ │ │ └── index.js │ │ └── uml │ │ │ └── index.js │ ├── design │ │ ├── icon.icns │ │ ├── icon.png │ │ ├── icon.psd │ │ ├── install.png │ │ ├── install.psd │ │ └── install.dmgCanvas │ │ │ ├── Disk Image │ │ │ └── QuickLook │ │ │ └── Preview.jpg │ ├── .gitignore │ ├── .babelrc │ ├── jasmine.json │ ├── electron-builder.yml │ ├── server.yml │ ├── .npmignore │ ├── CHANGELOG.md │ ├── karma.conf.js │ ├── .eslintrc.yml │ ├── README.md │ ├── webpack.config.js │ └── package.json └── embed │ ├── src │ ├── plugins │ │ └── csv.js │ ├── server │ │ └── index.js │ ├── assets │ │ ├── fonts │ │ │ ├── Roboto-Black.ttf │ │ │ ├── Roboto-Bold.ttf │ │ │ ├── Roboto-Italic.ttf │ │ │ ├── Roboto-Light.ttf │ │ │ ├── Roboto-Medium.ttf │ │ │ ├── Roboto-Thin.ttf │ │ │ ├── robotomono.woff2 │ │ │ ├── Roboto-Regular.ttf │ │ │ ├── RobotoMono-Bold.ttf │ │ │ ├── RobotoMono-Thin.ttf │ │ │ ├── Roboto-BlackItalic.ttf │ │ │ ├── Roboto-BoldItalic.ttf │ │ │ ├── Roboto-LightItalic.ttf │ │ │ ├── Roboto-ThinItalic.ttf │ │ │ ├── RobotoMono-Italic.ttf │ │ │ ├── RobotoMono-Light.ttf │ │ │ ├── RobotoMono-Medium.ttf │ │ │ ├── RobotoMono-Regular.ttf │ │ │ ├── Roboto-MediumItalic.ttf │ │ │ ├── RobotoMono-BoldItalic.ttf │ │ │ ├── RobotoMono-LightItalic.ttf │ │ │ ├── RobotoMono-MediumItalic.ttf │ │ │ └── RobotoMono-ThinItalic.ttf │ │ └── index.ejs │ ├── viewer │ │ ├── index.html │ │ ├── index.less │ │ └── index.js │ ├── toolbar │ │ ├── index.html │ │ ├── index.less │ │ ├── index.js │ │ └── items.js │ ├── editor │ │ ├── index.html │ │ ├── commands.js │ │ ├── stack.js │ │ └── index.less │ ├── finder │ │ ├── index.html │ │ ├── index.less │ │ └── index.js │ ├── client │ │ ├── index.html │ │ ├── shortcut.js │ │ ├── index.less │ │ └── index.js │ └── common │ │ └── parser.js │ ├── test │ ├── e2e │ │ ├── .gitkeep │ │ └── demo.test.js │ ├── unit │ │ ├── .gitkeep │ │ └── demo.test.js │ └── driver.js │ ├── docs │ ├── CNAME │ ├── index.html │ ├── images │ │ └── mditor.png │ └── demo │ │ ├── css │ │ ├── mditor.css.map │ │ └── mditor.min.css.map │ │ ├── font │ │ ├── 674f50d287a8c48dc19ba404d20fe713.eot │ │ ├── a48ac41620cd818c5020d0f4302489ff.ttf │ │ ├── b06871f281fee6b241d60582ae9369b9.ttf │ │ ├── af7ae505a9eed503f8b8e6982036873e.woff2 │ │ └── fee66e712a8a08eef5805a46892932ad.woff │ │ └── index.html │ ├── lib │ ├── plugins │ │ └── csv.js │ ├── server │ │ └── index.js │ ├── editor │ │ ├── commands.js │ │ └── stack.js │ ├── client │ │ ├── shortcut.js │ │ └── index.js │ ├── viewer │ │ └── index.js │ ├── toolbar │ │ ├── index.js │ │ └── items.js │ ├── finder │ │ └── index.js │ └── common │ │ └── parser.js │ ├── server.local.yml.rename │ ├── bin │ ├── restart.sh │ ├── stop.sh │ ├── prepublish.sh │ ├── clear.sh │ ├── test.sh │ ├── e2e.sh │ ├── start.sh │ ├── watch.sh │ ├── unit.sh │ └── build.sh │ ├── .gitignore │ ├── dist │ ├── css │ │ ├── mditor.css.map │ │ └── mditor.min.css.map │ ├── font │ │ ├── 674f50d287a8c48dc19ba404d20fe713.eot │ │ ├── a48ac41620cd818c5020d0f4302489ff.ttf │ │ ├── b06871f281fee6b241d60582ae9369b9.ttf │ │ ├── af7ae505a9eed503f8b8e6982036873e.woff2 │ │ └── fee66e712a8a08eef5805a46892932ad.woff │ └── index.html │ ├── .babelrc │ ├── jasmine.json │ ├── .npmignore │ ├── server.yml │ ├── CHANGELOG.md │ ├── karma.conf.js │ ├── .eslintrc.yml │ ├── webpack.config.js │ ├── package.json │ └── README.md ├── CNAME ├── .gitignore └── README.md /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | mditor.ink -------------------------------------------------------------------------------- /packages/desktop/docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/embed/src/plugins/csv.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/embed/test/e2e/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/embed/test/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/desktop/docs/CNAME: -------------------------------------------------------------------------------- 1 | mditor.com -------------------------------------------------------------------------------- /packages/desktop/test/e2e/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/desktop/test/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | mditor.ink 2 | www.mditor.ink 3 | -------------------------------------------------------------------------------- /packages/embed/docs/CNAME: -------------------------------------------------------------------------------- 1 | embed.mditor.com -------------------------------------------------------------------------------- /packages/embed/lib/plugins/csv.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/"use strict"; -------------------------------------------------------------------------------- /packages/desktop/bin/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | electron . -------------------------------------------------------------------------------- /packages/desktop/server.local.yml.rename: -------------------------------------------------------------------------------- 1 | proxy: 2 | rules: 3 | ^/api: '/' -------------------------------------------------------------------------------- /packages/embed/server.local.yml.rename: -------------------------------------------------------------------------------- 1 | proxy: 2 | rules: 3 | ^/api: '/' -------------------------------------------------------------------------------- /packages/desktop/src/convert/html.css: -------------------------------------------------------------------------------- 1 | .markdown-body table { 2 | display: table; 3 | } -------------------------------------------------------------------------------- /packages/embed/bin/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm stop && npm start -------------------------------------------------------------------------------- /packages/embed/docs/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/icon.png -------------------------------------------------------------------------------- /packages/desktop/bin/restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm stop && npm start -------------------------------------------------------------------------------- /packages/embed/bin/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | nokit delete $npm_package_name -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | logs/ 4 | node_modules/ 5 | .vscode/ 6 | server.local.yml 7 | -------------------------------------------------------------------------------- /docs/images/mditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/images/mditor.png -------------------------------------------------------------------------------- /packages/desktop/bin/prepublish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm test 6 | npm run build -------------------------------------------------------------------------------- /packages/desktop/bin/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | #nokit delete $npm_package_name -------------------------------------------------------------------------------- /packages/embed/bin/prepublish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm test 6 | npm run build -------------------------------------------------------------------------------- /docs/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/background.jpg -------------------------------------------------------------------------------- /docs/assets/site-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/site-shot.png -------------------------------------------------------------------------------- /docs/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /packages/embed/bin/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rm -rf ./dist/** 6 | mkdir -p ./dist -------------------------------------------------------------------------------- /docs/assets/export-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/export-shot.png -------------------------------------------------------------------------------- /docs/assets/f-multisource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/f-multisource.png -------------------------------------------------------------------------------- /docs/assets/logo-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/logo-github.png -------------------------------------------------------------------------------- /docs/assets/screen-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/assets/screen-shot.png -------------------------------------------------------------------------------- /packages/embed/.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | logs/ 4 | node_modules/ 5 | .vscode/ 6 | server.local.yml 7 | -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /packages/desktop/bin/clear.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | rm -rf ./build/dist/** 6 | mkdir -p ./build/dist -------------------------------------------------------------------------------- /packages/desktop/design/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/icon.icns -------------------------------------------------------------------------------- /packages/desktop/design/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/icon.png -------------------------------------------------------------------------------- /packages/desktop/design/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/icon.psd -------------------------------------------------------------------------------- /packages/embed/src/server/index.js: -------------------------------------------------------------------------------- 1 | exports.version = '{version}'; 2 | exports.Parser = require('../common/parser'); -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/slide/css/bundle.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/bundle.css","sourceRoot":""} -------------------------------------------------------------------------------- /packages/desktop/design/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/install.png -------------------------------------------------------------------------------- /packages/desktop/design/install.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/install.psd -------------------------------------------------------------------------------- /packages/embed/bin/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export NODE_ENV=mock 6 | 7 | npm run unit 8 | npm run e2e -------------------------------------------------------------------------------- /packages/desktop/bin/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | export NODE_ENV=mock 6 | 7 | npm run unit 8 | npm run e2e -------------------------------------------------------------------------------- /packages/desktop/docs/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/icon.png -------------------------------------------------------------------------------- /packages/desktop/docs/images/mditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/images/mditor.png -------------------------------------------------------------------------------- /packages/embed/docs/images/mditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/docs/images/mditor.png -------------------------------------------------------------------------------- /packages/embed/dist/css/mditor.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/mditor.css","sourceRoot":""} -------------------------------------------------------------------------------- /packages/desktop/bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run clear 6 | npm run lint 7 | 8 | webpack --display-error-details -------------------------------------------------------------------------------- /packages/desktop/docs/assets/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/background.jpg -------------------------------------------------------------------------------- /packages/desktop/docs/assets/export-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/export-shot.png -------------------------------------------------------------------------------- /packages/desktop/docs/assets/logo-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/logo-github.png -------------------------------------------------------------------------------- /packages/desktop/docs/assets/screen-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/screen-shot.png -------------------------------------------------------------------------------- /packages/desktop/docs/assets/site-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/site-shot.png -------------------------------------------------------------------------------- /packages/desktop/docs/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /packages/embed/dist/css/mditor.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/mditor.min.css","sourceRoot":""} -------------------------------------------------------------------------------- /packages/embed/docs/demo/css/mditor.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/mditor.css","sourceRoot":""} -------------------------------------------------------------------------------- /docs/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "detail": [ 4 | "1. 修正打开或保存对话框默认路径错误问题" 5 | ], 6 | "url": "http://mditor.com/" 7 | } -------------------------------------------------------------------------------- /packages/desktop/docs/assets/f-multisource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/assets/f-multisource.png -------------------------------------------------------------------------------- /packages/embed/docs/demo/css/mditor.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"css/mditor.min.css","sourceRoot":""} -------------------------------------------------------------------------------- /packages/embed/bin/e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run build 6 | npm restart 7 | 8 | JASMINE_CONFIG_PATH=$PWD/jasmine.json jasmine -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Black.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Italic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Medium.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Thin.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/robotomono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/robotomono.woff2 -------------------------------------------------------------------------------- /packages/desktop/bin/e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run build 6 | npm restart 7 | 8 | JASMINE_CONFIG_PATH=$PWD/jasmine.json jasmine -------------------------------------------------------------------------------- /packages/desktop/docs/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /packages/desktop/docs/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /packages/embed/bin/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | nokit start --name $npm_package_name -p $npm_package_dev_port --config server -e local -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-Bold.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-Thin.ttf -------------------------------------------------------------------------------- /docs/slide/font/674f50d287a8c48dc19ba404d20fe713.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/slide/font/674f50d287a8c48dc19ba404d20fe713.eot -------------------------------------------------------------------------------- /docs/slide/font/a48ac41620cd818c5020d0f4302489ff.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/slide/font/a48ac41620cd818c5020d0f4302489ff.ttf -------------------------------------------------------------------------------- /docs/slide/font/af7ae505a9eed503f8b8e6982036873e.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/slide/font/af7ae505a9eed503f8b8e6982036873e.woff2 -------------------------------------------------------------------------------- /docs/slide/font/b06871f281fee6b241d60582ae9369b9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/slide/font/b06871f281fee6b241d60582ae9369b9.ttf -------------------------------------------------------------------------------- /docs/slide/font/fee66e712a8a08eef5805a46892932ad.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/docs/slide/font/fee66e712a8a08eef5805a46892932ad.woff -------------------------------------------------------------------------------- /packages/desktop/design/install.dmgCanvas/Disk Image: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/install.dmgCanvas/Disk Image -------------------------------------------------------------------------------- /packages/desktop/docs/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /packages/desktop/docs/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-BlackItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-BoldItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-LightItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-ThinItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-Italic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-Light.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-Medium.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /packages/desktop/docs/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "detail": [ 4 | "1. 修正打开或保存对话框默认路径错误问题" 5 | ], 6 | "url": "http://mditor.com/" 7 | } -------------------------------------------------------------------------------- /packages/embed/lib/server/index.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | exports.version = '{version}'; 4 | exports.Parser = require('../common/parser'); -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/Roboto-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/Roboto-MediumItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-BoldItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-LightItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-MediumItalic.ttf -------------------------------------------------------------------------------- /packages/embed/src/assets/fonts/RobotoMono-ThinItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/src/assets/fonts/RobotoMono-ThinItalic.ttf -------------------------------------------------------------------------------- /packages/desktop/.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | *.js.map 4 | *.css.map 5 | logs/ 6 | node_modules/ 7 | build/ 8 | .vscode/ 9 | server.local.yml 10 | release/ -------------------------------------------------------------------------------- /packages/desktop/bin/watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run clear 6 | npm run lint 7 | 8 | export NODE_ENV=dev 9 | webpack --display-error-details --watch -------------------------------------------------------------------------------- /packages/desktop/test/unit/demo.test.js: -------------------------------------------------------------------------------- 1 | describe('demo', function () { 2 | 3 | it('should do what...', function () { 4 | expect(0).not.toBe(1); 5 | }); 6 | 7 | }); -------------------------------------------------------------------------------- /packages/embed/dist/font/674f50d287a8c48dc19ba404d20fe713.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/dist/font/674f50d287a8c48dc19ba404d20fe713.eot -------------------------------------------------------------------------------- /packages/embed/dist/font/a48ac41620cd818c5020d0f4302489ff.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/dist/font/a48ac41620cd818c5020d0f4302489ff.ttf -------------------------------------------------------------------------------- /packages/embed/dist/font/b06871f281fee6b241d60582ae9369b9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/dist/font/b06871f281fee6b241d60582ae9369b9.ttf -------------------------------------------------------------------------------- /packages/embed/test/unit/demo.test.js: -------------------------------------------------------------------------------- 1 | describe('demo', function () { 2 | 3 | it('should do what...', function () { 4 | expect(0).not.toBe(1); 5 | }); 6 | 7 | }); -------------------------------------------------------------------------------- /packages/desktop/design/install.dmgCanvas/QuickLook/Preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/design/install.dmgCanvas/QuickLook/Preview.jpg -------------------------------------------------------------------------------- /packages/embed/dist/font/af7ae505a9eed503f8b8e6982036873e.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/dist/font/af7ae505a9eed503f8b8e6982036873e.woff2 -------------------------------------------------------------------------------- /packages/embed/dist/font/fee66e712a8a08eef5805a46892932ad.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/dist/font/fee66e712a8a08eef5805a46892932ad.woff -------------------------------------------------------------------------------- /packages/embed/docs/demo/font/674f50d287a8c48dc19ba404d20fe713.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/docs/demo/font/674f50d287a8c48dc19ba404d20fe713.eot -------------------------------------------------------------------------------- /packages/embed/docs/demo/font/a48ac41620cd818c5020d0f4302489ff.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/docs/demo/font/a48ac41620cd818c5020d0f4302489ff.ttf -------------------------------------------------------------------------------- /packages/embed/docs/demo/font/b06871f281fee6b241d60582ae9369b9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/docs/demo/font/b06871f281fee6b241d60582ae9369b9.ttf -------------------------------------------------------------------------------- /packages/desktop/docs/slide/font/674f50d287a8c48dc19ba404d20fe713.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/slide/font/674f50d287a8c48dc19ba404d20fe713.eot -------------------------------------------------------------------------------- /packages/desktop/docs/slide/font/a48ac41620cd818c5020d0f4302489ff.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/slide/font/a48ac41620cd818c5020d0f4302489ff.ttf -------------------------------------------------------------------------------- /packages/desktop/docs/slide/font/b06871f281fee6b241d60582ae9369b9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/slide/font/b06871f281fee6b241d60582ae9369b9.ttf -------------------------------------------------------------------------------- /packages/embed/docs/demo/font/af7ae505a9eed503f8b8e6982036873e.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/docs/demo/font/af7ae505a9eed503f8b8e6982036873e.woff2 -------------------------------------------------------------------------------- /packages/embed/docs/demo/font/fee66e712a8a08eef5805a46892932ad.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/embed/docs/demo/font/fee66e712a8a08eef5805a46892932ad.woff -------------------------------------------------------------------------------- /packages/desktop/docs/slide/font/af7ae505a9eed503f8b8e6982036873e.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/slide/font/af7ae505a9eed503f8b8e6982036873e.woff2 -------------------------------------------------------------------------------- /packages/desktop/docs/slide/font/fee66e712a8a08eef5805a46892932ad.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Houfeng/mditor/HEAD/packages/desktop/docs/slide/font/fee66e712a8a08eef5805a46892932ad.woff -------------------------------------------------------------------------------- /packages/desktop/src/common/sleep.js: -------------------------------------------------------------------------------- 1 | const Promise = require('bluebird'); 2 | 3 | module.exports = function (delay) { 4 | return new Promise(resolve => { 5 | setTimeout(resolve, delay); 6 | }); 7 | }; -------------------------------------------------------------------------------- /packages/embed/bin/watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run clear 6 | npm run lint 7 | 8 | npm run restart 9 | 10 | export NODE_ENV=dev 11 | webpack --display-error-details --watch -------------------------------------------------------------------------------- /packages/embed/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-3" 5 | ], 6 | "plugins": [ 7 | "transform-runtime" 8 | ], 9 | "auxiliaryCommentBefore": "istanbul ignore next" 10 | } -------------------------------------------------------------------------------- /packages/embed/src/viewer/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
-------------------------------------------------------------------------------- /packages/desktop/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | //"es2015", 4 | //"stage-3" 5 | ], 6 | "plugins": [ 7 | //"transform-runtime" 8 | ], 9 | "auxiliaryCommentBefore": "istanbul ignore next" 10 | } -------------------------------------------------------------------------------- /packages/desktop/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test", 3 | "spec_files": [ 4 | "e2e/*.js" 5 | ], 6 | "helpers": [ 7 | "driver.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } -------------------------------------------------------------------------------- /packages/embed/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test", 3 | "spec_files": [ 4 | "e2e/*.js" 5 | ], 6 | "helpers": [ 7 | "driver.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } -------------------------------------------------------------------------------- /packages/desktop/electron-builder.yml: -------------------------------------------------------------------------------- 1 | appId: net.xhou.mditor 2 | electronVersion: "1.6.1" 3 | icon: ./design/icon.icns 4 | mac: 5 | category: net.xhou..mditor.editor 6 | target: dmg 7 | icon: ./design/icon.icns 8 | dmg: 9 | icon: ./design/icon.icns -------------------------------------------------------------------------------- /packages/embed/.npmignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | *.sh 4 | .babelrc 5 | .eslintrc.yml 6 | .travis.yml 7 | server.yml 8 | server.local.yml 9 | webpack.config.js 10 | logs/ 11 | node_modules/ 12 | scripts/ 13 | docs/ 14 | test/ 15 | examples/ 16 | .vscode/ -------------------------------------------------------------------------------- /packages/embed/server.yml: -------------------------------------------------------------------------------- 1 | filters: 2 | ^/: 'nokit-filter-proxy' 3 | public: 4 | ^/: './dist' 5 | cache: 6 | enabled: false 7 | log: 8 | enabled: false 9 | session: 10 | enabled: false 11 | proxy: 12 | rules: 13 | ^/api: '...' -------------------------------------------------------------------------------- /packages/desktop/src/convert/border.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #eee; 3 | } 4 | 5 | .markdown-body { 6 | background-color: #fff; 7 | padding: 30px; 8 | max-width: 840px; 9 | margin: 20px auto; 10 | box-shadow: 0 0 5px 0px rgba(0, 0, 0, .1); 11 | } -------------------------------------------------------------------------------- /packages/desktop/server.yml: -------------------------------------------------------------------------------- 1 | filters: 2 | ^/: 'nokit-filter-proxy' 3 | public: 4 | ^/: './build/dist' 5 | cache: 6 | enabled: false 7 | log: 8 | enabled: false 9 | session: 10 | enabled: false 11 | proxy: 12 | rules: 13 | ^/api: '...' -------------------------------------------------------------------------------- /packages/desktop/bin/unit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run lint 6 | 7 | karma start karma.conf.js --single-run 8 | cat ./build/coverage/report-text/text-summary.txt 9 | 10 | echo '' 11 | echo 'Coverage Detail: ./build/coverage/report-html/index.html' 12 | echo '' -------------------------------------------------------------------------------- /packages/desktop/src/common/yaml.js: -------------------------------------------------------------------------------- 1 | const yaml = require('js-yaml'); 2 | 3 | function parseYaml(text) { 4 | try { 5 | return yaml.safeLoad(text, 'utf8'); 6 | } catch (err) { 7 | console.error(err); 8 | return null; 9 | } 10 | } 11 | 12 | module.exports = parseYaml; -------------------------------------------------------------------------------- /packages/embed/bin/unit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run lint 6 | 7 | karma start karma.conf.js --single-run 8 | cat ./build/coverage/report-text/text-summary.txt 9 | 10 | echo '' 11 | echo 'Coverage Detail: ./build/coverage/report-html/index.html' 12 | echo '' -------------------------------------------------------------------------------- /packages/desktop/.npmignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.log 3 | *.sh 4 | *.js.map 5 | *.css.map 6 | .babelrc 7 | .eslintrc.yml 8 | .travis.yml 9 | server.yml 10 | server.local.yml 11 | webpack.config.js 12 | logs/ 13 | node_modules/ 14 | scripts/ 15 | docs/ 16 | test/ 17 | examples/ 18 | build/ 19 | .vscode/ 20 | release/ -------------------------------------------------------------------------------- /packages/embed/src/toolbar/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/desktop/src/common/mkdirp.js: -------------------------------------------------------------------------------- 1 | const mkdirp = require('mkdirp'); 2 | const Promise = require('bluebird'); 3 | 4 | module.exports = function (path) { 5 | return new Promise((resolve, reject) => { 6 | mkdirp(path, err => { 7 | if (err) return reject(err); 8 | resolve(path); 9 | }); 10 | }); 11 | }; -------------------------------------------------------------------------------- /packages/embed/bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run clear 6 | npm run lint 7 | 8 | babel src --out-dir lib 9 | 10 | export NODE_ENV=dev 11 | webpack --display-error-details 12 | 13 | export NODE_ENV=prod 14 | webpack --display-error-details 15 | 16 | rm -rf ./docs/demo/* 17 | cp -R ./dist/* ./docs/demo/ -------------------------------------------------------------------------------- /packages/desktop/test/e2e/demo.test.js: -------------------------------------------------------------------------------- 1 | describe('demo test', function () { 2 | 3 | beforeEach(function () { 4 | driver.get('/'); 5 | }); 6 | 7 | it('check title', function (done) { 8 | driver.getTitle().then(function (title) { 9 | expect(title).toBe('Hello Mokit'); 10 | done(); 11 | }); 12 | }); 13 | 14 | }); -------------------------------------------------------------------------------- /packages/embed/test/e2e/demo.test.js: -------------------------------------------------------------------------------- 1 | describe('demo test', function () { 2 | 3 | beforeEach(function () { 4 | driver.get('/'); 5 | }); 6 | 7 | it('check title', function (done) { 8 | driver.getTitle().then(function (title) { 9 | expect(title).toBe('Hello Mokit'); 10 | done(); 11 | }); 12 | }); 13 | 14 | }); -------------------------------------------------------------------------------- /packages/desktop/src/common/fs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const Promise = require('bluebird'); 3 | 4 | exports.writeFile = Promise.promisify(fs.writeFile); 5 | exports.readFile = Promise.promisify(fs.readFile); 6 | exports.unlink = Promise.promisify(fs.unlink); 7 | 8 | exports.exists = async function exists(file) { 9 | return new Promise(resolve => { 10 | fs.exists(file, resolve); 11 | }); 12 | }; -------------------------------------------------------------------------------- /packages/desktop/bin/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | npm run build 6 | 7 | #icns converter https://iconverticons.com/online/ 8 | 9 | node ./bin/release.js 10 | 11 | PRJ_PATH=$PWD 12 | 13 | #install deps 14 | cd ./release/Mditor-darwin-x64/Mditor.app/Contents/Resources/app/ 15 | cnpm prune --production 16 | cnpm i 17 | cnpm prune --production 18 | 19 | #dmg dmgCanvas http://www.araelium.com/dmgcanvas/ -------------------------------------------------------------------------------- /packages/desktop/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.0.0 2 | 1. 修正打开或保存对话框默认路径错误问题 3 | 4 | ### 1.0.0Beta10 5 | 1. 新增支持「国际化」目前包括「中文」和「英文」 6 | 2. 新增支持「拖拽」插入图片 7 | 3. 新增加支持「复制」插入图片,并自动保存到当前目录 8 | 9 | ### 1.0.0Beta8 10 | 1. 新增支持导出「演示文稿」 11 | 2. 修复有时序号补全错误问题 12 | 13 | ### 1.0.0Beta7 14 | 1. 改进「偏好设置」功能 15 | 2. 修复有时右键打开文件空白的问题 16 | 17 | ### 1.0.0Beta6 18 | 1. 新增「偏好设置」功能 19 | 2. 新增支持用「相对路径引」入图片 20 | 3. 新增支持「双击标题栏」切换窗口状态 21 | 4. 修复「右键菜单」无法弹出问题 22 | 5. 改进 undo/redo 操作 -------------------------------------------------------------------------------- /packages/embed/src/editor/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | 7 |
-------------------------------------------------------------------------------- /packages/desktop/src/convert/tmpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 12 | 13 | 14 | 15 |
16 | ${content} 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /packages/desktop/src/convert/slide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ${title} 9 | 10 | ${items.map(item=>item).join('\n')} 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/desktop/src/common/blob2base64.js: -------------------------------------------------------------------------------- 1 | const Promise = require('bluebird'); 2 | 3 | module.exports = function blobToBase64(blob) { 4 | return new Promise((resolve, reject) => { 5 | var reader = new FileReader(); 6 | 7 | function onLoadEnd(e) { 8 | reader.removeEventListener('loadend', onLoadEnd, false); 9 | if (e.error) return reject(e.error); 10 | resolve(reader.result); 11 | } 12 | reader.addEventListener('loadend', onLoadEnd, false); 13 | try { 14 | reader.readAsDataURL(blob); 15 | } catch (err) { 16 | console.warn(err); 17 | } 18 | }); 19 | }; -------------------------------------------------------------------------------- /packages/desktop/src/menu/index.js: -------------------------------------------------------------------------------- 1 | const Menu = require('electron').Menu; 2 | const Promise = require('bluebird'); 3 | 4 | exports.createMain = async function () { 5 | let template = await Promise.all([ 6 | require('./file')(), 7 | require('./edit')(), 8 | require('./view')(), 9 | require('./window')(), 10 | require('./help')() 11 | ]); 12 | if (process.platform === 'darwin') { 13 | template.unshift(await require('./main')()); 14 | } 15 | return Menu.buildFromTemplate(template); 16 | }; 17 | 18 | exports.createContextMenu = async function () { 19 | return require('./contextmenu')(); 20 | }; -------------------------------------------------------------------------------- /docs/javascripts/scale.fix.js: -------------------------------------------------------------------------------- 1 | var metas = document.getElementsByTagName('meta'); 2 | var i; 3 | if (navigator.userAgent.match(/iPhone/i)) { 4 | for (i=0; i { 5 | var reader = new FileReader(); 6 | 7 | function onLoadEnd(e) { 8 | reader.removeEventListener('loadend', onLoadEnd, false); 9 | if (e.error) return reject(e.error); 10 | resolve(Buffer.from(reader.result)); 11 | } 12 | reader.addEventListener('loadend', onLoadEnd, false); 13 | try { 14 | reader.readAsArrayBuffer(blob); 15 | } catch (err) { 16 | console.warn(err); 17 | } 18 | }); 19 | }; -------------------------------------------------------------------------------- /packages/desktop/src/menu/view.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const i18n = require('../i18n'); 3 | 4 | module.exports = async() => { 5 | let locale = i18n.locale; 6 | return { 7 | label: locale.view, 8 | submenu: [{ 9 | label: locale.toggleSplit, 10 | accelerator: 'Shift+Alt+S', 11 | click() { 12 | app.execCommand('toggleSplit'); 13 | } 14 | }, 15 | { 16 | label: locale.togglePreview, 17 | accelerator: 'Shift+Alt+V', 18 | click() { 19 | app.execCommand('togglePreview'); 20 | } 21 | } 22 | ] 23 | }; 24 | }; -------------------------------------------------------------------------------- /packages/embed/src/finder/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 |
6 |
7 | 9 | 10 |
11 |
-------------------------------------------------------------------------------- /packages/desktop/docs/javascripts/scale.fix.js: -------------------------------------------------------------------------------- 1 | var metas = document.getElementsByTagName('meta'); 2 | var i; 3 | if (navigator.userAgent.match(/iPhone/i)) { 4 | for (i=0; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | Mditor 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/embed/src/client/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 7 | 8 | 9 |
10 |
-------------------------------------------------------------------------------- /packages/desktop/src/preference/tmpl.md: -------------------------------------------------------------------------------- 1 | # Preferences 2 | 3 | Here is the Mditor "preference settings", each configuration is Yaml format, save after editing can be, [online document](http://mditor.com/doc/preference.html). 4 | 5 | 6 | ### 1. Editor Settings 7 | 8 | ```editor 9 | # tab: 2 10 | # backgroundColor: black 11 | # color: white 12 | ``` 13 | 14 | > With these configurations, tab: The number of designated tab indent space, 0 do not use spaces instead of tab. backgroundColor: Edit area background color, the binary need to add "quotes", such as'#000', color: Edit area foreground. 15 | 16 | 17 | ### 2. Custom Shortcuts 18 | 19 | ```shortcut 20 | # bold: command+b 21 | ``` 22 | 23 | > The format is "command: shortcut", please refer to the sample. -------------------------------------------------------------------------------- /packages/embed/src/viewer/index.less: -------------------------------------------------------------------------------- 1 | .mditor { 2 | .viewer { 3 | overflow: auto; 4 | position: relative; 5 | display: inline-block; 6 | border: none; 7 | outline: none; 8 | width: 100%; 9 | height: 100%; 10 | resize: none; 11 | background-color: transparent; 12 | margin: 0px; 13 | padding: 10px 12px 40px 12px; 14 | color: #333; 15 | word-break: break-all; 16 | word-wrap: break-word; 17 | .markdown-body { 18 | max-width: 900px; 19 | margin: auto; 20 | } 21 | .alert { 22 | color: #ddd; 23 | text-align: center; 24 | position: absolute; 25 | left: 0; 26 | width: 100%; 27 | top: 50%; 28 | font-size: 20px; 29 | margin-top: -15px; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /packages/embed/src/editor/commands.js: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | name: 'undo', 3 | key: '{cmd}+z', 4 | handler() { 5 | this.editor.undo(); 6 | } 7 | }, { 8 | name: 'redo', 9 | key: '{cmd}+shift+z', 10 | handler() { 11 | this.editor.redo(); 12 | } 13 | }, { 14 | name: 'h2', 15 | key: 'shift+alt+2', 16 | handler() { 17 | this.editor.wrapSelectText('## '); 18 | } 19 | }, { 20 | name: 'h3', 21 | key: 'shift+alt+3', 22 | handler() { 23 | this.editor.wrapSelectText('### '); 24 | } 25 | }, { 26 | name: 'h4', 27 | key: 'shift+alt+4', 28 | handler() { 29 | this.editor.wrapSelectText('#### '); 30 | } 31 | }, { 32 | name: 'h5', 33 | key: 'shift+alt+5', 34 | handler() { 35 | this.editor.wrapSelectText('##### '); 36 | } 37 | }]; -------------------------------------------------------------------------------- /packages/desktop/src/window/drapable.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require('mokit').EventEmitter; 2 | 3 | module.exports = function (element) { 4 | let eventer = new EventEmitter(element); 5 | eventer.on('dragover', function (event) { 6 | event.preventDefault(); 7 | }); 8 | eventer.on('drop', function (event) { 9 | event.preventDefault(); 10 | let files = [].slice.call(event.dataTransfer.files); 11 | if (!files || files.length < 1) return; 12 | let firstFile = files[0]; 13 | if (firstFile.type.startsWith('text/')) { 14 | ctx.openFile(firstFile.path); 15 | } else { 16 | let imageFiles = files.filter(file => file.type.startsWith('image/')); 17 | if (imageFiles.length < 1) return; 18 | ctx.insertImage(imageFiles.map(item => item.path)); 19 | } 20 | }); 21 | }; -------------------------------------------------------------------------------- /packages/desktop/test/driver.js: -------------------------------------------------------------------------------- 1 | const webdriver = require('selenium-webdriver'); 2 | const phantomjsPath = require('phantomjs-prebuilt').path; 3 | const pkg = require('../package.json'); 4 | 5 | const capabilities = webdriver.Capabilities.phantomjs(); 6 | capabilities.set("phantomjs.binary.path", phantomjsPath); 7 | const driver = new webdriver.Builder(). 8 | withCapabilities(capabilities). 9 | build(); 10 | 11 | driver.manage().window().maximize(); 12 | 13 | driver._get = driver.get; 14 | driver.get = function (path) { 15 | path = path || '/'; 16 | return this._get(`http://${pkg.dev.host}:${pkg.dev.port}/#!${path}`); 17 | }; 18 | 19 | global.driver = driver; 20 | global.by = webdriver.By; 21 | global.until = webdriver.until; 22 | 23 | by.text = function (text) { 24 | return by.xpath('//*[text()="' + text + '"]'); 25 | }; -------------------------------------------------------------------------------- /packages/embed/test/driver.js: -------------------------------------------------------------------------------- 1 | const webdriver = require('selenium-webdriver'); 2 | const phantomjsPath = require('phantomjs-prebuilt').path; 3 | const pkg = require('../package.json'); 4 | 5 | const capabilities = webdriver.Capabilities.phantomjs(); 6 | capabilities.set("phantomjs.binary.path", phantomjsPath); 7 | const driver = new webdriver.Builder(). 8 | withCapabilities(capabilities). 9 | build(); 10 | 11 | driver.manage().window().maximize(); 12 | 13 | driver._get = driver.get; 14 | driver.get = function (path) { 15 | path = path || '/'; 16 | return this._get(`http://${pkg.dev.host}:${pkg.dev.port}/#!${path}`); 17 | }; 18 | 19 | global.driver = driver; 20 | global.by = webdriver.By; 21 | global.until = webdriver.until; 22 | 23 | by.text = function (text) { 24 | return by.xpath('//*[text()="' + text + '"]'); 25 | }; -------------------------------------------------------------------------------- /packages/desktop/src/menu/help.js: -------------------------------------------------------------------------------- 1 | const shell = require('electron').shell; 2 | const pkg = require('../../package'); 3 | const i18n = require('../i18n'); 4 | 5 | module.exports = async() => { 6 | let locale = i18n.locale; 7 | return { 8 | label: locale.help, 9 | role: 'help', 10 | submenu: [{ 11 | label: `${pkg.displayName} ${locale.homepage}`, 12 | click() { 13 | shell.openExternal(`${pkg.homepage}?locale=${i18n.localeName}`); 14 | } 15 | }, { 16 | label: locale.umlDoc, 17 | click() { 18 | shell.openExternal(`${pkg.homepage}/doc/uml.html?locale=${i18n.localeName}`); 19 | } 20 | }, { 21 | label: locale.slideDoc, 22 | click() { 23 | shell.openExternal(`${pkg.homepage}/doc/slide.html?locale=${i18n.localeName}`); 24 | } 25 | }] 26 | }; 27 | }; -------------------------------------------------------------------------------- /packages/desktop/src/recent/index.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const store = require('../common/store'); 3 | 4 | const STORE_KEY = 'recent'; 5 | const MAX_NUM = 10; 6 | 7 | exports.add = async function (filename) { 8 | let list = (await store.getItem(STORE_KEY)) || []; 9 | let index = list.findIndex(item => item === filename); 10 | if (index > -1) list.splice(index, 1); 11 | list.unshift(filename); 12 | list = list.slice(0, MAX_NUM); 13 | await store.setItem(STORE_KEY, list); 14 | app.addRecentDocument(filename); 15 | await app.createMenu(); 16 | }; 17 | 18 | exports.getItems = async function () { 19 | return (await store.getItem(STORE_KEY)) || []; 20 | }; 21 | 22 | exports.clear = async function () { 23 | await store.removeItem(STORE_KEY); 24 | app.clearRecentDocuments(); 25 | await app.createMenu(); 26 | }; -------------------------------------------------------------------------------- /docs/doc/slide.md: -------------------------------------------------------------------------------- 1 | `slide` 2 | 3 | # 如何编写演示文稿 4 | 5 | 通常 Mditor 仅适用于编写「技术分享」类的「演示文稿」,普通的 Markdown 可能需要简单修改才适合导出为「演示文稿」,按「回车」全屏,按「空格或右箭头」阅读下一页 6 | 7 | `slide` 8 | 9 | # 两种语法 10 | 11 | - 通过 **'slide'** 开始一页文稿 12 | - 通过 **'''slide \r\n'''** 开始一页文稿 13 | 14 | > 编写时请将 **'** 写为 **`**,第一种方式背景和转场动盏将随机产生,第二种方式可指定一些参数,请阅读后边的示例 15 | 16 | `slide` 17 | 18 | # 示例一 19 | 编写时请将 **'** 写为 **`** 20 | ```md 21 | 'slide' 22 | 23 | # 文稿一 24 | 1. 项目一 25 | 2. 项目二 26 | 27 | 'slide' 28 | 29 | # 文稿二 30 | 1. 项目一 31 | 2. 项目二 32 | ``` 33 | 34 | `slide` 35 | 36 | # 示例二 37 | 编写时请将 **'** 写为 **`** 38 | ```md 39 | '''slide 40 | bgcolor: '#000' # 可指定背景色 41 | align: center # 可指定对齐方式 42 | effect: 17 # 可指定转场效果 (1~68) 43 | style: 'color: red' # 可指定样式 44 | ''' 45 | 46 | # 文稿一 47 | 1. 项目一 48 | 2. 项目二 49 | ``` 50 | 51 | ```slide 52 | align: center 53 | ``` 54 | # 谢 谢 -------------------------------------------------------------------------------- /packages/desktop/docs/doc/slide.md: -------------------------------------------------------------------------------- 1 | `slide` 2 | 3 | # 如何编写演示文稿 4 | 5 | 通常 Mditor 仅适用于编写「技术分享」类的「演示文稿」,普通的 Markdown 可能需要简单修改才适合导出为「演示文稿」,按「回车」全屏,按「空格或右箭头」阅读下一页 6 | 7 | `slide` 8 | 9 | # 两种语法 10 | 11 | - 通过 **'slide'** 开始一页文稿 12 | - 通过 **'''slide \r\n'''** 开始一页文稿 13 | 14 | > 编写时请将 **'** 写为 **`**,第一种方式背景和转场动盏将随机产生,第二种方式可指定一些参数,请阅读后边的示例 15 | 16 | `slide` 17 | 18 | # 示例一 19 | 编写时请将 **'** 写为 **`** 20 | ```md 21 | 'slide' 22 | 23 | # 文稿一 24 | 1. 项目一 25 | 2. 项目二 26 | 27 | 'slide' 28 | 29 | # 文稿二 30 | 1. 项目一 31 | 2. 项目二 32 | ``` 33 | 34 | `slide` 35 | 36 | # 示例二 37 | 编写时请将 **'** 写为 **`** 38 | ```md 39 | '''slide 40 | bgcolor: '#000' # 可指定背景色 41 | align: center # 可指定对齐方式 42 | effect: 17 # 可指定转场效果 (1~68) 43 | style: 'color: red' # 可指定样式 44 | ''' 45 | 46 | # 文稿一 47 | 1. 项目一 48 | 2. 项目二 49 | ``` 50 | 51 | ```slide 52 | align: center 53 | ``` 54 | # 谢 谢 -------------------------------------------------------------------------------- /packages/desktop/src/i18n/index.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const yaml = require('../common/yaml'); 3 | const fs = require('../common/fs'); 4 | 5 | exports.getName = async function () { 6 | this.localeName = app.getLocale().toLowerCase(); 7 | return this.localeName; 8 | }; 9 | 10 | exports.load = async function () { 11 | let name = await this.getName(); 12 | let localeFile; 13 | let localeFiles = [ 14 | `${__dirname}/locales/${name}.yml`, 15 | `${__dirname}/locales/${name.split('-')[0]}.yml`, 16 | `${__dirname}/locales/en.yml` 17 | ]; 18 | for (let i = 0; i < localeFiles.length; i++) { 19 | if (await fs.exists(localeFiles[i])) { 20 | localeFile = localeFiles[i]; 21 | break; 22 | } 23 | } 24 | let buffer = await fs.readFile(localeFile); 25 | this.locale = yaml(buffer.toString()); 26 | return this.locale; 27 | }; -------------------------------------------------------------------------------- /packages/desktop/src/update/index.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../../package'); 2 | const store = require('../common/store'); 3 | const fetch = require('node-fetch'); 4 | const utils = require('ntils'); 5 | const os = require('os'); 6 | const i18n = require('../i18n'); 7 | 8 | const STORE_KEY = 'update'; 9 | const ONE_DAY_MS = 10800000; //3 个小时内只提示一次 10 | 11 | exports.check = async function (force) { 12 | //如果不是强制检查,则对比上次更新时间 13 | if (!force) { 14 | let prevTime = await store.getItem(STORE_KEY); 15 | if (Date.now() - prevTime < ONE_DAY_MS) return; 16 | } 17 | //拉取服务器信息 18 | let response = await fetch(`${pkg.update.url}?locale=${i18n.localeName}`); 19 | let info = await response.json(); 20 | if (info.version == pkg.version) return; 21 | if (utils.isArray(info.detail)) { 22 | info.detail = info.detail.join(os.EOL); 23 | } 24 | //记录本次更新时间 25 | store.setItem(STORE_KEY, Date.now()); 26 | return info; 27 | }; -------------------------------------------------------------------------------- /packages/embed/src/toolbar/index.less: -------------------------------------------------------------------------------- 1 | .mditor { 2 | .toolbar { 3 | margin: 0px; 4 | padding: 4px; 5 | height: 100%; 6 | position: relative; 7 | .item { 8 | transition: .3s; 9 | height: 27px; 10 | width: 27px; 11 | text-align: center; 12 | line-height: 25px; 13 | font-size: 14px; 14 | border: 1px solid transparent; 15 | border-radius: 3px; 16 | margin: 1px; 17 | color: #888; 18 | transition: .2s; 19 | -webkit-transition: .2s; 20 | position: relative; 21 | vertical-align: middle; 22 | &:hover { 23 | color: #444; 24 | border: 1px solid #ddd; 25 | } 26 | &:active, &.active { 27 | border: 1px solid #ddd; 28 | background-color: #ebebeb; 29 | box-shadow: 0 0 3px rgba(0, 0, 0, .1) inset; 30 | } 31 | &.control { 32 | float: right; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /packages/desktop/src/common/store.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const fs = require('../common/fs'); 3 | 4 | const DATA_PATH = app.getPath('userData'); 5 | 6 | exports.setItem = async function (key, value) { 7 | let storeFile = `${DATA_PATH}/${key}.json`; 8 | try { 9 | await fs.writeFile(storeFile, JSON.stringify(value)); 10 | } catch (err) { 11 | console.error('setItem', err); 12 | } 13 | }; 14 | 15 | exports.getItem = async function (key) { 16 | let storeFile = `${DATA_PATH}/${key}.json`; 17 | try { 18 | let buffer = await fs.readFile(storeFile); 19 | return JSON.parse(buffer.toString()); 20 | } catch (err) { 21 | console.error('getItem', err); 22 | } 23 | }; 24 | 25 | exports.removeItem = async function (key) { 26 | let storeFile = `${DATA_PATH}/${key}.json`; 27 | try { 28 | await fs.unlink(storeFile); 29 | } catch (err) { 30 | console.error('removeItem', err); 31 | } 32 | }; -------------------------------------------------------------------------------- /packages/embed/src/finder/index.less: -------------------------------------------------------------------------------- 1 | .mditor { 2 | .body { 3 | .finder { 4 | position: absolute; 5 | top: 0; 6 | right: 0; 7 | padding: 8px; 8 | background-color: rgba(240, 240, 240, .9); 9 | box-shadow: -1px 1px 2px 0 rgba(0, 0, 0, .18); 10 | z-index: 2; 11 | input { 12 | outline: 0; 13 | font-size: 13px; 14 | padding: 5px; 15 | height: 25px; 16 | vertical-align: middle; 17 | border: solid 1px #ddd; 18 | margin: 2px; 19 | min-width: 200px; 20 | -webkit-font-smoothing: antialiased; 21 | } 22 | i.fa { 23 | vertical-align: middle; 24 | font-size: 12px; 25 | padding: 5px; 26 | outline: 0; 27 | color: #888; 28 | &:hover { 29 | color: #555; 30 | } 31 | } 32 | display: none; 33 | &.active { 34 | display: block; 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /packages/desktop/bin/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDocumentTypes 6 | 7 | 8 | CFBundleTypeName 9 | Mditor File 10 | CFBundleTypeRole 11 | Editor 12 | CFBundleTypeOSTypes 13 | 14 | TEXT 15 | utxt 16 | TUTX 17 | **** 18 | 19 | CFBundleTypeIconFile 20 | ../design/icon.icns 21 | CFBundleTypeExtensions 22 | 23 | md 24 | txt 25 | text 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /packages/embed/src/editor/stack.js: -------------------------------------------------------------------------------- 1 | const Class = require('cify').Class; 2 | const utils = require('ntils'); 3 | 4 | const Stack = new Class({ 5 | constructor(item) { 6 | this.init(item); 7 | }, 8 | init(item) { 9 | this.undoList = [item || { 10 | value: null 11 | }]; 12 | this.redoList = []; 13 | }, 14 | push(item) { 15 | if (this.last() === item) return; 16 | this.undoList.push(item); 17 | }, 18 | last() { 19 | return this.undoList[this.undoList.length - 1]; 20 | }, 21 | undo() { 22 | if (this.undoList.length > 1) { 23 | let item = this.undoList.pop(); 24 | if (utils.isNull(item) || utils.isNull(item.value)) return; 25 | this.redoList.push(item); 26 | } 27 | return this.last(); 28 | }, 29 | redo() { 30 | let item = this.redoList.pop(); 31 | if (utils.isNull(item) || utils.isNull(item.value)) return; 32 | this.undoList.push(item); 33 | return item; 34 | } 35 | }); 36 | 37 | module.exports = Stack; -------------------------------------------------------------------------------- /packages/desktop/src/menu/window.js: -------------------------------------------------------------------------------- 1 | const i18n = require('../i18n'); 2 | 3 | module.exports = async() => { 4 | let locale = i18n.locale; 5 | const windowMenu = { 6 | label: locale.window, 7 | role: 'window', 8 | submenu: [{ 9 | label: locale.minimize, 10 | role: 'minimize' 11 | }, 12 | { 13 | label: locale.close, 14 | role: 'close' 15 | } 16 | ] 17 | }; 18 | 19 | if (process.platform === 'darwin') { 20 | windowMenu.submenu = [{ 21 | label: locale.close, 22 | accelerator: 'CmdOrCtrl+W', 23 | role: 'close' 24 | }, 25 | { 26 | label: locale.minimize, 27 | accelerator: 'CmdOrCtrl+M', 28 | role: 'minimize' 29 | }, 30 | { 31 | label: locale.zoom, 32 | role: 'zoom' 33 | }, 34 | { 35 | type: 'separator' 36 | }, 37 | { 38 | label: locale.front, 39 | role: 'front' 40 | } 41 | ]; 42 | } 43 | 44 | return windowMenu; 45 | }; -------------------------------------------------------------------------------- /packages/embed/lib/editor/commands.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | module.exports = [{ 4 | name: 'undo', 5 | key: '{cmd}+z', 6 | /*istanbul ignore next*/handler: function handler() { 7 | this.editor.undo(); 8 | } 9 | }, { 10 | name: 'redo', 11 | key: '{cmd}+shift+z', 12 | /*istanbul ignore next*/handler: function handler() { 13 | this.editor.redo(); 14 | } 15 | }, { 16 | name: 'h2', 17 | key: 'shift+alt+2', 18 | /*istanbul ignore next*/handler: function handler() { 19 | this.editor.wrapSelectText('## '); 20 | } 21 | }, { 22 | name: 'h3', 23 | key: 'shift+alt+3', 24 | /*istanbul ignore next*/handler: function handler() { 25 | this.editor.wrapSelectText('### '); 26 | } 27 | }, { 28 | name: 'h4', 29 | key: 'shift+alt+4', 30 | /*istanbul ignore next*/handler: function handler() { 31 | this.editor.wrapSelectText('#### '); 32 | } 33 | }, { 34 | name: 'h5', 35 | key: 'shift+alt+5', 36 | /*istanbul ignore next*/handler: function handler() { 37 | this.editor.wrapSelectText('##### '); 38 | } 39 | }]; -------------------------------------------------------------------------------- /packages/embed/src/viewer/index.js: -------------------------------------------------------------------------------- 1 | const mokit = require('mokit'); 2 | 3 | require('./index.less'); 4 | 5 | const Viewer = new mokit.Component({ 6 | template: require('./index.html'), 7 | 8 | data() { 9 | return { 10 | html: '', 11 | alert: '预览区域' 12 | }; 13 | }, 14 | 15 | props: { 16 | mditor: null, 17 | value: { 18 | get() { 19 | return this._value; 20 | }, 21 | set(value) { 22 | this._value = value; 23 | let beforeEvent = { value: this._value }; 24 | this.$emit('beforeRender', beforeEvent); 25 | this.mditor.parser.parse(beforeEvent.value, (err, result) => { 26 | let afterEvent = { value: result || err }; 27 | this.$emit('afterRender', afterEvent); 28 | this.html = afterEvent.value; 29 | }); 30 | } 31 | } 32 | }, 33 | 34 | onClick(event) { 35 | event.preventDefault(); 36 | let tag = event.target; 37 | if (tag.tagName == 'A') { 38 | let href = tag.getAttribute('href'); 39 | if (href) window.open(href, '_blank'); 40 | } 41 | } 42 | 43 | }); 44 | 45 | module.exports = Viewer; -------------------------------------------------------------------------------- /packages/desktop/src/i18n/locales/zh.yml: -------------------------------------------------------------------------------- 1 | about: 关于 2 | checkUpdate: 检查更新 3 | preference: 偏好设置 4 | resetPreference: 重置偏好设置 5 | services: 服务 6 | hide: 隐藏 7 | hideOthers: 隐藏其它 8 | unHide: 取消隐藏 9 | quit: 退出 10 | clearRecent: 清除最近编辑 11 | file: 文件 12 | newFile: 新建 13 | open: 打开 14 | recentItems: 最近编辑 15 | save: 保存 16 | saveAs: 另存为 17 | export: 导出 18 | pdf: PDF 19 | html: HTML 20 | image: 图片 21 | slide: 演示 22 | edit: 编辑 23 | undo: 撤销 24 | redo: 重做 25 | cut: 剪切 26 | copy: 复制 27 | paste: 粘贴 28 | selectAll: 全选 29 | delete: 删除 30 | findAndReplace: 查找和替换 31 | view: 视图 32 | toggleSplit: 切换分屏 33 | togglePreview: 切换预览 34 | previewArea: 预览区域 35 | window: 窗口 36 | minimize: 最小化 37 | close: 关闭 38 | zoom: 缩放 39 | front: 置于最前 40 | help: 帮助 41 | homepage: 主页 42 | umlDoc: 如何生成 UML 图形 43 | slideDoc: 如何编写「演示」 44 | currentlyTheLatestVersion: 当前已是最新版本 45 | goDownload: 前往下载 46 | donNotUpdate: 暂不更新 47 | discoverNewVersion: 发现新版本 48 | recommendDownload: 建议现在就去下载新版本 49 | cancel: 取消 50 | donNotSave: 不保存 51 | confirmSave: 确认保存 52 | saveAlertDetail: 文件 "${filename}" 还未保存,是否现在保存? 53 | bold: 粗体 54 | italic: 斜体 55 | underline: 下划线 56 | strikethrough: 删除线 57 | header: 标题 58 | quote: 引用 59 | code: 代码 60 | listOl: 有序列表 61 | listUl: 无序列表 62 | link: 链接 63 | table: 表格 64 | line: 分隔线 -------------------------------------------------------------------------------- /packages/desktop/src/window/index.less: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html, body, .app { 6 | height: 100%; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | .mditor { 12 | margin: 0px !important; 13 | width: 100% !important; 14 | height: 100% !important; 15 | min-width: 100% !important; 16 | min-height: 100% !important; 17 | max-width: 100% !important; 18 | max-height: 100% !important; 19 | border-radius: 0px !important; 20 | border: none !important; 21 | .head { 22 | padding-top: 1px !important; 23 | padding-left: 72px !important; 24 | -webkit-app-region: drag; 25 | .toolbar { 26 | .item { 27 | -webkit-app-region: no-drag 28 | } 29 | .item.fa-arrows-alt { 30 | display: none !important; 31 | } 32 | } 33 | } 34 | .editor { 35 | font-size: 16px; 36 | line-height: normal; 37 | } 38 | .viewer { 39 | .highlight-alert { 40 | border-bottom: solid 1px #ddd; 41 | padding-bottom: 5px; 42 | margin-bottom: 10px; 43 | color: firebrick; 44 | font-size: 12px; 45 | } 46 | } 47 | } 48 | 49 | .fullscreen { 50 | .mditor { 51 | .head { 52 | padding-left: 0 !important; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /packages/embed/lib/editor/stack.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var Class = require('cify').Class; 4 | var utils = require('ntils'); 5 | 6 | var Stack = new Class({ 7 | /*istanbul ignore next*/constructor: function constructor(item) { 8 | this.init(item); 9 | }, 10 | /*istanbul ignore next*/init: function init(item) { 11 | this.undoList = [item || { 12 | value: null 13 | }]; 14 | this.redoList = []; 15 | }, 16 | /*istanbul ignore next*/push: function push(item) { 17 | if (this.last() === item) return; 18 | this.undoList.push(item); 19 | }, 20 | /*istanbul ignore next*/last: function last() { 21 | return this.undoList[this.undoList.length - 1]; 22 | }, 23 | /*istanbul ignore next*/undo: function undo() { 24 | if (this.undoList.length > 1) { 25 | var item = this.undoList.pop(); 26 | if (utils.isNull(item) || utils.isNull(item.value)) return; 27 | this.redoList.push(item); 28 | } 29 | return this.last(); 30 | }, 31 | /*istanbul ignore next*/redo: function redo() { 32 | var item = this.redoList.pop(); 33 | if (utils.isNull(item) || utils.isNull(item.value)) return; 34 | this.undoList.push(item); 35 | return item; 36 | } 37 | }); 38 | 39 | module.exports = Stack; -------------------------------------------------------------------------------- /packages/embed/karma.conf.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config.js'); 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | browsers: ['PhantomJS'], 6 | frameworks: ['jasmine'], 7 | files: [ 8 | 'test/unit/**/*.test.js' 9 | ], 10 | reporters: ['spec', 'coverage'], 11 | preprocessors: { 12 | 'test/unit/**/*.test.js': ['webpack'], 13 | }, 14 | coverageReporter: { 15 | dir: './build/coverage/', 16 | reporters: [{ 17 | type: 'html', 18 | subdir: 'report-html' 19 | }, { 20 | type: 'text-summary', 21 | subdir: 'report-text', 22 | file: 'text-summary.txt' 23 | }, { 24 | type: 'text', 25 | subdir: 'report-text', 26 | file: 'text.txt' 27 | }, ] 28 | }, 29 | webpack: { 30 | devtool: 'inline-source-map', 31 | module: { 32 | preLoaders: [{ 33 | test: /\.js$/, 34 | exclude: [/node_modules/, /\.test\.js$/, /mocks/], 35 | loader: 'isparta' 36 | }], 37 | loaders: webpackConfig.module.loaders 38 | }, 39 | plugins: webpackConfig.plugins, 40 | resolve: webpackConfig.resolve 41 | }, 42 | webpackServer: { 43 | noInfo: true 44 | } 45 | }); 46 | }; -------------------------------------------------------------------------------- /packages/desktop/karma.conf.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config.js'); 2 | 3 | module.exports = function (config) { 4 | config.set({ 5 | browsers: ['PhantomJS'], 6 | frameworks: ['jasmine'], 7 | files: [ 8 | 'test/unit/**/*.test.js' 9 | ], 10 | reporters: ['spec', 'coverage'], 11 | preprocessors: { 12 | 'test/unit/**/*.test.js': ['webpack'], 13 | }, 14 | coverageReporter: { 15 | dir: './build/coverage/', 16 | reporters: [{ 17 | type: 'html', 18 | subdir: 'report-html' 19 | }, { 20 | type: 'text-summary', 21 | subdir: 'report-text', 22 | file: 'text-summary.txt' 23 | }, { 24 | type: 'text', 25 | subdir: 'report-text', 26 | file: 'text.txt' 27 | }, ] 28 | }, 29 | webpack: { 30 | devtool: 'inline-source-map', 31 | module: { 32 | preLoaders: [{ 33 | test: /\.js$/, 34 | exclude: [/node_modules/, /\.test\.js$/, /mocks/], 35 | loader: 'isparta' 36 | }], 37 | loaders: webpackConfig.module.loaders 38 | }, 39 | plugins: webpackConfig.plugins, 40 | resolve: webpackConfig.resolve 41 | }, 42 | webpackServer: { 43 | noInfo: true 44 | } 45 | }); 46 | }; -------------------------------------------------------------------------------- /packages/desktop/src/menu/contextmenu.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | const Menu = electron.Menu; 3 | const MenuItem = electron.MenuItem; 4 | const i18n = require('../i18n'); 5 | 6 | module.exports = async() => { 7 | let locale = i18n.locale; 8 | const items = [{ 9 | label: locale.undo, 10 | role: 'undo', 11 | accelerator: 'CmdOrCtrl+Z' 12 | }, 13 | { 14 | label: locale.redo, 15 | role: 'redo', 16 | accelerator: 'Shift+CmdOrCtrl+Z' 17 | }, 18 | { 19 | type: 'separator' 20 | }, 21 | { 22 | label: locale.cut, 23 | role: 'cut', 24 | accelerator: 'CmdOrCtrl+X' 25 | }, 26 | { 27 | label: locale.copy, 28 | role: 'copy', 29 | accelerator: 'CmdOrCtrl+C' 30 | }, 31 | { 32 | label: locale.paste, 33 | role: 'paste', 34 | accelerator: 'CmdOrCtrl+V' 35 | }, 36 | { 37 | label: locale.delete, 38 | role: 'delete' 39 | }, 40 | { 41 | type: 'separator' 42 | }, 43 | { 44 | label: locale.selectAll, 45 | role: 'selectall', 46 | accelerator: 'CmdOrCtrl+A' 47 | } 48 | ]; 49 | 50 | const menu = new Menu(); 51 | items.forEach(item => { 52 | menu.append(new MenuItem(item)); 53 | }); 54 | 55 | return menu; 56 | }; -------------------------------------------------------------------------------- /packages/desktop/src/uml/index.js: -------------------------------------------------------------------------------- 1 | const encodeer = require('./encodeer'); 2 | const fetch = global.fetch ? global.fetch : require('node-fetch'); 3 | const Class = require('cify').Class; 4 | 5 | const UML_ERROR = /syntax error/i; 6 | 7 | function createAlert(message, detail) { 8 | return `
${message}
${detail}`; 9 | } 10 | 11 | const UMLParser = new Class({ 12 | 13 | //http://www.plantuml.com/plantuml/svg/ 14 | //http://www.plantuml.com/plantuml/png/ 15 | parse(code, lang, done) { 16 | if (!code) return done(null, ''); 17 | let params = unescape(encodeURIComponent(code)); 18 | let url = `http://www.plantuml.com/plantuml/svg/${encodeer.encode64(encodeer.zipDeflate(params, 9))}`; 19 | //发生错误时也不用 done 的第一个参数传递, 20 | //这样不让 mditor embed 认为是文档解析错误了 21 | //让 err 作为正确的结果返回,错误将显示在「图形」位置 22 | fetch(url).then((res, err) => { 23 | if (err) done(null, createAlert('发生了错误。', JSON.stringify(err))); 24 | res.text().then(result => { 25 | if (UML_ERROR.test(result)) { 26 | done(null, createAlert('语法错误,已显示原代码。', code)); 27 | }; 28 | done(null, result); 29 | }); 30 | }).catch(err => { 31 | done(null, createAlert('暂时无法渲染 UML 图形,请检查网络设置。', code)); 32 | }); 33 | } 34 | 35 | }); 36 | 37 | module.exports = UMLParser; -------------------------------------------------------------------------------- /packages/embed/src/client/shortcut.js: -------------------------------------------------------------------------------- 1 | const shortcuts = require('shortcut-key'); 2 | const utils = require('ntils'); 3 | 4 | shortcuts.filter = event => { 5 | return !!event.target; 6 | }; 7 | 8 | const Shortcut = module.exports = function (mditor) { 9 | utils.defineFreezeProp(this, 'mditor', mditor); 10 | }; 11 | 12 | Shortcut.prototype._inRegion = function (target, owner) { 13 | if (!target) return false; 14 | owner = owner || this.mditor.editor.$element; 15 | if (utils.isFunction(owner)) owner = owner(this.mditor); 16 | return (target === owner) || this._inRegion(target.parentNode, owner); 17 | }; 18 | 19 | Shortcut.prototype.bind = function (key, cmd, allowDefault, owner) { 20 | let mditor = this.mditor; 21 | //检查参数 22 | if (!key || !cmd) return; 23 | key = key.replace('{cmd}', mditor.CMD); 24 | shortcuts(key, event => { 25 | if (!this._inRegion(event.target, owner)) return; 26 | //禁用浏览器默认快捷键 27 | if (!allowDefault) { 28 | event.preventDefault(); 29 | } 30 | if (cmd instanceof Function) { 31 | cmd.call(mditor, event); 32 | } else { 33 | mditor.execCommand(cmd, event); 34 | } 35 | setTimeout(() => { 36 | mditor.focus(); 37 | }, 0); 38 | }); 39 | }; 40 | 41 | Shortcut.prototype.unbind = function (key) { 42 | shortcuts.unbind(key); 43 | }; -------------------------------------------------------------------------------- /packages/desktop/src/menu/edit.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const i18n = require('../i18n'); 3 | 4 | module.exports = async() => { 5 | let locale = i18n.locale; 6 | return { 7 | label: locale.edit, 8 | submenu: [{ 9 | label: locale.undo, 10 | accelerator: 'CmdOrCtrl+Z', 11 | click() { 12 | app.execCommand('undo'); 13 | } 14 | }, 15 | { 16 | label: locale.redo, 17 | accelerator: 'CmdOrCtrl+Shift+Z', 18 | click() { 19 | app.execCommand('redo'); 20 | } 21 | }, 22 | { 23 | type: 'separator' 24 | }, 25 | { 26 | label: locale.cut, 27 | role: 'cut' 28 | }, 29 | { 30 | label: locale.copy, 31 | role: 'copy' 32 | }, 33 | { 34 | label: locale.paste, 35 | role: 'paste' 36 | }, 37 | { 38 | label: locale.delete, 39 | role: 'delete' 40 | }, 41 | { 42 | label: locale.selectAll, 43 | role: 'selectall' 44 | }, 45 | { 46 | type: 'separator' 47 | }, 48 | { 49 | label: locale.findAndReplace, 50 | accelerator: 'CmdOrCtrl+F', 51 | click() { 52 | app.execCommand('find'); 53 | } 54 | } 55 | ] 56 | }; 57 | }; -------------------------------------------------------------------------------- /packages/embed/dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | mditor 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 |
22 | 25 |
26 | 27 | 28 | 29 | 30 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /packages/embed/src/editor/index.less: -------------------------------------------------------------------------------- 1 | .mditor { 2 | .editor { 3 | position: relative; 4 | display: inline-block; 5 | width: 100%; 6 | height: 100%; 7 | -webkit-text-size-adjust: none; 8 | transform: translateZ(0); 9 | .textarea, .backdrop { 10 | position: absolute; 11 | border: none; 12 | outline: none; 13 | display: block; 14 | left: 0; 15 | top: 0; 16 | width: 100%; 17 | height: 100%; 18 | resize: none; 19 | background-color: transparent; 20 | margin: 0px; 21 | overflow-x: hidden; 22 | overflow-y: auto; 23 | transition: transform 1s; 24 | letter-spacing: 1px; 25 | } 26 | .textarea, .backdrop .inner { 27 | padding: 8px 12px; 28 | white-space: pre-wrap; 29 | word-wrap: break-word; 30 | } 31 | .textarea { 32 | z-index: 1; 33 | color: #444; 34 | } 35 | .backdrop { 36 | z-index: 0; 37 | pointer-events: none; 38 | overflow: hidden; 39 | padding: 0px; 40 | .inner { 41 | color: transparent; 42 | mark { 43 | border-radius: 3px; 44 | color: transparent; 45 | background-color: rgba(255, 245, 75, .8); 46 | &.active { 47 | background-color: rgba(145, 225, 255, .8); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /packages/desktop/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | parser: babel-eslint 2 | rules: 3 | strict: 4 | - 0 5 | indent: 6 | - 2 7 | - 2 8 | quotes: 9 | - 2 10 | - single 11 | linebreak-style: 12 | - 2 13 | - unix 14 | semi: 15 | - 2 16 | - always 17 | no-multi-spaces: 18 | - 2 19 | no-self-compare: 20 | - 2 21 | max-depth: 22 | - 2 23 | - 4 24 | max-nested-callbacks: 25 | - 2 26 | - 4 27 | max-params: 28 | - 2 29 | - 4 30 | max-statements: 31 | - 2 32 | - 25 33 | max-statements-per-line: 34 | - 2 35 | max-len: 36 | - 2 37 | - 120 38 | multiline-ternary: 39 | - 0 40 | callback-return: 41 | - 2 42 | handle-callback-err: 43 | - 2 44 | array-bracket-spacing: 45 | - 2 46 | no-const-assign: 47 | - 2 48 | no-return-assign: 49 | - 0 50 | no-inner-declarations: 51 | - 2 52 | no-var: 53 | - 2 54 | no-console: 55 | - 1 56 | no-lonely-if: 57 | - 2 58 | require-jsdoc: 59 | - 2 60 | - require: 61 | FunctionDeclaration: true 62 | MethodDefinition: true 63 | ClassDeclaration: true 64 | valid-jsdoc: 65 | - 2 66 | comma-dangle: 67 | - 2 68 | - never 69 | no-undef: 70 | - 2 71 | env: 72 | es6: true 73 | browser: true 74 | commonjs: true 75 | extends: 'eslint:recommended' 76 | ecmaFeatures: 77 | jsx: true -------------------------------------------------------------------------------- /packages/embed/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | parser: babel-eslint 2 | rules: 3 | strict: 4 | - 0 5 | indent: 6 | - 2 7 | - 2 8 | quotes: 9 | - 2 10 | - single 11 | linebreak-style: 12 | - 2 13 | - unix 14 | semi: 15 | - 2 16 | - always 17 | no-multi-spaces: 18 | - 2 19 | no-self-compare: 20 | - 2 21 | max-depth: 22 | - 2 23 | - 4 24 | max-nested-callbacks: 25 | - 2 26 | - 4 27 | max-params: 28 | - 2 29 | - 4 30 | max-statements: 31 | - 2 32 | - 25 33 | max-statements-per-line: 34 | - 2 35 | max-len: 36 | - 2 37 | - 120 38 | multiline-ternary: 39 | - 0 40 | callback-return: 41 | - 2 42 | handle-callback-err: 43 | - 2 44 | array-bracket-spacing: 45 | - 2 46 | no-const-assign: 47 | - 2 48 | no-return-assign: 49 | - 0 50 | no-inner-declarations: 51 | - 2 52 | no-var: 53 | - 2 54 | no-console: 55 | - 1 56 | no-lonely-if: 57 | - 2 58 | require-jsdoc: 59 | - 2 60 | - require: 61 | FunctionDeclaration: true 62 | MethodDefinition: true 63 | ClassDeclaration: true 64 | valid-jsdoc: 65 | - 2 66 | comma-dangle: 67 | - 2 68 | - never 69 | no-undef: 70 | - 2 71 | env: 72 | es6: true 73 | browser: true 74 | commonjs: true 75 | extends: 'eslint:recommended' 76 | ecmaFeatures: 77 | jsx: true -------------------------------------------------------------------------------- /packages/embed/docs/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | mditor 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 |
22 | 25 |
26 | 27 | 28 | 29 | 30 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt](http://mditor.com/assets/screen-shot.png) 2 | 3 | 4 | # 简单说明 5 | Mditor 最早只有「组件版」,随着「桌面版」的发布,Mditor 目前有两个版本: 6 | - 可嵌入到任意 Web 应用的 Embed 版本,这是一桌面版的基础,Repo: [https://github.com/houfeng/mditor](https://github.com/Houfeng/mditor) 7 | - 独立的桌面版本,目前仅有 Mac 版本,主页:[http://mditor.com](http://mditor.com),Repo: [https://github.com/houfeng/mditor-desktop](https://github.com/houfeng/mditor-desktop) 8 | 9 | 10 | # 相关特性 11 | 除常规的编辑功能,Mditor 桌面版还有如下特性 12 | - 多文件编辑,Mditor 桌面版是一个「多窗口」应用,可以通过「菜单、Dock、右键菜单」打开多个窗口实例进行多件编辑 13 | - 支持 GFM,如表格等(GFM 是 Github 拓展的基于 Markdown 的一种纯文本的书写格式) 14 | - 自动完成,Mditor 支持「无序列表、有序列表、引用」的自动完成,以辅助输入。 15 | - 多种编辑语言的的「代码高亮」支持(通过 ``` 语法) 16 | - 分屏实时预览,全屏预览 17 | - 导出 「HTML、PDF、Image」等功能。 18 | 19 | # 如何参与 20 | - 如果有任何问题或建议,可以直接发起 [Issue](https://github.com/Houfeng/mditor-desktop/issues) 21 | - 当然,你也可以直接向 Mditor 发起 [Pull Request](https://github.com/Houfeng/mditor-desktop/pulls) 22 | - 非常欢迎,直接给 Mditor [加个 Star](https://github.com/houfeng/mditor-desktop),这将是对 Midior 不错的鼓励,它会变成动力。 23 | 24 | 25 | ## 开发指南 26 | 27 | ##### Clone 源码 28 | ```sh 29 | $ git clone git@github.com:Houfeng/mditor-desktop.git your_path 30 | ``` 31 | 32 | ##### 安装依赖 33 | 前提是需要安装好 Nodejs 和 npm(建议用 cnpm 可以通过国内镜象加速) 34 | ```sh 35 | $ npm install 36 | ``` 37 | 38 | ##### 自动构建 39 | 将会自动进行基于 Webpack 的构建(部分资源需要 Webpack 打包),并将 Watch 文件的改动然后自动进行构建 40 | ```sh 41 | $ npm run dev 42 | ``` 43 | 44 | ##### 启动程序 45 | 将会启动开发中的 Mditor 46 | ```sh 47 | $ npm start 48 | ``` -------------------------------------------------------------------------------- /packages/embed/lib/client/shortcut.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var shortcuts = require('shortcut-key'); 4 | var utils = require('ntils'); 5 | 6 | shortcuts.filter = function (event) { 7 | return !!event.target; 8 | }; 9 | 10 | var Shortcut = module.exports = function (mditor) { 11 | utils.defineFreezeProp(this, 'mditor', mditor); 12 | }; 13 | 14 | Shortcut.prototype._inRegion = function (target, owner) { 15 | if (!target) return false; 16 | owner = owner || this.mditor.editor.$element; 17 | if (utils.isFunction(owner)) owner = owner(this.mditor); 18 | return target === owner || this._inRegion(target.parentNode, owner); 19 | }; 20 | 21 | Shortcut.prototype.bind = function (key, cmd, allowDefault, owner) { 22 | /*istanbul ignore next*/var _this = this; 23 | 24 | var mditor = this.mditor; 25 | //检查参数 26 | if (!key || !cmd) return; 27 | key = key.replace('{cmd}', mditor.CMD); 28 | shortcuts(key, function (event) { 29 | if (! /*istanbul ignore next*/_this._inRegion(event.target, owner)) return; 30 | //禁用浏览器默认快捷键 31 | if (!allowDefault) { 32 | event.preventDefault(); 33 | } 34 | if (cmd instanceof Function) { 35 | cmd.call(mditor, event); 36 | } else { 37 | mditor.execCommand(cmd, event); 38 | } 39 | setTimeout(function () { 40 | mditor.focus(); 41 | }, 0); 42 | }); 43 | }; 44 | 45 | Shortcut.prototype.unbind = function (key) { 46 | shortcuts.unbind(key); 47 | }; -------------------------------------------------------------------------------- /packages/embed/lib/viewer/index.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var mokit = require('mokit'); 4 | 5 | require('./index.less'); 6 | 7 | var Viewer = new mokit.Component({ 8 | template: require('./index.html'), 9 | 10 | /*istanbul ignore next*/data: function data() { 11 | return { 12 | html: '', 13 | alert: '预览区域' 14 | }; 15 | }, 16 | 17 | 18 | props: { 19 | mditor: null, 20 | value: { 21 | /*istanbul ignore next*/get: function get() { 22 | return this._value; 23 | }, 24 | /*istanbul ignore next*/set: function set(value) { 25 | /*istanbul ignore next*/var _this = this; 26 | 27 | this._value = value; 28 | var beforeEvent = { value: this._value }; 29 | this.$emit('beforeRender', beforeEvent); 30 | this.mditor.parser.parse(beforeEvent.value, function (err, result) { 31 | var afterEvent = { value: result || err }; 32 | /*istanbul ignore next*/_this.$emit('afterRender', afterEvent); 33 | /*istanbul ignore next*/_this.html = afterEvent.value; 34 | }); 35 | } 36 | } 37 | }, 38 | 39 | /*istanbul ignore next*/onClick: function onClick(event) { 40 | event.preventDefault(); 41 | var tag = event.target; 42 | if (tag.tagName == 'A') { 43 | var href = tag.getAttribute('href'); 44 | if (href) window.open(href, '_blank'); 45 | } 46 | } 47 | }); 48 | 49 | module.exports = Viewer; -------------------------------------------------------------------------------- /packages/desktop/README.md: -------------------------------------------------------------------------------- 1 | ![alt](http://mditor.com/assets/screen-shot.png) 2 | 3 | 4 | # 简单说明 5 | Mditor 最早只有「组件版」,随着「桌面版」的发布,Mditor 目前有两个版本: 6 | - 可嵌入到任意 Web 应用的 Embed 版本,这是一桌面版的基础,Repo: [https://github.com/houfeng/mditor](https://github.com/Houfeng/mditor) 7 | - 独立的桌面版本,目前仅有 Mac 版本,主页:[http://mditor.com](http://mditor.com),Repo: [https://github.com/houfeng/mditor-desktop](https://github.com/houfeng/mditor-desktop) 8 | 9 | 10 | # 相关特性 11 | 除常规的编辑功能,Mditor 桌面版还有如下特性 12 | - 多文件编辑,Mditor 桌面版是一个「多窗口」应用,可以通过「菜单、Dock、右键菜单」打开多个窗口实例进行多件编辑 13 | - 支持 GFM,如表格等(GFM 是 Github 拓展的基于 Markdown 的一种纯文本的书写格式) 14 | - 自动完成,Mditor 支持「无序列表、有序列表、引用」的自动完成,以辅助输入。 15 | - 多种编辑语言的的「代码高亮」支持(通过 ``` 语法) 16 | - 分屏实时预览,全屏预览 17 | - 导出 「HTML、PDF、Image」等功能。 18 | 19 | # 如何参与 20 | - 如果有任何问题或建议,可以直接发起 [Issue](https://github.com/Houfeng/mditor-desktop/issues) 21 | - 当然,你也可以直接向 Mditor 发起 [Pull Request](https://github.com/Houfeng/mditor-desktop/pulls) 22 | - 非常欢迎,直接给 Mditor [加个 Star](https://github.com/houfeng/mditor-desktop),这将是对 Midior 不错的鼓励,它会变成动力。 23 | 24 | 25 | ## 开发指南 26 | 27 | ##### Clone 源码 28 | ```sh 29 | $ git clone git@github.com:Houfeng/mditor-desktop.git your_path 30 | ``` 31 | 32 | ##### 安装依赖 33 | 前提是需要安装好 Nodejs 和 npm(建议用 cnpm 可以通过国内镜象加速) 34 | ```sh 35 | $ npm install 36 | ``` 37 | 38 | ##### 自动构建 39 | 将会自动进行基于 Webpack 的构建(部分资源需要 Webpack 打包),并将 Watch 文件的改动然后自动进行构建 40 | ```sh 41 | $ npm run dev 42 | ``` 43 | 44 | ##### 启动程序 45 | 将会启动开发中的 Mditor 46 | ```sh 47 | $ npm start 48 | ``` -------------------------------------------------------------------------------- /packages/desktop/src/menu/main.js: -------------------------------------------------------------------------------- 1 | const pkg = require('../../package.json'); 2 | const app = require('electron').app; 3 | const i18n = require('../i18n'); 4 | 5 | module.exports = async() => { 6 | let locale = i18n.locale; 7 | return { 8 | label: pkg.displayName, 9 | submenu: [{ 10 | label: `${locale.about} ${pkg.displayName}`, 11 | role: 'about' 12 | }, 13 | { 14 | label: `${locale.checkUpdate}...`, 15 | click() { 16 | app.checkUpdate(true); 17 | } 18 | }, 19 | { 20 | type: 'separator' 21 | }, 22 | { 23 | label: `${locale.preference}...`, 24 | click() { 25 | app.openPreference(); 26 | } 27 | }, 28 | { 29 | label: `${locale.resetPreference}...`, 30 | click() { 31 | app.resetPreference(); 32 | } 33 | }, 34 | { 35 | type: 'separator' 36 | }, 37 | { 38 | label: locale.services, 39 | role: 'services', 40 | submenu: [] 41 | }, 42 | { 43 | type: 'separator' 44 | }, 45 | { 46 | label: locale.hide, 47 | role: 'hide' 48 | }, 49 | { 50 | label: locale.hideOthers, 51 | role: 'hideothers' 52 | }, 53 | { 54 | label: locale.unHide, 55 | role: 'unhide' 56 | }, 57 | { 58 | type: 'separator' 59 | }, 60 | { 61 | label: locale.quit, 62 | role: 'quit' 63 | } 64 | ] 65 | }; 66 | }; -------------------------------------------------------------------------------- /packages/embed/src/toolbar/index.js: -------------------------------------------------------------------------------- 1 | const mokit = require('mokit'); 2 | const items = require('./items'); 3 | 4 | require('./index.less'); 5 | 6 | const Toolbar = new mokit.Component({ 7 | template: require('./index.html'), 8 | props: { 9 | mditor: null 10 | }, 11 | 12 | data() { 13 | return { 14 | items: items.slice(0) 15 | }; 16 | }, 17 | 18 | onReady() { 19 | this.bindCommands(); 20 | }, 21 | 22 | watch: { 23 | items() { 24 | this.bindCommands(); 25 | } 26 | }, 27 | 28 | bindCommands() { 29 | if (!this.mditor) return; 30 | this.items.forEach(item => { 31 | this.mditor.removeCommand(item.name); 32 | this.mditor.addCommand(item); 33 | }); 34 | }, 35 | 36 | isActive(item) { 37 | return this.mditor && item.state && this.mditor[item.state]; 38 | }, 39 | 40 | exec(name, event) { 41 | event.preventDefault(); 42 | this.mditor.execCommand(name, event); 43 | }, 44 | 45 | getItem(name) { 46 | return this.items.find(item => item.name === name); 47 | }, 48 | 49 | removeItem(name) { 50 | let index = this.items.findIndex(item => item.name === name); 51 | return this.items.splice(index, 1); 52 | }, 53 | 54 | addItem(item) { 55 | this.items.push(item); 56 | }, 57 | 58 | replaceItem(name, newItem) { 59 | let index = this.items.findIndex(item => item.name === name); 60 | let oldItem = this.items.splice(index, 1); 61 | this.items.splice(index, 0, newItem); 62 | return oldItem; 63 | } 64 | 65 | }); 66 | 67 | module.exports = Toolbar; -------------------------------------------------------------------------------- /packages/embed/src/assets/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= htmlWebpackPlugin.options.title %> 8 | <% for (var css in htmlWebpackPlugin.files.css) { %> 9 | 10 | <% } %> 11 | 17 | 18 | 19 | 20 | 21 |
22 | 25 |
26 | 27 | <% for (var chunk in htmlWebpackPlugin.files.chunks) { %> 28 | 29 | <% } %> 30 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /packages/desktop/bin/release.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const packager = require('electron-packager'); 4 | const pkg = require('../package'); 5 | 6 | const CWD = path.resolve(__dirname, '../'); 7 | 8 | //electron-packager . Mditor --ignore='node_modules' --overwrite --out=release --icon=./design/icon.icns 9 | packager({ 10 | name: 'Mditor', 11 | appBundleId: 'net.xhou.mditor', 12 | appCategoryType: 'public.app-category.utilities', 13 | dir: CWD, 14 | out: `${CWD}/release`, 15 | appVersion: pkg.version, 16 | arch: 'x64', 17 | icon: `${CWD}/design/icon.icns`, 18 | overwrite: true, 19 | electronVersion: '1.6.2', 20 | platform: "darwin", 21 | "osx-sign": { 22 | type: "distribution" 23 | }, 24 | 'extend-info': `${__dirname}/info.plist`, 25 | ignore: [ 26 | /node_modules/, 27 | /design/, 28 | /docs/, 29 | /release/, 30 | /test/, 31 | /bin/, 32 | /(\.DS_Store|\.babelrc|\.eslintrc\.yml|electron-builder\.yml|server\.yml|ignore|\.conf\.js|\.rename|\.config\.js|\.map|jasmine\.json)$/ 33 | ] 34 | }, function (err, appPaths) { 35 | if (err) console.error(err); 36 | console.log('packaged', appPaths); 37 | 38 | //处理 deps 39 | let pkgFile = path.resolve(__dirname, '../release/Mditor-darwin-x64/Mditor.app/Contents/Resources/app/package.json'); 40 | let buffer = fs.readFileSync(pkgFile); 41 | let pkgObj = JSON.parse(buffer.toString()); 42 | delete pkgObj.devDependencies; 43 | delete pkgObj.scripts; 44 | delete pkgObj.dev; 45 | fs.writeFile(pkgFile, JSON.stringify(pkgObj, null, 2)); 46 | 47 | }); -------------------------------------------------------------------------------- /packages/desktop/src/i18n/locales/en.yml: -------------------------------------------------------------------------------- 1 | about: About 2 | checkUpdate: Check For Updates 3 | preference: Preferences 4 | resetPreference: Reset Preferences 5 | services: Services 6 | hide: Hide 7 | hideOthers: Hide Others 8 | unHide: Un Hide 9 | quit: Quit 10 | clearRecent: Clear Recent Items 11 | file: File 12 | newFile: New File 13 | open: Open 14 | recentItems: Recent Items 15 | save: Save 16 | saveAs: Save As 17 | export: Export 18 | pdf: PDF 19 | html: HTML 20 | image: Image 21 | slide: Slide 22 | edit: Edit 23 | undo: Undo 24 | redo: Redo 25 | cut: Cut 26 | copy: Copy 27 | paste: Paste 28 | selectAll: Select All 29 | delete: Delete 30 | findAndReplace: Find And Replace 31 | view: View 32 | toggleSplit: Toggle Split 33 | togglePreview: Toggle Preview 34 | previewArea: Preview Area 35 | window: Window 36 | minimize: Minimize 37 | close: Close 38 | zoom: Zoom 39 | front: Bring To Front 40 | help: Help 41 | homepage: Homepage 42 | umlDoc: How to generate UML graphics 43 | slideDoc: How to write "Slide" 44 | currentlyTheLatestVersion: Currently the latest version 45 | goDownload: Go Download 46 | donNotUpdate: Don't Update 47 | discoverNewVersion: Discover new version 48 | recommendDownload: Recommend to download the new version now 49 | cancel: Cancel 50 | donNotSave: Don't Save 51 | confirmSave: Confirm Save 52 | saveAlertDetail: The file "${filename}" has not been saved, save now? 53 | bold: Bold 54 | italic: Italic 55 | underline: Underline 56 | strikethrough: Strikethrough 57 | header: Header 58 | quote: Quote 59 | code: Code 60 | listOl: Ordered list 61 | listUl: Unordered list 62 | link: Link 63 | table: Table 64 | line: Line -------------------------------------------------------------------------------- /packages/desktop/src/preference/index.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const Promise = require('bluebird'); 3 | const fs = require('../common/fs'); 4 | const Parser = require('mditor').Parser; 5 | const yaml = require('../common/yaml'); 6 | 7 | const DATA_PATH = app.getPath('userData'); 8 | const PREFERENCE_FILE = `${DATA_PATH}/preference.md`; 9 | 10 | async function createFile() { 11 | let buffer = await fs.readFile(`${__dirname}/tmpl.md`) 12 | return fs.writeFile(PREFERENCE_FILE, buffer); 13 | } 14 | 15 | async function isExists() { 16 | return await fs.exists(PREFERENCE_FILE) 17 | } 18 | 19 | async function getFile() { 20 | if (!await isExists()) await createFile(); 21 | return PREFERENCE_FILE; 22 | } 23 | 24 | async function load() { 25 | if (!await isExists()) return; 26 | let buffer = await fs.readFile(PREFERENCE_FILE); 27 | if (!buffer) return; 28 | let content = buffer.toString(); 29 | let editorConfigs, shortcutConfigs; 30 | Parser.highlights['editor'] = { 31 | parse: function (code) { 32 | editorConfigs = code; 33 | } 34 | }; 35 | Parser.highlights['shortcut'] = { 36 | parse: function (code) { 37 | shortcutConfigs = code; 38 | } 39 | }; 40 | let parser = new Parser(); 41 | parser.parse(content); 42 | Parser.highlights['editor'] = null; 43 | Parser.highlights['shortcut'] = null; 44 | return { 45 | editor: yaml(editorConfigs), 46 | shortcut: yaml(shortcutConfigs) 47 | } 48 | } 49 | 50 | async function reset() { 51 | if (!await isExists()) return; 52 | return fs.unlink(PREFERENCE_FILE); 53 | } 54 | 55 | exports.getFile = getFile; 56 | exports.load = load; 57 | exports.reset = reset; -------------------------------------------------------------------------------- /packages/embed/lib/toolbar/index.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var mokit = require('mokit'); 4 | var items = require('./items'); 5 | 6 | require('./index.less'); 7 | 8 | var Toolbar = new mokit.Component({ 9 | template: require('./index.html'), 10 | props: { 11 | mditor: null 12 | }, 13 | 14 | /*istanbul ignore next*/data: function data() { 15 | return { 16 | items: items.slice(0) 17 | }; 18 | }, 19 | /*istanbul ignore next*/onReady: function onReady() { 20 | this.bindCommands(); 21 | }, 22 | 23 | 24 | watch: { 25 | /*istanbul ignore next*/items: function items() { 26 | this.bindCommands(); 27 | } 28 | }, 29 | 30 | /*istanbul ignore next*/bindCommands: function bindCommands() { 31 | /*istanbul ignore next*/var _this = this; 32 | 33 | if (!this.mditor) return; 34 | this.items.forEach(function (item) { 35 | /*istanbul ignore next*/_this.mditor.removeCommand(item.name); 36 | /*istanbul ignore next*/_this.mditor.addCommand(item); 37 | }); 38 | }, 39 | /*istanbul ignore next*/isActive: function isActive(item) { 40 | return this.mditor && item.state && this.mditor[item.state]; 41 | }, 42 | /*istanbul ignore next*/exec: function exec(name, event) { 43 | event.preventDefault(); 44 | this.mditor.execCommand(name, event); 45 | }, 46 | /*istanbul ignore next*/getItem: function getItem(name) { 47 | return this.items.find(function (item) /*istanbul ignore next*/{ 48 | return item.name === name; 49 | }); 50 | }, 51 | /*istanbul ignore next*/removeItem: function removeItem(name) { 52 | var index = this.items.findIndex(function (item) /*istanbul ignore next*/{ 53 | return item.name === name; 54 | }); 55 | return this.items.splice(index, 1); 56 | }, 57 | /*istanbul ignore next*/addItem: function addItem(item) { 58 | this.items.push(item); 59 | }, 60 | /*istanbul ignore next*/replaceItem: function replaceItem(name, newItem) { 61 | var index = this.items.findIndex(function (item) /*istanbul ignore next*/{ 62 | return item.name === name; 63 | }); 64 | var oldItem = this.items.splice(index, 1); 65 | this.items.splice(index, 0, newItem); 66 | return oldItem; 67 | } 68 | }); 69 | 70 | module.exports = Toolbar; -------------------------------------------------------------------------------- /packages/embed/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 5 | const os = require('os'); 6 | const pkg = require('./package.json'); 7 | 8 | const ENV = process.env.NODE_ENV || 'prod'; 9 | console.log(`${os.EOL}NODE_ENV:`, ENV); 10 | 11 | const cssBundlePlugin = new ExtractTextPlugin(`css/mditor${ENV == 'prod' ? '.min' : ''}.css`); 12 | 13 | const htmlPlugin = new HtmlWebpackPlugin({ 14 | title: pkg.name, 15 | filename: 'index.html', 16 | template: './src/assets/index.ejs', 17 | inject: false 18 | }); 19 | 20 | const cleanPlugin = new CleanWebpackPlugin(['dist'], { 21 | verbose: false 22 | }); 23 | 24 | const compressPlugin = new webpack.optimize.UglifyJsPlugin({ 25 | compress: { 26 | warnings: false 27 | } 28 | }); 29 | 30 | const bannerPlugin = new webpack.BannerPlugin(`${pkg.displayName} version ${pkg.version} 31 | Homepage: ${pkg.homepage}`); 32 | 33 | // webpack plugins 34 | const plugins = [ 35 | htmlPlugin, 36 | cssBundlePlugin, 37 | bannerPlugin, 38 | ]; 39 | if (ENV === 'prod') plugins.push(compressPlugin); 40 | 41 | // webpack loaders 42 | const loaders = [{ 43 | test: /\.js$/, 44 | loader: 'babel', 45 | exclude: [/node_modules/, /\.test\.js$/] 46 | }, { 47 | test: /.*mokit.*\.js$/, 48 | loader: 'babel' 49 | }, { 50 | test: /\.json$/, 51 | loader: 'json', 52 | }, { 53 | test: /\?raw$/, 54 | loader: 'raw' 55 | }, { 56 | test: /\.html$/, 57 | loader: 'raw' 58 | }, { 59 | test: /\.(png|jpg|gif)\?*.*$/, 60 | loader: 'url?limit=8192&name=img/[hash].[ext]' 61 | }, { 62 | test: /\.(eot|woff|woff2|webfont|ttf|svg)\?*.*$/, 63 | loader: 'url?limit=8192&name=font/[hash].[ext]' 64 | }, { 65 | test: /\.less$/, 66 | loader: cssBundlePlugin.extract('css!less', { 67 | publicPath: '../' 68 | }) 69 | }, { 70 | test: /\.css$/, 71 | loader: cssBundlePlugin.extract('css', { 72 | publicPath: '../' 73 | }) 74 | }]; 75 | 76 | // webpack configs 77 | module.exports = { 78 | entry: { 79 | mditor: `./src/client` 80 | }, 81 | output: { 82 | path: './dist/', 83 | filename: `js/[name]${ENV == 'prod' ? '.min' : ''}.js` 84 | }, 85 | devtool: 'source-map', 86 | module: { 87 | loaders: loaders 88 | }, 89 | plugins: plugins 90 | }; -------------------------------------------------------------------------------- /packages/desktop/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 5 | const os = require('os'); 6 | const path = require('path'); 7 | 8 | const ENV = process.env.NODE_ENV || 'prod'; 9 | console.log(`${os.EOL}NODE_ENV:`, ENV); 10 | 11 | const cssBundlePlugin = new ExtractTextPlugin('css/bundle.css'); 12 | 13 | const htmlPlugin = new HtmlWebpackPlugin({ 14 | title: 'template', 15 | filename: 'index.html', 16 | template: './src/window/index.html' 17 | }); 18 | 19 | const cleanPlugin = new CleanWebpackPlugin(['build/dist'], { 20 | verbose: false 21 | }); 22 | 23 | const compressPlugin = new webpack.optimize.UglifyJsPlugin({ 24 | compress: { 25 | warnings: false 26 | } 27 | }); 28 | 29 | // webpack plugins 30 | const plugins = [ 31 | htmlPlugin, 32 | cssBundlePlugin, 33 | cleanPlugin, 34 | ]; 35 | //if (ENV === 'prod') plugins.push(compressPlugin); 36 | 37 | // webpack loaders 38 | const loaders = [{ 39 | test: /\.js$/, 40 | loader: 'babel-loader', 41 | exclude: [/node_modules/, /\.test\.js$/] 42 | }, { 43 | test: /.*mokit.*\.js$/, 44 | loader: 'babel-loader' 45 | }, { 46 | test: /mditor/, 47 | loader: 'babel-loader' 48 | }, { 49 | test: /\.json$/, 50 | loader: 'json-loader' 51 | }, { 52 | test: /\?raw$/, 53 | loader: 'raw-loader' 54 | }, { 55 | test: /\.html$/, 56 | loader: 'raw-loader' 57 | }, { 58 | test: /\.(png|jpg|gif)\?*.*$/, 59 | loader: 'url-loader?limit=8192&name=img/[hash].[ext]' 60 | }, { 61 | test: /\.(eot|woff|woff2|webfont|ttf|svg)\?*.*$/, 62 | loader: 'url-loader?limit=8192&name=font/[hash].[ext]' 63 | }, { 64 | test: /\.less$/, 65 | loader: cssBundlePlugin.extract({ 66 | use: ['css-loader', 'less-loader'], 67 | publicPath: '../' 68 | }) 69 | }, { 70 | test: /\.css$/, 71 | loader: cssBundlePlugin.extract({ 72 | use: 'css-loader', 73 | publicPath: '../' 74 | }) 75 | }]; 76 | 77 | // webpack configs 78 | module.exports = { 79 | context: __dirname, 80 | entry: { 81 | bundle: `./src/window/index.js` 82 | }, 83 | output: { 84 | path: path.resolve(__dirname, './build'), 85 | filename: 'js/[name].js' 86 | }, 87 | devtool: 'source-map', 88 | module: { 89 | loaders: loaders 90 | }, 91 | plugins: plugins 92 | }; -------------------------------------------------------------------------------- /docs/assets/gh-fork-ribbon.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * "Fork me on GitHub" CSS ribbon v0.1.1 | MIT License 3 | * https://github.com/simonwhitaker/github-fork-ribbon-css 4 | */.github-fork-ribbon{position:absolute;padding:2px 0;background-color:#a00;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.15)));background-image:-webkit-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-moz-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-ms-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.15));-webkit-box-shadow:0 2px 3px 0 rgba(0,0,0,.5);-moz-box-shadow:0 2px 3px 0 rgba(0,0,0,.5);box-shadow:0 2px 3px 0 rgba(0,0,0,.5);font:700 13px "Helvetica Neue",Helvetica,Arial,sans-serif;z-index:9999;pointer-events:auto}.github-fork-ribbon a,.github-fork-ribbon a:hover{color:#fff;text-decoration:none;text-shadow:0 -1px rgba(0,0,0,.5);text-align:center;width:200px;line-height:20px;display:inline-block;padding:2px 0;border-width:1px 0;border-style:dotted;border-color:#fff;border-color:rgba(255,255,255,.7)}.github-fork-ribbon-wrapper{width:150px;height:150px;position:absolute;overflow:hidden;top:0;z-index:9999;pointer-events:none}.github-fork-ribbon-wrapper.fixed{position:fixed}.github-fork-ribbon-wrapper.left{left:0}.github-fork-ribbon-wrapper.right{right:0}.github-fork-ribbon-wrapper.left-bottom{position:fixed;top:inherit;bottom:0;left:0}.github-fork-ribbon-wrapper.right-bottom{position:fixed;top:inherit;bottom:0;right:0}.github-fork-ribbon-wrapper.right .github-fork-ribbon{top:42px;right:-43px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.github-fork-ribbon-wrapper.left .github-fork-ribbon{top:42px;left:-43px;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}.github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon{top:80px;left:-43px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon{top:80px;right:-43px;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)} -------------------------------------------------------------------------------- /packages/desktop/docs/assets/gh-fork-ribbon.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * "Fork me on GitHub" CSS ribbon v0.1.1 | MIT License 3 | * https://github.com/simonwhitaker/github-fork-ribbon-css 4 | */.github-fork-ribbon{position:absolute;padding:2px 0;background-color:#a00;background-image:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.15)));background-image:-webkit-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-moz-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-ms-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:-o-linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,.15));background-image:linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,.15));-webkit-box-shadow:0 2px 3px 0 rgba(0,0,0,.5);-moz-box-shadow:0 2px 3px 0 rgba(0,0,0,.5);box-shadow:0 2px 3px 0 rgba(0,0,0,.5);font:700 13px "Helvetica Neue",Helvetica,Arial,sans-serif;z-index:9999;pointer-events:auto}.github-fork-ribbon a,.github-fork-ribbon a:hover{color:#fff;text-decoration:none;text-shadow:0 -1px rgba(0,0,0,.5);text-align:center;width:200px;line-height:20px;display:inline-block;padding:2px 0;border-width:1px 0;border-style:dotted;border-color:#fff;border-color:rgba(255,255,255,.7)}.github-fork-ribbon-wrapper{width:150px;height:150px;position:absolute;overflow:hidden;top:0;z-index:9999;pointer-events:none}.github-fork-ribbon-wrapper.fixed{position:fixed}.github-fork-ribbon-wrapper.left{left:0}.github-fork-ribbon-wrapper.right{right:0}.github-fork-ribbon-wrapper.left-bottom{position:fixed;top:inherit;bottom:0;left:0}.github-fork-ribbon-wrapper.right-bottom{position:fixed;top:inherit;bottom:0;right:0}.github-fork-ribbon-wrapper.right .github-fork-ribbon{top:42px;right:-43px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.github-fork-ribbon-wrapper.left .github-fork-ribbon{top:42px;left:-43px;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}.github-fork-ribbon-wrapper.left-bottom .github-fork-ribbon{top:80px;left:-43px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.github-fork-ribbon-wrapper.right-bottom .github-fork-ribbon{top:80px;right:-43px;-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)} -------------------------------------------------------------------------------- /packages/desktop/src/menu/file.js: -------------------------------------------------------------------------------- 1 | const app = require('electron').app; 2 | const recent = require('../recent'); 3 | const i18n = require('../i18n'); 4 | 5 | module.exports = async() => { 6 | let locale = i18n.locale; 7 | let recentItems = await recent.getItems(); 8 | recentItems = recentItems.map(filename => { 9 | return { 10 | label: filename, 11 | click() { 12 | app.openFileInWindow(filename); 13 | } 14 | }; 15 | }); 16 | recentItems.push({ 17 | type: 'separator' 18 | }); 19 | recentItems.push({ 20 | label: locale.clearRecent, 21 | click() { 22 | recent.clear(); 23 | } 24 | }); 25 | 26 | return { 27 | label: locale.file, 28 | role: 'file', 29 | submenu: [{ 30 | label: locale.newFile, 31 | accelerator: 'CmdOrCtrl+N', 32 | click() { 33 | app.createWindow(); 34 | } 35 | }, 36 | { 37 | type: 'separator' 38 | }, 39 | { 40 | label: `${locale.open}...`, 41 | accelerator: 'CmdOrCtrl+O', 42 | click() { 43 | app.open(); 44 | } 45 | }, 46 | { 47 | label: locale.recentItems, 48 | submenu: recentItems 49 | }, 50 | { 51 | type: 'separator' 52 | }, 53 | { 54 | accelerator: 'CmdOrCtrl+S', 55 | label: `${locale.save}...`, 56 | click() { 57 | app.save(); 58 | } 59 | }, 60 | { 61 | accelerator: 'Shift+CmdOrCtrl+S', 62 | label: `${locale.saveAs}...`, 63 | click() { 64 | app.saveAs(); 65 | } 66 | }, 67 | { 68 | type: 'separator' 69 | }, 70 | { 71 | label: locale.export, 72 | submenu: [{ 73 | label: `${locale.html}...`, 74 | click() { 75 | app.toHTML(); 76 | } 77 | }, { 78 | label: `${locale.pdf}...`, 79 | click() { 80 | app.toPDF(); 81 | } 82 | }, { 83 | label: `${locale.image}...`, 84 | click() { 85 | app.toImage(); 86 | } 87 | }, { 88 | label: `${locale.slide}...`, 89 | click() { 90 | app.toSlide(); 91 | } 92 | }] 93 | }, { 94 | type: 'separator' 95 | }, { 96 | label: locale.quit, 97 | click() { 98 | app.quit(); 99 | } 100 | } 101 | ] 102 | }; 103 | }; -------------------------------------------------------------------------------- /packages/embed/src/finder/index.js: -------------------------------------------------------------------------------- 1 | const mokit = require('mokit'); 2 | const utils = require('ntils'); 3 | 4 | require('./index.less'); 5 | 6 | const CHECK_REGEXP = /^\/[\s\S]+\/(i|g|m)*$/; 7 | 8 | const Finder = new mokit.Component({ 9 | template: require('./index.html'), 10 | props: { 11 | mditor: null, 12 | active: false, 13 | findWord: '', 14 | replaceWord: '' 15 | }, 16 | onReady() { 17 | this.mditor.removeCommand('find'); 18 | this.mditor.addCommand({ 19 | name: 'find', 20 | key: '{cmd}+f', 21 | owner: this.mditor.$element, 22 | handler: this.show.bind(this, null) 23 | }); 24 | this.mditor.removeCommand('cancel-find'); 25 | this.mditor.addCommand({ 26 | name: 'cancel-find', 27 | key: 'esc', 28 | owner: this.mditor.$element, 29 | handler: this.hide.bind(this) 30 | }); 31 | }, 32 | hide() { 33 | this.findWord = ''; 34 | this.replaceWord = ''; 35 | this.mditor.editor.markExp = null; 36 | this.active = false; 37 | }, 38 | show(text) { 39 | this.active = true; 40 | this.findWord = text || this.mditor.editor.getSelectText(); 41 | if (this.active) { 42 | setTimeout(() => { 43 | this.findBox.focus(); 44 | }, 200); 45 | } 46 | this.mditor.editor.syncScroll(); 47 | }, 48 | watch: { 49 | findWord() { 50 | if (!this.mditor || !this.mditor.editor) return; 51 | if (!this.findWord) { 52 | this.mditor.editor.markExp = null; 53 | } else { 54 | this.mditor.editor.markExp = this.parseRegexp(this.findWord); 55 | } 56 | setTimeout(() => { 57 | this.mditor.editor.activeMark(0); 58 | }, 100); 59 | } 60 | }, 61 | parseRegexp(text, forceStr) { 62 | if (!forceStr && CHECK_REGEXP.test(text)) { 63 | try { 64 | return (new Function(`return ${text}`))(); 65 | } catch (err) { 66 | return this.parseRegexp(text, true); 67 | } 68 | } else { 69 | return new RegExp(utils.escapeRegExp(text), 'gm'); 70 | } 71 | }, 72 | find() { 73 | this.mditor.editor.activeMark(); 74 | }, 75 | replace() { 76 | this.mditor.value = this.mditor.value.replace( 77 | this.mditor.editor.markExp, 78 | this.replaceWord || '' 79 | ); 80 | }, 81 | onFindEnter(event) { 82 | if (event.keyCode != 13) return; 83 | event.preventDefault(); 84 | this.find(); 85 | }, 86 | onReplaceEnter(event) { 87 | if (event.keyCode != 13) return; 88 | event.preventDefault(); 89 | this.replace(); 90 | }, 91 | onCompositionEnd(event) { 92 | event.target.blur(); 93 | event.target.focus(); 94 | } 95 | }); 96 | 97 | module.exports = Finder; -------------------------------------------------------------------------------- /docs/doc/slide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | slide 9 | 10 | 20 | 36 | 58 | 79 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /packages/desktop/docs/doc/slide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | slide 9 | 10 | 20 | 36 | 58 | 79 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /packages/desktop/src/convert/index.js: -------------------------------------------------------------------------------- 1 | const pdf = require('html-pdf'); 2 | const Promise = require('bluebird'); 3 | const fs = require('../common/fs'); 4 | const path = require('path'); 5 | const stp = require('stp'); 6 | const UMLParser = require('../uml'); 7 | const yaml = require('../common/yaml'); 8 | 9 | const Parser = require('mditor').Parser; 10 | Parser.highlights['uml'] = new UMLParser(); 11 | 12 | const parser = Promise.promisifyAll(new Parser()); 13 | 14 | exports.toHTML = async function (opts) { 15 | if (!opts) return; 16 | opts.title = opts.title || 'Untitled'; 17 | opts.style = opts.style || ''; 18 | opts.content = opts.content || ''; 19 | let styleFile = require.resolve('mditor/dist/css/mditor.min.css'); 20 | opts.style += (await fs.readFile(styleFile)).toString(); 21 | opts.style += (await fs.readFile(`${__dirname}/html.css`)).toString(); 22 | if (opts.border) { 23 | opts.style += (await fs.readFile(`${__dirname}/border.css`)).toString(); 24 | } 25 | opts.content = await parser.parseAsync(opts.content); 26 | let tmpl = (await fs.readFile(`${__dirname}/tmpl.html`)).toString(); 27 | let fn = stp(tmpl); 28 | return fn(opts); 29 | }; 30 | 31 | exports.toPDF = async function (opts) { 32 | let html = await this.toHTML(opts); 33 | return new Promise((resolve, reject) => { 34 | pdf.create(html, { 35 | format: 'A4', 36 | border: '1cm', 37 | type: 'pdf' 38 | }).toBuffer(function (err, buffer) { 39 | if (err) return reject(err); 40 | resolve(buffer); 41 | }); 42 | }); 43 | }; 44 | 45 | exports.toImage = async function (opts) { 46 | opts.style = 'body{padding:15px;background-color: #fff;}'; 47 | let html = await this.toHTML(opts); 48 | return new Promise((resolve, reject) => { 49 | pdf.create(html, { 50 | border: '1cm', 51 | width: '900px', 52 | type: opts.type || 'png' 53 | }).toBuffer(function (err, buffer) { 54 | if (err) return reject(err); 55 | resolve(buffer); 56 | }); 57 | }); 58 | }; 59 | 60 | exports.toSlide = async function (opts) { 61 | if (!opts) return; 62 | opts.title = opts.title || 'Untitled'; 63 | opts.content = opts.content || ''; 64 | let parts = opts.content.split(/`{1,3}slide([\s\S]*?)`{1,3}/).slice(1); 65 | opts.items = []; 66 | for (let i = 0; i < parts.length; i += 2) { 67 | let meta = yaml(parts[i]) || {}; 68 | let body = await parser.parseAsync(parts[i + 1]); 69 | opts.items.push(``); 77 | } 78 | let tmpl = (await fs.readFile(`${__dirname}/slide.html`)).toString(); 79 | let fn = stp(tmpl); 80 | return fn(opts); 81 | }; -------------------------------------------------------------------------------- /packages/desktop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mditor-desktop", 3 | "displayName": "Mditor", 4 | "version": "1.0.0", 5 | "description": "Mditor,最好用的 markdown 编辑器!", 6 | "main": "./src/app.js", 7 | "scripts": { 8 | "start": "./bin/start.sh", 9 | "stop": "./bin/stop.sh", 10 | "restart": "./bin/restart.sh", 11 | "clear": "./bin/clear.sh", 12 | "lint": "eslint ./lib", 13 | "build": "./bin/build.sh", 14 | "watch": "./bin/watch.sh", 15 | "dev": "./bin/watch.sh", 16 | "unit": "./bin/unit.sh", 17 | "e2e": "./bin/e2e.sh", 18 | "test": "./bin/test.sh", 19 | "release": "./bin/release.sh", 20 | "desk": "electron ." 21 | }, 22 | "author": "", 23 | "license": "MIT", 24 | "dev": { 25 | "host": "localhost", 26 | "port": 8006 27 | }, 28 | "homepage": "http://mditor.com", 29 | "update": { 30 | "url": "http://mditor.com/update.json" 31 | }, 32 | "devDependencies": { 33 | "babel-core": "^6.21.0", 34 | "babel-eslint": "^7.1.1", 35 | "babel-loader": "^6.4.0", 36 | "babel-plugin-transform-runtime": "^6.15.0", 37 | "babel-polyfill": "^6.20.0", 38 | "babel-preset-es2015": "^6.18.0", 39 | "babel-preset-stage-3": "^6.17.0", 40 | "babel-runtime": "^6.20.0", 41 | "bootstrap": "^3.3.7", 42 | "cify": "^2.1.11", 43 | "clean-webpack-plugin": "^0.1.14", 44 | "css-loader": "^0.26.1", 45 | "electron": "^1.6.5", 46 | "electron-builder": "^15.2.0", 47 | "electron-installer-dmg": "^0.2.0", 48 | "electron-packager": "^8.5.2", 49 | "eslint": "^3.13.0", 50 | "exorcist": "^0.4.0", 51 | "extract-text-webpack-plugin": "^2.1.0", 52 | "file-loader": "^0.10.1", 53 | "html-webpack-plugin": "^2.26.0", 54 | "isparta": "4.0.0", 55 | "isparta-loader": "^2.0.0", 56 | "istanbul": "0.4.5", 57 | "jasmine": "2.5.2", 58 | "jasmine-core": "2.5.0", 59 | "json-loader": "^0.5.4", 60 | "karma": "1.2.0", 61 | "karma-chrome-launcher": "2.0.0", 62 | "karma-coverage": "1.1.1", 63 | "karma-jasmine": "1.0.2", 64 | "karma-phantomjs-launcher": "1.0.2", 65 | "karma-sourcemap-loader": "0.3.7", 66 | "karma-spec-reporter": "0.0.26", 67 | "karma-webpack": "1.8.0", 68 | "less": "^2.7.2", 69 | "less-loader": "^3.0.0", 70 | "nokit-filter-proxy": "0.0.8", 71 | "nokitjs": "^1.26.3", 72 | "phantomjs-prebuilt": "^2.1.13", 73 | "raw-loader": "^0.5.1", 74 | "selenium-webdriver": "^3.0.1", 75 | "style-loader": "^0.13.2", 76 | "url-loader": "^0.5.8", 77 | "webpack": "^2.2.1" 78 | }, 79 | "dependencies": { 80 | "bluebird": "^3.5.0", 81 | "electron-renderer-value": "^1.1.0", 82 | "html-pdf": "^2.1.0", 83 | "js-yaml": "^3.8.2", 84 | "md5": "^2.2.1", 85 | "mditor": "^1.2.9", 86 | "mkdirp": "^0.5.1", 87 | "mokit": "^3.1.2", 88 | "node-fetch": "^1.6.3", 89 | "ntils": "^2.1.0", 90 | "stp": "^0.0.2" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /packages/embed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mditor", 3 | "displayName": "Mditor embed", 4 | "version": "1.3.3", 5 | "description": "一个简洁、易于集成、方便扩展、期望舒服的编写 markdown 的编辑器", 6 | "main": "./lib/server/index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/houfeng/mditor.git" 10 | }, 11 | "keywords": [ 12 | "mditor", 13 | "markdown", 14 | "embedded", 15 | "editor" 16 | ], 17 | "scripts": { 18 | "start": "./bin/start.sh", 19 | "stop": "./bin/stop.sh", 20 | "restart": "./bin/restart.sh", 21 | "clear": "./bin/clear.sh", 22 | "lint": "eslint ./src", 23 | "build": "./bin/build.sh", 24 | "watch": "./bin/watch.sh", 25 | "dev": "./bin/watch.sh", 26 | "unit": "./bin/unit.sh", 27 | "e2e": "./bin/e2e.sh", 28 | "test": "./bin/test.sh" 29 | }, 30 | "author": { 31 | "name": "Houfeng", 32 | "email": "admin@xhou.net", 33 | "url": "http://houfeng.net" 34 | }, 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/houfeng/mditor/issues" 38 | }, 39 | "homepage": "http://mditor.com", 40 | "dependencies": { 41 | "font-awesome": "^4.7.0", 42 | "github-markdown-css": "^2.4.1", 43 | "marked": "^0.3.6", 44 | "mokit": "^3.1.2", 45 | "ntils": "^2.1.0", 46 | "prismjs": "^1.8.3", 47 | "shortcut-key": "^1.0.0" 48 | }, 49 | "dev": { 50 | "host": "localhost", 51 | "port": 8008 52 | }, 53 | "devDependencies": { 54 | "babel-cli": "^6.24.1", 55 | "babel-core": "^6.21.0", 56 | "babel-eslint": "^7.1.1", 57 | "babel-loader": "^6.2.10", 58 | "babel-plugin-transform-runtime": "^6.15.0", 59 | "babel-polyfill": "^6.20.0", 60 | "babel-preset-es2015": "^6.18.0", 61 | "babel-preset-stage-3": "^6.17.0", 62 | "babel-runtime": "^6.20.0", 63 | "bootstrap": "^3.3.7", 64 | "cify": "^2.1.11", 65 | "clean-webpack-plugin": "^0.1.14", 66 | "css-loader": "^0.26.1", 67 | "eslint": "^3.13.0", 68 | "exorcist": "^0.4.0", 69 | "extract-text-webpack-plugin": "^1.0.1", 70 | "file-loader": "^0.10.0", 71 | "html-webpack-plugin": "^2.26.0", 72 | "isparta": "4.0.0", 73 | "isparta-loader": "^2.0.0", 74 | "istanbul": "0.4.5", 75 | "jasmine": "2.5.2", 76 | "jasmine-core": "2.5.0", 77 | "json-loader": "^0.5.4", 78 | "karma": "1.2.0", 79 | "karma-chrome-launcher": "2.0.0", 80 | "karma-coverage": "1.1.1", 81 | "karma-jasmine": "1.0.2", 82 | "karma-phantomjs-launcher": "1.0.2", 83 | "karma-sourcemap-loader": "0.3.7", 84 | "karma-spec-reporter": "0.0.26", 85 | "karma-webpack": "1.8.0", 86 | "less": "^2.7.2", 87 | "less-loader": "^2.2.3", 88 | "nokit-filter-proxy": "0.0.8", 89 | "nokitjs": "^1.26.3", 90 | "phantomjs-prebuilt": "^2.1.13", 91 | "raw-loader": "^0.5.1", 92 | "selenium-webdriver": "^3.0.1", 93 | "url-loader": "^0.5.7", 94 | "webpack": "^1.14.0" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /docs/slide/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | PPT 9 | 20 | 24 | 30 | 69 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /packages/desktop/docs/slide/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | PPT 9 | 20 | 24 | 30 | 69 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/stylesheets/github-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 GitHub Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | .pl-c /* comment */ { 19 | color: #969896; 20 | } 21 | 22 | .pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */, 23 | .pl-s .pl-v /* string variable */ { 24 | color: #0086b3; 25 | } 26 | 27 | .pl-e /* entity */, 28 | .pl-en /* entity.name */ { 29 | color: #795da3; 30 | } 31 | 32 | .pl-s .pl-s1 /* string source */, 33 | .pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ { 34 | color: #333; 35 | } 36 | 37 | .pl-ent /* entity.name.tag */ { 38 | color: #63a35c; 39 | } 40 | 41 | .pl-k /* keyword, storage, storage.type */ { 42 | color: #a71d5d; 43 | } 44 | 45 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 46 | .pl-s /* string */, 47 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 48 | .pl-sr /* string.regexp */, 49 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 50 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */, 51 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */ { 52 | color: #183691; 53 | } 54 | 55 | .pl-v /* variable */ { 56 | color: #ed6a43; 57 | } 58 | 59 | .pl-id /* invalid.deprecated */ { 60 | color: #b52a1d; 61 | } 62 | 63 | .pl-ii /* invalid.illegal */ { 64 | background-color: #b52a1d; 65 | color: #f8f8f8; 66 | } 67 | 68 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 69 | color: #63a35c; 70 | font-weight: bold; 71 | } 72 | 73 | .pl-ml /* markup.list */ { 74 | color: #693a17; 75 | } 76 | 77 | .pl-mh /* markup.heading */, 78 | .pl-mh .pl-en /* markup.heading entity.name */, 79 | .pl-ms /* meta.separator */ { 80 | color: #1d3e81; 81 | font-weight: bold; 82 | } 83 | 84 | .pl-mq /* markup.quote */ { 85 | color: #008080; 86 | } 87 | 88 | .pl-mi /* markup.italic */ { 89 | color: #333; 90 | font-style: italic; 91 | } 92 | 93 | .pl-mb /* markup.bold */ { 94 | color: #333; 95 | font-weight: bold; 96 | } 97 | 98 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 99 | background-color: #ffecec; 100 | color: #bd2c00; 101 | } 102 | 103 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 104 | background-color: #eaffea; 105 | color: #55a532; 106 | } 107 | 108 | .pl-mdr /* meta.diff.range */ { 109 | color: #795da3; 110 | font-weight: bold; 111 | } 112 | 113 | .pl-mo /* meta.output */ { 114 | color: #1d3e81; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /packages/desktop/docs/stylesheets/github-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 GitHub Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | .pl-c /* comment */ { 19 | color: #969896; 20 | } 21 | 22 | .pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */, 23 | .pl-s .pl-v /* string variable */ { 24 | color: #0086b3; 25 | } 26 | 27 | .pl-e /* entity */, 28 | .pl-en /* entity.name */ { 29 | color: #795da3; 30 | } 31 | 32 | .pl-s .pl-s1 /* string source */, 33 | .pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ { 34 | color: #333; 35 | } 36 | 37 | .pl-ent /* entity.name.tag */ { 38 | color: #63a35c; 39 | } 40 | 41 | .pl-k /* keyword, storage, storage.type */ { 42 | color: #a71d5d; 43 | } 44 | 45 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 46 | .pl-s /* string */, 47 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 48 | .pl-sr /* string.regexp */, 49 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 50 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */, 51 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */ { 52 | color: #183691; 53 | } 54 | 55 | .pl-v /* variable */ { 56 | color: #ed6a43; 57 | } 58 | 59 | .pl-id /* invalid.deprecated */ { 60 | color: #b52a1d; 61 | } 62 | 63 | .pl-ii /* invalid.illegal */ { 64 | background-color: #b52a1d; 65 | color: #f8f8f8; 66 | } 67 | 68 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 69 | color: #63a35c; 70 | font-weight: bold; 71 | } 72 | 73 | .pl-ml /* markup.list */ { 74 | color: #693a17; 75 | } 76 | 77 | .pl-mh /* markup.heading */, 78 | .pl-mh .pl-en /* markup.heading entity.name */, 79 | .pl-ms /* meta.separator */ { 80 | color: #1d3e81; 81 | font-weight: bold; 82 | } 83 | 84 | .pl-mq /* markup.quote */ { 85 | color: #008080; 86 | } 87 | 88 | .pl-mi /* markup.italic */ { 89 | color: #333; 90 | font-style: italic; 91 | } 92 | 93 | .pl-mb /* markup.bold */ { 94 | color: #333; 95 | font-weight: bold; 96 | } 97 | 98 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 99 | background-color: #ffecec; 100 | color: #bd2c00; 101 | } 102 | 103 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 104 | background-color: #eaffea; 105 | color: #55a532; 106 | } 107 | 108 | .pl-mdr /* meta.diff.range */ { 109 | color: #795da3; 110 | font-weight: bold; 111 | } 112 | 113 | .pl-mo /* meta.output */ { 114 | color: #1d3e81; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /packages/embed/README.md: -------------------------------------------------------------------------------- 1 | ## 只求极致 2 | 3 | [ **M** ] arkdown + E [ **ditor** ] = **Mditor** 4 | 5 | [![version](https://badge.fury.io/js/mditor.svg)](http://badge.fury.io/js/mditor) 6 | 7 | Mditor 是一个简洁、易于集成、方便扩展、期望舒服的编写 markdown 的编辑器,仅此而已... 8 | 9 | 支持浏览器: chrome/safari/firefox/ie9+ 10 | 11 | ![image](http://embed.mditor.com/images/mditor.png) 12 | 13 | ## 在线体验 14 | [在线 demo](http://embed.mditor.com/demo/index.html) 15 | 16 | ## 使用桌面版 17 | 下载桌面版本 [http://mditor.com/](http://mditor.com/) 18 | 19 | ## 在浏览器集成 Mditor 20 | 21 | ##### 第一步: 22 | 23 | 引入 Mditor 样式文件 24 | ```html 25 | 26 | ``` 27 | 28 | 引用 Mditor 脚本文件 29 | ```html 30 | 31 | ``` 32 | 33 | 当然,也可以使用 CDN 资源 34 | ```html 35 | ... 36 | 37 | ... 38 | 39 | ... 40 | ``` 41 | 42 | ##### 第二步: 43 | 44 | 添加 textarea 元素 45 | ```html 46 | 47 | ``` 48 | 49 | 创建 Mditor 实例 50 | ```js 51 | var mditor = Mditor.fromTextarea(document.getElementById('editor')); 52 | 53 | //获取或设置编辑器的值 54 | mditor.on('ready',function(){ 55 | console.log(mditor.value); 56 | mditor.value = '** hello **'; 57 | }); 58 | ``` 59 | 60 | 所有 API 都应在 ready 事件中进行调用 61 | 62 | ##### 模式控制 API: 63 | 64 | ```js 65 | //是否打开分屏 66 | mditor.split = true; //打开 67 | mditor.split = false; //关闭 68 | 69 | //是否打开预览 70 | mditor.preivew = true; //打开 71 | mditor.preivew = false; //关闭 72 | 73 | //是否全屏 74 | mditor.fullscreen = true; //打开 75 | mditor.fullscreen = false; //关闭 76 | ``` 77 | 78 | ##### 工具条配置 API 79 | 80 | ```js 81 | //mditor.toolbar.items 是一个数组,包括所有按钮的信息 82 | //可以直接操作 items 以控制工具条 83 | 84 | //只保留第一个按钮 85 | mditor.toolbar.items = mditor.toolbar.items.slice(0,1); 86 | //添加一个按钮 87 | mditor.toolbar.addItem({...}); 88 | //移除一个按钮 89 | mditor.toolbar.removeItem(name); 90 | //替换一个按钮 91 | mditor.toolbar.replaceItem(name, {...}); 92 | //获取一个按钮 93 | mditor.toolbar.getItem(name); 94 | 95 | //更改按钮行为 96 | //示例,更改「图片」按钮配置,其它按钮是同样的方法 97 | let btn = mditor.toolbar.getItem('image'); 98 | //替换按钮动作 99 | btn.handler = function(){ 100 | //自定义处理逻辑 101 | //this 指向当前 mditor 实例 102 | }; 103 | 104 | //还可以替换其它信息 105 | btn.icon = '...'; //设置按钮图标 106 | btn.title = '...'; //投置按钮标题 107 | btn.control = true; //作为控制按钮显示在右侧 108 | btn.key = 'ctrl+d'; //设置按钮快捷建 109 | ``` 110 | 111 | ##### 文本编辑 API 112 | 113 | ```js 114 | //编辑器相关 API 在 mditor.editor 对象上 115 | 116 | //在光标前插入文本 117 | mditor.editor.insertBeforeText('文本'); 118 | //在光标后插入文本 119 | mditor.editor.insertAfterText('文本'); 120 | //其它,说明待补充 121 | ... 122 | ``` 123 | 124 | ## 在服务器渲染 Markdown 125 | 126 | 通过 npm 安装 127 | ```sh 128 | npm install mditor -save 129 | ``` 130 | 131 | 在服务端解析 132 | ```javascript 133 | var mditor = require("mditor"); 134 | var parser = new mditor.Parser(); 135 | var html = parser.parse("** Hello mditor! **"); 136 | ``` 137 | 138 | 在页面中展示解析后的内容 139 | ```html 140 | ... 141 | 142 | 143 | ... 144 |
145 | 146 |
147 | ``` 148 | 149 | -end- 150 | -------------------------------------------------------------------------------- /packages/embed/lib/finder/index.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var mokit = require('mokit'); 4 | var utils = require('ntils'); 5 | 6 | require('./index.less'); 7 | 8 | var CHECK_REGEXP = /^\/[\s\S]+\/(i|g|m)*$/; 9 | 10 | var Finder = new mokit.Component({ 11 | template: require('./index.html'), 12 | props: { 13 | mditor: null, 14 | active: false, 15 | findWord: '', 16 | replaceWord: '' 17 | }, 18 | /*istanbul ignore next*/onReady: function onReady() { 19 | this.mditor.removeCommand('find'); 20 | this.mditor.addCommand({ 21 | name: 'find', 22 | key: '{cmd}+f', 23 | owner: this.mditor.$element, 24 | handler: this.show.bind(this, null) 25 | }); 26 | this.mditor.removeCommand('cancel-find'); 27 | this.mditor.addCommand({ 28 | name: 'cancel-find', 29 | key: 'esc', 30 | owner: this.mditor.$element, 31 | handler: this.hide.bind(this) 32 | }); 33 | }, 34 | /*istanbul ignore next*/hide: function hide() { 35 | this.findWord = ''; 36 | this.replaceWord = ''; 37 | this.mditor.editor.markExp = null; 38 | this.active = false; 39 | }, 40 | /*istanbul ignore next*/show: function show(text) { 41 | /*istanbul ignore next*/var _this = this; 42 | 43 | this.active = true; 44 | this.findWord = text || this.mditor.editor.getSelectText(); 45 | if (this.active) { 46 | setTimeout(function () { 47 | /*istanbul ignore next*/_this.findBox.focus(); 48 | }, 200); 49 | } 50 | this.mditor.editor.syncScroll(); 51 | }, 52 | 53 | watch: { 54 | /*istanbul ignore next*/findWord: function findWord() { 55 | /*istanbul ignore next*/var _this2 = this; 56 | 57 | if (!this.mditor || !this.mditor.editor) return; 58 | if (!this.findWord) { 59 | this.mditor.editor.markExp = null; 60 | } else { 61 | this.mditor.editor.markExp = this.parseRegexp(this.findWord); 62 | } 63 | setTimeout(function () { 64 | /*istanbul ignore next*/_this2.mditor.editor.activeMark(0); 65 | }, 100); 66 | } 67 | }, 68 | /*istanbul ignore next*/parseRegexp: function parseRegexp(text, forceStr) { 69 | if (!forceStr && CHECK_REGEXP.test(text)) { 70 | try { 71 | return new Function( /*istanbul ignore next*/'return ' + text)(); 72 | } catch (err) { 73 | return this.parseRegexp(text, true); 74 | } 75 | } else { 76 | return new RegExp(utils.escapeRegExp(text), 'gm'); 77 | } 78 | }, 79 | /*istanbul ignore next*/find: function find() { 80 | this.mditor.editor.activeMark(); 81 | }, 82 | /*istanbul ignore next*/replace: function replace() { 83 | this.mditor.value = this.mditor.value.replace(this.mditor.editor.markExp, this.replaceWord || ''); 84 | }, 85 | /*istanbul ignore next*/onFindEnter: function onFindEnter(event) { 86 | if (event.keyCode != 13) return; 87 | event.preventDefault(); 88 | this.find(); 89 | }, 90 | /*istanbul ignore next*/onReplaceEnter: function onReplaceEnter(event) { 91 | if (event.keyCode != 13) return; 92 | event.preventDefault(); 93 | this.replace(); 94 | }, 95 | /*istanbul ignore next*/onCompositionEnd: function onCompositionEnd(event) { 96 | event.target.blur(); 97 | event.target.focus(); 98 | } 99 | }); 100 | 101 | module.exports = Finder; -------------------------------------------------------------------------------- /docs/assets/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var screenshots = ['scr1', 'scr2', 'scr3', 'scr4']; 4 | 5 | document.addEventListener('DOMContentLoaded', function() { 6 | initDownload(); 7 | setImages(); 8 | }); 9 | 10 | function setImages() { 11 | var odd = false; 12 | each('.feature>img', function(el) { 13 | if (odd) { 14 | el.parentNode.insertBefore(el, el.parentNode.firstChild); 15 | } 16 | odd = !odd; 17 | }); 18 | } 19 | 20 | function initDownload() { 21 | var os = detectOs(); 22 | if (os) { 23 | setDownloadButton(os); 24 | } 25 | } 26 | 27 | function detectOs() { 28 | var platform = navigator.platform.toLowerCase(); 29 | if (platform.indexOf('mac') >= 0) { 30 | return 'mac'; 31 | } 32 | if (platform.indexOf('linux') >= 0) { 33 | return 'linux'; 34 | } 35 | if (platform.indexOf('win') >= 0) { 36 | return 'win32'; 37 | } 38 | return undefined; 39 | } 40 | 41 | function setDownloadButton(os) { 42 | setDownloadButtonTitle(os); 43 | setLatestReleaseUrl(os); 44 | } 45 | 46 | function setDownloadButtonTitle(os) { 47 | each('.btn-download>.btn-desc', function(el) { 48 | switch (os) { 49 | case 'mac': 50 | el.innerHTML = ' for Mac OS X'; 51 | break; 52 | case 'win32': 53 | el.innerHTML = ' for Windows'; 54 | break; 55 | case 'linux': 56 | el.innerHTML = ' for Linux'; 57 | break; 58 | } 59 | }); 60 | each('.btn-sub-link', function(el) { 61 | el.style.display = 'block'; 62 | }); 63 | } 64 | 65 | function setLatestReleaseUrl(os) { 66 | var xhr = new XMLHttpRequest(); 67 | xhr.responseType = 'json'; 68 | xhr.addEventListener('load', function() { 69 | releasesLoaded(xhr.response, os) 70 | }); 71 | xhr.open('GET', 'https://api.github.com/repos/antelle/keeweb/releases/latest'); 72 | xhr.send(); 73 | } 74 | 75 | function releasesLoaded(releaseInfo, os) { 76 | var knownAssets = [ 77 | 'KeeWeb.linux.x64.deb', 78 | 'KeeWeb.mac.dmg', 79 | 'KeeWeb.win32.exe' 80 | ]; 81 | var url; 82 | releaseInfo.assets.forEach(function(asset) { 83 | if (asset.name.indexOf(os) > 0 && knownAssets.indexOf(asset.name) >= 0) { 84 | url = asset.browser_download_url; 85 | } 86 | }); 87 | if (url) { 88 | each('.btn-download', function(el) { 89 | el.setAttribute('href', url); 90 | }); 91 | } 92 | } 93 | 94 | function rotateScreenshot(next) { 95 | var el = document.getElementById('scr-large'); 96 | var src = el.getAttribute('src'); 97 | var pic = src.match(/scr\d/)[0]; 98 | var ix = screenshots.indexOf(pic); 99 | ix = (ix + screenshots.length + (next ? 1 : -1)) % screenshots.length; 100 | src = src.replace(pic, screenshots[ix]); 101 | el.setAttribute('src', src); 102 | each('.screenshot-loader', function(el) { 103 | el.style.display = 'inline-block'; 104 | }); 105 | } 106 | 107 | function screenshotLoaded() { 108 | each('.screenshot-loader', function(el) { 109 | el.style.display = 'none'; 110 | }); 111 | } 112 | 113 | function each(sel, fn) { 114 | Array.prototype.forEach.call(document.querySelectorAll(sel), fn); 115 | } 116 | -------------------------------------------------------------------------------- /packages/desktop/docs/assets/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var screenshots = ['scr1', 'scr2', 'scr3', 'scr4']; 4 | 5 | document.addEventListener('DOMContentLoaded', function() { 6 | initDownload(); 7 | setImages(); 8 | }); 9 | 10 | function setImages() { 11 | var odd = false; 12 | each('.feature>img', function(el) { 13 | if (odd) { 14 | el.parentNode.insertBefore(el, el.parentNode.firstChild); 15 | } 16 | odd = !odd; 17 | }); 18 | } 19 | 20 | function initDownload() { 21 | var os = detectOs(); 22 | if (os) { 23 | setDownloadButton(os); 24 | } 25 | } 26 | 27 | function detectOs() { 28 | var platform = navigator.platform.toLowerCase(); 29 | if (platform.indexOf('mac') >= 0) { 30 | return 'mac'; 31 | } 32 | if (platform.indexOf('linux') >= 0) { 33 | return 'linux'; 34 | } 35 | if (platform.indexOf('win') >= 0) { 36 | return 'win32'; 37 | } 38 | return undefined; 39 | } 40 | 41 | function setDownloadButton(os) { 42 | setDownloadButtonTitle(os); 43 | setLatestReleaseUrl(os); 44 | } 45 | 46 | function setDownloadButtonTitle(os) { 47 | each('.btn-download>.btn-desc', function(el) { 48 | switch (os) { 49 | case 'mac': 50 | el.innerHTML = ' for Mac OS X'; 51 | break; 52 | case 'win32': 53 | el.innerHTML = ' for Windows'; 54 | break; 55 | case 'linux': 56 | el.innerHTML = ' for Linux'; 57 | break; 58 | } 59 | }); 60 | each('.btn-sub-link', function(el) { 61 | el.style.display = 'block'; 62 | }); 63 | } 64 | 65 | function setLatestReleaseUrl(os) { 66 | var xhr = new XMLHttpRequest(); 67 | xhr.responseType = 'json'; 68 | xhr.addEventListener('load', function() { 69 | releasesLoaded(xhr.response, os) 70 | }); 71 | xhr.open('GET', 'https://api.github.com/repos/antelle/keeweb/releases/latest'); 72 | xhr.send(); 73 | } 74 | 75 | function releasesLoaded(releaseInfo, os) { 76 | var knownAssets = [ 77 | 'KeeWeb.linux.x64.deb', 78 | 'KeeWeb.mac.dmg', 79 | 'KeeWeb.win32.exe' 80 | ]; 81 | var url; 82 | releaseInfo.assets.forEach(function(asset) { 83 | if (asset.name.indexOf(os) > 0 && knownAssets.indexOf(asset.name) >= 0) { 84 | url = asset.browser_download_url; 85 | } 86 | }); 87 | if (url) { 88 | each('.btn-download', function(el) { 89 | el.setAttribute('href', url); 90 | }); 91 | } 92 | } 93 | 94 | function rotateScreenshot(next) { 95 | var el = document.getElementById('scr-large'); 96 | var src = el.getAttribute('src'); 97 | var pic = src.match(/scr\d/)[0]; 98 | var ix = screenshots.indexOf(pic); 99 | ix = (ix + screenshots.length + (next ? 1 : -1)) % screenshots.length; 100 | src = src.replace(pic, screenshots[ix]); 101 | el.setAttribute('src', src); 102 | each('.screenshot-loader', function(el) { 103 | el.style.display = 'inline-block'; 104 | }); 105 | } 106 | 107 | function screenshotLoaded() { 108 | each('.screenshot-loader', function(el) { 109 | el.style.display = 'none'; 110 | }); 111 | } 112 | 113 | function each(sel, fn) { 114 | Array.prototype.forEach.call(document.querySelectorAll(sel), fn); 115 | } 116 | -------------------------------------------------------------------------------- /packages/embed/src/client/index.less: -------------------------------------------------------------------------------- 1 | /** 2 | Mditor 公共样式 3 | **/ 4 | 5 | @font-face { 6 | font-family: 'RobotoMono'; 7 | font-style: normal; 8 | font-weight: 400; 9 | src: url('../assets/fonts/RobotoMono-Regular.ttf') format('truetype'); 10 | } 11 | 12 | .mditor { 13 | transition: .05s; 14 | border-radius: 3px; 15 | padding: 38px 0 0 0; 16 | overflow: hidden; 17 | background-color: #fff; 18 | border: 1px solid #ccc; 19 | position: relative; 20 | outline: 0; 21 | &, * { 22 | box-sizing: border-box; 23 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 24 | } 25 | .head { 26 | -webkit-touch-callout: none; 27 | user-select: none; 28 | cursor: default; 29 | outline: 0; 30 | position: absolute; 31 | height: 38px; 32 | left: 0; 33 | top: 0; 34 | width: 100%; 35 | zoom: 1; 36 | margin: 0; 37 | padding: 0px; 38 | border-bottom: 1px solid #CCC; 39 | border-radius: 3px 3px 0 0; 40 | background: #f1f1f1; 41 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4); 42 | background-color: #f3f3f3; 43 | background-image: -moz-linear-gradient(top, #f5f5f5, #efefef); 44 | background-image: -ms-linear-gradient(top, #f5f5f5, #efefef); 45 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#efefef)); 46 | background-image: -webkit-linear-gradient(top, #f5f5f5, #efefef); 47 | background-image: -o-linear-gradient(top, #f5f5f5, #efefef); 48 | background-image: linear-gradient(top, #f5f5f5, #efefef); 49 | background-repeat: repeat-x; 50 | } 51 | .body { 52 | margin: 0px; 53 | padding: 0px; 54 | position: relative; 55 | line-height: 0; 56 | font-size: 0; 57 | border-radius: 0 0 3px 3px; 58 | box-shadow: 0 0 4px 0 rgba(0, 0, 0, .1) inset; 59 | height: 100%; 60 | &.active { 61 | border: 1px solid #aaa; 62 | } 63 | } 64 | &.split { 65 | .editor, .viewer { 66 | width: 50%; 67 | float: left; 68 | } 69 | .editor { 70 | border-right: 1px solid #ddd; 71 | } 72 | } 73 | &.preview { 74 | .viewer, { 75 | width: 100%; 76 | } 77 | .editor, .toolbar .item { 78 | display: none; 79 | } 80 | .toolbar .item.control { 81 | display: inline-block; 82 | } 83 | } 84 | &.fullscreen { 85 | position: fixed; 86 | margin: 0px !important; 87 | left: 0px !important; 88 | top: 0px !important; 89 | width: 100% !important; 90 | height: 100% !important; 91 | min-width: 100% !important; 92 | min-height: 100% !important; 93 | max-width: 100% !important; 94 | max-height: 100% !important; 95 | z-index: 999999; 96 | border-radius: 0px; 97 | border: none; 98 | } 99 | &.toolbar-hidden { 100 | .head { 101 | display: none; 102 | } 103 | .body { 104 | border-radius: 3px; 105 | } 106 | } 107 | .textarea, .backdrop, .viewer { 108 | -webkit-font-smoothing: antialiased; 109 | font: 16px/1.6 'RobotoMono', 'PingFang SC', 'Lantinghei SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', '\\5FAE\8F6F\96C5\9ED1', 'STHeitiSC-Light', 'simsun', '\\5B8B\4F53', 'WenQuanYi Zen Hei', 'WenQuanYi Micro Hei', sans-serif; 110 | } 111 | } 112 | 113 | .mditor-hidden { 114 | display: none; 115 | } 116 | 117 | .markdown-body { 118 | &>ul { 119 | list-style-type: initial; 120 | } 121 | &>ol { 122 | list-style-type: decimal; 123 | } 124 | table { 125 | display: table; 126 | } 127 | hr { 128 | height: 2px; 129 | padding: 0; 130 | margin: 24px 0; 131 | background-color: #eaecef; 132 | border: 0; 133 | } 134 | img { 135 | background-color: rgba(255, 255, 255, .1); 136 | } 137 | } -------------------------------------------------------------------------------- /packages/embed/src/common/parser.js: -------------------------------------------------------------------------------- 1 | const marked = require('marked'); 2 | const Prism = require('prismjs'); 3 | 4 | //language 5 | require('prismjs/components/prism-java'); 6 | require('prismjs/components/prism-csharp'); 7 | require('prismjs/components/prism-php'); 8 | require('prismjs/components/prism-python'); 9 | require('prismjs/components/prism-json'); 10 | require('prismjs/components/prism-yaml.min'); 11 | require('prismjs/components/prism-perl'); 12 | require('prismjs/components/prism-go'); 13 | require('prismjs/components/prism-bash'); 14 | require('prismjs/components/prism-fsharp'); 15 | require('prismjs/components/prism-typescript'); 16 | require('prismjs/components/prism-stylus'); 17 | require('prismjs/components/prism-less'); 18 | require('prismjs/components/prism-sass'); 19 | require('prismjs/components/prism-handlebars'); 20 | require('prismjs/components/prism-applescript'); 21 | require('prismjs/components/prism-actionscript'); 22 | require('prismjs/components/prism-aspnet'); 23 | require('prismjs/components/prism-basic'); 24 | require('prismjs/components/prism-c'); 25 | require('prismjs/components/prism-pascal'); 26 | require('prismjs/components/prism-vim'); 27 | require('prismjs/components/prism-swift'); 28 | require('prismjs/components/prism-objectivec'); 29 | require('prismjs/components/prism-sql'); 30 | require('prismjs/components/prism-scheme'); 31 | require('prismjs/components/prism-ruby'); 32 | require('prismjs/components/prism-smarty'); 33 | require('prismjs/components/prism-smalltalk'); 34 | require('prismjs/components/prism-rust'); 35 | require('prismjs/components/prism-r'); 36 | require('prismjs/components/prism-d'); 37 | require('prismjs/components/prism-dart'); 38 | require('prismjs/components/prism-coffeescript'); 39 | require('prismjs/components/prism-batch'); 40 | require('prismjs/components/prism-cpp'); 41 | require('prismjs/components/prism-lua'); 42 | require('prismjs/components/prism-livescript'); 43 | require('prismjs/components/prism-latex'); 44 | require('prismjs/components/prism-groovy'); 45 | require('prismjs/components/prism-graphql'); 46 | require('prismjs/components/prism-nginx'); 47 | require('prismjs/components/prism-erlang'); 48 | require('prismjs/components/prism-powershell'); 49 | require('prismjs/components/prism-makefile'); 50 | require('prismjs/components/prism-markdown'); 51 | 52 | //alias 53 | Prism.languages.js = Prism.languages.javascript; 54 | Prism.languages['c#'] = Prism.languages.csharp; 55 | Prism.languages['f#'] = Prism.languages.fsharp; 56 | Prism.languages.sh = Prism.languages.bash; 57 | Prism.languages.md = Prism.languages.markdown; 58 | Prism.languages.py = Prism.languages.python; 59 | Prism.languages.yml = Prism.languages.yaml; 60 | Prism.languages.rb = Prism.languages.ruby; 61 | 62 | const Parser = function (options) { 63 | options = options || {}; 64 | this.options = options; 65 | }; 66 | 67 | Parser.highlights = {}; 68 | Parser.marked = marked; 69 | Parser.Prism = Prism; 70 | 71 | //使标题解析 # 号可以无空格 72 | marked.Lexer.rules.gfm.heading = marked.Lexer.rules.heading; 73 | marked.Lexer.rules.tables.heading = marked.Lexer.rules.heading; 74 | 75 | let renderer = new marked.Renderer(); 76 | Parser.renderer = renderer; 77 | marked.setOptions({ 78 | renderer: renderer, gfm: true, tables: true, breaks: true, //可行尾不加两空格直接换行 79 | pedantic: false, 80 | sanitize: false, 81 | smartLists: true, 82 | smartypants: false, 83 | mangle: false, 84 | highlight: function (code, lang, callback) { 85 | if (Parser.highlights[lang]) { 86 | let result = Parser.highlights[lang].parse(code, lang, callback); 87 | if (!callback) 88 | return result; 89 | } else if (Prism.languages[lang]) { 90 | let result = Prism.highlight(code, Prism.languages[lang]); 91 | if (callback) 92 | return callback(null, result); 93 | else 94 | return result; 95 | } else { 96 | if (callback) //eslint-disable-line 97 | return callback(null, code); 98 | else 99 | return code; 100 | } 101 | } 102 | }); 103 | 104 | Parser.prototype.parse = function (mdText, callback) { 105 | return marked(mdText, callback); 106 | }; 107 | 108 | module.exports = Parser; -------------------------------------------------------------------------------- /packages/embed/lib/common/parser.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var marked = require('marked'); 4 | var Prism = require('prismjs'); 5 | 6 | //language 7 | require('prismjs/components/prism-java'); 8 | require('prismjs/components/prism-csharp'); 9 | require('prismjs/components/prism-php'); 10 | require('prismjs/components/prism-python'); 11 | require('prismjs/components/prism-json'); 12 | require('prismjs/components/prism-yaml.min'); 13 | require('prismjs/components/prism-perl'); 14 | require('prismjs/components/prism-go'); 15 | require('prismjs/components/prism-bash'); 16 | require('prismjs/components/prism-fsharp'); 17 | require('prismjs/components/prism-typescript'); 18 | require('prismjs/components/prism-stylus'); 19 | require('prismjs/components/prism-less'); 20 | require('prismjs/components/prism-sass'); 21 | require('prismjs/components/prism-handlebars'); 22 | require('prismjs/components/prism-applescript'); 23 | require('prismjs/components/prism-actionscript'); 24 | require('prismjs/components/prism-aspnet'); 25 | require('prismjs/components/prism-basic'); 26 | require('prismjs/components/prism-c'); 27 | require('prismjs/components/prism-pascal'); 28 | require('prismjs/components/prism-vim'); 29 | require('prismjs/components/prism-swift'); 30 | require('prismjs/components/prism-objectivec'); 31 | require('prismjs/components/prism-sql'); 32 | require('prismjs/components/prism-scheme'); 33 | require('prismjs/components/prism-ruby'); 34 | require('prismjs/components/prism-smarty'); 35 | require('prismjs/components/prism-smalltalk'); 36 | require('prismjs/components/prism-rust'); 37 | require('prismjs/components/prism-r'); 38 | require('prismjs/components/prism-d'); 39 | require('prismjs/components/prism-dart'); 40 | require('prismjs/components/prism-coffeescript'); 41 | require('prismjs/components/prism-batch'); 42 | require('prismjs/components/prism-cpp'); 43 | require('prismjs/components/prism-lua'); 44 | require('prismjs/components/prism-livescript'); 45 | require('prismjs/components/prism-latex'); 46 | require('prismjs/components/prism-groovy'); 47 | require('prismjs/components/prism-graphql'); 48 | require('prismjs/components/prism-nginx'); 49 | require('prismjs/components/prism-erlang'); 50 | require('prismjs/components/prism-powershell'); 51 | require('prismjs/components/prism-makefile'); 52 | require('prismjs/components/prism-markdown'); 53 | 54 | //alias 55 | Prism.languages.js = Prism.languages.javascript; 56 | Prism.languages['c#'] = Prism.languages.csharp; 57 | Prism.languages['f#'] = Prism.languages.fsharp; 58 | Prism.languages.sh = Prism.languages.bash; 59 | Prism.languages.md = Prism.languages.markdown; 60 | Prism.languages.py = Prism.languages.python; 61 | Prism.languages.yml = Prism.languages.yaml; 62 | Prism.languages.rb = Prism.languages.ruby; 63 | 64 | var Parser = function Parser(options) { 65 | options = options || {}; 66 | this.options = options; 67 | }; 68 | 69 | Parser.highlights = {}; 70 | Parser.marked = marked; 71 | Parser.Prism = Prism; 72 | 73 | //使标题解析 # 号可以无空格 74 | marked.Lexer.rules.gfm.heading = marked.Lexer.rules.heading; 75 | marked.Lexer.rules.tables.heading = marked.Lexer.rules.heading; 76 | 77 | var renderer = new marked.Renderer(); 78 | Parser.renderer = renderer; 79 | marked.setOptions({ 80 | renderer: renderer, gfm: true, tables: true, breaks: true, //可行尾不加两空格直接换行 81 | pedantic: false, 82 | sanitize: false, 83 | smartLists: true, 84 | smartypants: false, 85 | mangle: false, 86 | highlight: function /*istanbul ignore next*/highlight(code, lang, callback) { 87 | if (Parser.highlights[lang]) { 88 | var result = Parser.highlights[lang].parse(code, lang, callback); 89 | if (!callback) return result; 90 | } else if (Prism.languages[lang]) { 91 | var _result = Prism.highlight(code, Prism.languages[lang]); 92 | if (callback) return callback(null, _result);else return _result; 93 | } else { 94 | if (callback) //eslint-disable-line 95 | return callback(null, code);else return code; 96 | } 97 | } 98 | }); 99 | 100 | Parser.prototype.parse = function (mdText, callback) { 101 | return marked(mdText, callback); 102 | }; 103 | 104 | module.exports = Parser; -------------------------------------------------------------------------------- /packages/embed/src/toolbar/items.js: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | name: 'bold', 3 | title: '粗体', 4 | key: 'shift+alt+b', 5 | handler() { 6 | this.editor.wrapSelectText('**', '**'); 7 | } 8 | }, { 9 | name: 'italic', 10 | title: '斜体', 11 | key: 'shift+alt+i', 12 | handler() { 13 | this.editor.wrapSelectText('*', '*'); 14 | } 15 | }, { 16 | name: 'underline', 17 | title: '下划线', 18 | key: 'shift+alt+e', 19 | handler() { 20 | this.editor.wrapSelectText('', ''); 21 | } 22 | }, { 23 | name: 'strikethrough', 24 | title: '删除线', 25 | key: 'shift+alt+d', 26 | handler() { 27 | this.editor.wrapSelectText('~~', '~~'); 28 | } 29 | }, { 30 | name: 'header', 31 | title: '标题', 32 | key: 'shift+alt+1', 33 | handler() { 34 | this.editor.wrapSelectText('# '); 35 | } 36 | }, { 37 | name: 'quote', 38 | icon: 'quote-left', 39 | title: '引用', 40 | key: 'shift+alt+q', 41 | handler() { 42 | let selectText = this.editor.getSelectText(); 43 | if (selectText.length < 1) { 44 | this.editor.wrapSelectText('> '); 45 | return; 46 | } 47 | let textArray = selectText.split(this.EOL); 48 | let buffer = []; 49 | textArray.forEach(function (line) { 50 | buffer.push('> ' + line + ' '); 51 | }); 52 | this.editor.setSelectText(buffer.join(this.EOL) + this.EOL); 53 | } 54 | }, { 55 | name: 'code', 56 | title: '代码', 57 | key: 'shift+alt+c', 58 | handler() { 59 | let lang = 'js' + this.EOL; 60 | let before = '```' + lang; 61 | let after = '``` ' + this.EOL; 62 | let text = this.editor.getSelectText().trim(); 63 | if (text.length > 0) { 64 | text += this.EOL; 65 | } 66 | this.editor.setSelectText(text); 67 | this.editor.wrapSelectText(before, after); 68 | let range = this.editor.getSelectRange(); 69 | let start = range.start - lang.length; 70 | let end = range.start - this.EOL.length; 71 | this.editor.setSelectRange(start, end); 72 | } 73 | }, { 74 | name: 'list-ol', 75 | title: '有序列表', 76 | key: 'shift+alt+o', 77 | handler() { 78 | let selectText = this.editor.getSelectText(); 79 | if (selectText.length < 1) { 80 | this.editor.wrapSelectText('1. '); 81 | return; 82 | } 83 | let textArray = selectText.split(this.EOL); 84 | let buffer = []; 85 | for (let i = 0; i < textArray.length; i++) { 86 | let line = textArray[i]; 87 | buffer.push((i + 1) + '. ' + line); 88 | } 89 | this.editor.setSelectText(buffer.join(this.EOL) + this.EOL); 90 | } 91 | }, { 92 | name: 'list-ul', 93 | title: '无序列表', 94 | key: 'shift+alt+u', 95 | handler() { 96 | let selectText = this.editor.getSelectText(); 97 | if (selectText.length < 1) { 98 | this.editor.wrapSelectText('- '); 99 | return; 100 | } 101 | let textArray = selectText.split(this.EOL); 102 | let buffer = []; 103 | textArray.forEach(function (line) { 104 | buffer.push('- ' + line); 105 | }); 106 | this.editor.setSelectText(buffer.join(this.EOL) + this.EOL); 107 | } 108 | }, { 109 | name: 'link', 110 | title: '链接', 111 | key: 'shift+alt+l', 112 | handler() { 113 | let text = this.editor.getSelectText(); 114 | if (!text || /^(https:|http:|ftp:|file:|mailto:|\/|\.)/i.test(text)) { 115 | this.editor.wrapSelectText('[link](', ')'); 116 | if (!text) return; 117 | let range = this.editor.getSelectRange(); 118 | let start = range.start - 6; 119 | this.editor.setSelectRange(start, start + 4); 120 | } else { 121 | this.editor.wrapSelectText('[', ']()'); 122 | let range = this.editor.getSelectRange(); 123 | let index = range.end + 2; 124 | this.editor.setSelectRange(index, index); 125 | } 126 | } 127 | }, { 128 | name: 'table', 129 | title: '表格', 130 | key: 'shift+alt+t', 131 | handler() { 132 | let buffer = [ 133 | 'column1 | column2 | column3 ', 134 | '------- | ------- | ------- ', 135 | 'column1 | column2 | column3 ', 136 | 'column1 | column2 | column3 ', 137 | 'column1 | column2 | column3 ' 138 | ]; 139 | this.editor.wrapSelectText(buffer.join(this.EOL) + this.EOL); 140 | } 141 | }, { 142 | name: 'line', 143 | title: '分隔线', 144 | icon: 'minus', 145 | key: 'shift+alt+h', 146 | handler() { 147 | this.editor.wrapSelectText('----' + this.EOL); 148 | } 149 | }, { 150 | name: 'image', 151 | title: '图片', 152 | key: 'shift+alt+p', 153 | handler() { 154 | this.editor.wrapSelectText('![alt](', ')'); 155 | } 156 | }, { 157 | name: 'help', 158 | title: '帮助', 159 | icon: 'question', 160 | key: 'shift+alt+/', 161 | handler() { 162 | window.open('http://mditor.com', '_blank'); 163 | } 164 | }, { 165 | name: 'toggleFullScreen', 166 | title: '全屏', 167 | icon: 'arrows-alt', 168 | key: 'shift+alt+f', 169 | control: true, 170 | state: 'fullscreen', 171 | owner: function (mditor) { 172 | return mditor.$element; 173 | }, 174 | handler() { 175 | this.fullscreen = !this.fullscreen; 176 | } 177 | }, { 178 | name: 'togglePreview', 179 | title: '预览', 180 | icon: 'desktop', 181 | key: 'shift+alt+v', 182 | control: true, 183 | state: 'preview', 184 | owner: function (mditor) { 185 | return mditor.$element; 186 | }, 187 | handler() { 188 | this.preview = !this.preview; 189 | if (this.preview) { 190 | this._split = this.split; 191 | this.split = false; 192 | } else { 193 | this.split = this._split; 194 | } 195 | } 196 | }, { 197 | name: 'toggleSplit', 198 | title: '分屏', 199 | icon: 'columns', 200 | key: 'shift+alt+s', 201 | control: true, 202 | state: 'split', 203 | owner: function (mditor) { 204 | return mditor.$element; 205 | }, 206 | handler() { 207 | this.split = !this.split; 208 | if (this.split) { 209 | this.preview = false; 210 | } 211 | } 212 | }]; -------------------------------------------------------------------------------- /docs/assets/site.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, 2 | a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, 3 | small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, 4 | table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, 5 | figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, 6 | time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } 7 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } 8 | body { line-height: 1; } 9 | ol, ul { list-style: none; } 10 | blockquote, q { quotes: none; } 11 | blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } 12 | table { border-collapse: collapse; border-spacing: 0; } 13 | .right { float: right; } 14 | .left { float: left; } 15 | .clear { clear: both; } 16 | 17 | body { 18 | font-family: -apple-system, "BlinkMacSystemFont", "Open Sans", "Raleway", "Helvetica Neue", "Helvetica", "Arial", sans-serif; 19 | font-feature-settings: "liga" 0; 20 | text-align: center; 21 | background: #F5F5F5; 22 | font-weight: 300; 23 | background-image: url("../assets/background.jpg"); 24 | background-size: cover; 25 | background-repeat: no-repeat; 26 | } 27 | 28 | #intro { 29 | color: #263C69; 30 | } 31 | 32 | h1 { 33 | font-size: 3em; 34 | padding-top: 10vh; 35 | } 36 | h1>span { 37 | line-height: 65px; 38 | vertical-align: top; 39 | } 40 | h2 { 41 | font-size: 2em; 42 | font-weight: 100; 43 | margin: 1em; 44 | } 45 | h3 { 46 | font-size: 1.5em; 47 | font-weight: 100; 48 | margin-bottom: 1em; 49 | } 50 | hr { 51 | margin: 1.5em 0; 52 | height: 1px; 53 | background: #e3e3e3; 54 | border: none; 55 | } 56 | .logo { 57 | width: 64px; 58 | height: 64px; 59 | } 60 | .buttons-col { 61 | display: inline-block; 62 | vertical-align: top; 63 | } 64 | .btn { 65 | appearance: none; 66 | cursor: pointer; 67 | display: inline-block; 68 | font-size: 1.2em; 69 | line-height: 1; 70 | padding: .6em 1.4em; 71 | margin: 1em 1em .4em; 72 | text-decoration: none; 73 | vertical-align: middle; 74 | white-space: nowrap; 75 | outline: none; 76 | min-width: 80px; 77 | border-radius: 2px; 78 | -webkit-user-select: none; 79 | -moz-user-select: none; 80 | background: #528BFF; 81 | border: 1px solid #4A86FF; 82 | color: #fafafa; 83 | } 84 | .btn.btn-large { 85 | width: 180px; 86 | } 87 | .btn:hover { 88 | background: #4B85FA; 89 | border-color: #417EFA; 90 | color: #fefefe; 91 | box-shadow: 0 0 20px rgba(0, 0, 0, .05); 92 | } 93 | .btn:active { 94 | background: #467EF0; 95 | border-color: #437FF7; 96 | color: #fff; 97 | } 98 | .btn-desc { 99 | font-size: .8rem; 100 | margin-top: .5em; 101 | font-weight: 100; 102 | } 103 | .btn-sub-link { 104 | text-decoration: none; 105 | color: rgb(86, 86, 86); 106 | font-size: .8rem; 107 | display: block; 108 | transition: color 100ms ease-out; 109 | } 110 | .btn-sub-link-platforms { 111 | display: none; 112 | } 113 | .btn-sub-link:hover { 114 | color: #528BFF; 115 | transition: color 100ms ease-in; 116 | } 117 | .screenshots { 118 | max-width: 100vw; 119 | white-space: nowrap; 120 | -webkit-user-select: none; 121 | -moz-user-select: none; 122 | position: relative; 123 | } 124 | .screenshot { 125 | width: 850px; 126 | max-width: 80vw; 127 | position: relative; 128 | vertical-align: middle; 129 | } 130 | .screenshot-loader.fa { 131 | width: 200px; 132 | position:absolute; 133 | left: 0; 134 | right: 0; 135 | top: 50%; 136 | margin: -70px auto 0 auto; 137 | font-size: 120px; 138 | color: rgba(128, 128, 128, .05); 139 | } 140 | .scr-arrow { 141 | vertical-align: middle; 142 | cursor: pointer; 143 | display: inline-block; 144 | } 145 | .scr-arrow:before { 146 | font-size: 4em; 147 | color: #ccc; 148 | } 149 | .scr-arrow:hover:before { 150 | color: #528BFF; 151 | } 152 | .feature { 153 | border-bottom: 1px solid #e3e3e3; 154 | padding: 2em 0; 155 | } 156 | .feature-item { 157 | display: inline-block; 158 | width: 400px; 159 | text-align: left; 160 | vertical-align: top; 161 | margin: .5em 3em; 162 | } 163 | .feature-item>h2 { 164 | margin: 0 0 .8em; 165 | } 166 | .feature-item>p { 167 | line-height: 1.4em; 168 | padding-bottom: 1em; 169 | } 170 | .feature>img { 171 | width: 350px; 172 | border-radius: 3px; 173 | /**box-shadow: 0 0 20px rgba(0, 0, 0, .2);**/ 174 | margin: .5em 3em; 175 | } 176 | 177 | @media only screen and (max-width: 510px) { 178 | 179 | .feature-item { 180 | width: 80vw; 181 | margin: .5em 2.5em; 182 | } 183 | .feature>img { 184 | width: 90vw; 185 | margin: .5em 1em; 186 | } 187 | } 188 | 189 | @media screen and (max-width: 945px) { 190 | .feature-item { 191 | text-align: center; 192 | } 193 | } 194 | 195 | @media screen and (min-width: 945px) { 196 | .padding35 { 197 | padding-top: 50px; 198 | } 199 | 200 | .padding50 { 201 | padding-top: 50px; 202 | } 203 | 204 | .padding100 { 205 | padding-top: 100px; 206 | } 207 | } 208 | 209 | 210 | .img-ext { 211 | width: 250px; 212 | opacity: .9; 213 | } 214 | .footer { 215 | color: #999; 216 | font-size: .8em; 217 | margin: 2em; 218 | } 219 | .footer a { 220 | color: #999; 221 | text-decoration: none; 222 | } 223 | .footer a ~ a { 224 | margin-left: 1.5em; 225 | } 226 | .footer a:hover { 227 | color: #528BFF; 228 | } 229 | 230 | .hide { 231 | display: none; 232 | } 233 | 234 | .twitter-follow-button { 235 | position: fixed !important; 236 | top: 10px; 237 | left: 20px; 238 | z-index: 10; 239 | } -------------------------------------------------------------------------------- /packages/desktop/docs/assets/site.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, 2 | a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, 3 | small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, 4 | table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, 5 | figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, 6 | time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } 7 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } 8 | body { line-height: 1; } 9 | ol, ul { list-style: none; } 10 | blockquote, q { quotes: none; } 11 | blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } 12 | table { border-collapse: collapse; border-spacing: 0; } 13 | .right { float: right; } 14 | .left { float: left; } 15 | .clear { clear: both; } 16 | 17 | body { 18 | font-family: -apple-system, "BlinkMacSystemFont", "Open Sans", "Raleway", "Helvetica Neue", "Helvetica", "Arial", sans-serif; 19 | font-feature-settings: "liga" 0; 20 | text-align: center; 21 | background: #F5F5F5; 22 | font-weight: 300; 23 | background-image: url("../assets/background.jpg"); 24 | background-size: cover; 25 | background-repeat: no-repeat; 26 | } 27 | 28 | #intro { 29 | color: #263C69; 30 | } 31 | 32 | h1 { 33 | font-size: 3em; 34 | padding-top: 10vh; 35 | } 36 | h1>span { 37 | line-height: 65px; 38 | vertical-align: top; 39 | } 40 | h2 { 41 | font-size: 2em; 42 | font-weight: 100; 43 | margin: 1em; 44 | } 45 | h3 { 46 | font-size: 1.5em; 47 | font-weight: 100; 48 | margin-bottom: 1em; 49 | } 50 | hr { 51 | margin: 1.5em 0; 52 | height: 1px; 53 | background: #e3e3e3; 54 | border: none; 55 | } 56 | .logo { 57 | width: 64px; 58 | height: 64px; 59 | } 60 | .buttons-col { 61 | display: inline-block; 62 | vertical-align: top; 63 | } 64 | .btn { 65 | appearance: none; 66 | cursor: pointer; 67 | display: inline-block; 68 | font-size: 1.2em; 69 | line-height: 1; 70 | padding: .6em 1.4em; 71 | margin: 1em 1em .4em; 72 | text-decoration: none; 73 | vertical-align: middle; 74 | white-space: nowrap; 75 | outline: none; 76 | min-width: 80px; 77 | border-radius: 2px; 78 | -webkit-user-select: none; 79 | -moz-user-select: none; 80 | background: #528BFF; 81 | border: 1px solid #4A86FF; 82 | color: #fafafa; 83 | } 84 | .btn.btn-large { 85 | width: 180px; 86 | } 87 | .btn:hover { 88 | background: #4B85FA; 89 | border-color: #417EFA; 90 | color: #fefefe; 91 | box-shadow: 0 0 20px rgba(0, 0, 0, .05); 92 | } 93 | .btn:active { 94 | background: #467EF0; 95 | border-color: #437FF7; 96 | color: #fff; 97 | } 98 | .btn-desc { 99 | font-size: .8rem; 100 | margin-top: .5em; 101 | font-weight: 100; 102 | } 103 | .btn-sub-link { 104 | text-decoration: none; 105 | color: rgb(86, 86, 86); 106 | font-size: .8rem; 107 | display: block; 108 | transition: color 100ms ease-out; 109 | } 110 | .btn-sub-link-platforms { 111 | display: none; 112 | } 113 | .btn-sub-link:hover { 114 | color: #528BFF; 115 | transition: color 100ms ease-in; 116 | } 117 | .screenshots { 118 | max-width: 100vw; 119 | white-space: nowrap; 120 | -webkit-user-select: none; 121 | -moz-user-select: none; 122 | position: relative; 123 | } 124 | .screenshot { 125 | width: 850px; 126 | max-width: 80vw; 127 | position: relative; 128 | vertical-align: middle; 129 | } 130 | .screenshot-loader.fa { 131 | width: 200px; 132 | position:absolute; 133 | left: 0; 134 | right: 0; 135 | top: 50%; 136 | margin: -70px auto 0 auto; 137 | font-size: 120px; 138 | color: rgba(128, 128, 128, .05); 139 | } 140 | .scr-arrow { 141 | vertical-align: middle; 142 | cursor: pointer; 143 | display: inline-block; 144 | } 145 | .scr-arrow:before { 146 | font-size: 4em; 147 | color: #ccc; 148 | } 149 | .scr-arrow:hover:before { 150 | color: #528BFF; 151 | } 152 | .feature { 153 | border-bottom: 1px solid #e3e3e3; 154 | padding: 2em 0; 155 | } 156 | .feature-item { 157 | display: inline-block; 158 | width: 400px; 159 | text-align: left; 160 | vertical-align: top; 161 | margin: .5em 3em; 162 | } 163 | .feature-item>h2 { 164 | margin: 0 0 .8em; 165 | } 166 | .feature-item>p { 167 | line-height: 1.4em; 168 | padding-bottom: 1em; 169 | } 170 | .feature>img { 171 | width: 350px; 172 | border-radius: 3px; 173 | /**box-shadow: 0 0 20px rgba(0, 0, 0, .2);**/ 174 | margin: .5em 3em; 175 | } 176 | 177 | @media only screen and (max-width: 510px) { 178 | 179 | .feature-item { 180 | width: 80vw; 181 | margin: .5em 2.5em; 182 | } 183 | .feature>img { 184 | width: 90vw; 185 | margin: .5em 1em; 186 | } 187 | } 188 | 189 | @media screen and (max-width: 945px) { 190 | .feature-item { 191 | text-align: center; 192 | } 193 | } 194 | 195 | @media screen and (min-width: 945px) { 196 | .padding35 { 197 | padding-top: 50px; 198 | } 199 | 200 | .padding50 { 201 | padding-top: 50px; 202 | } 203 | 204 | .padding100 { 205 | padding-top: 100px; 206 | } 207 | } 208 | 209 | 210 | .img-ext { 211 | width: 250px; 212 | opacity: .9; 213 | } 214 | .footer { 215 | color: #999; 216 | font-size: .8em; 217 | margin: 2em; 218 | } 219 | .footer a { 220 | color: #999; 221 | text-decoration: none; 222 | } 223 | .footer a ~ a { 224 | margin-left: 1.5em; 225 | } 226 | .footer a:hover { 227 | color: #528BFF; 228 | } 229 | 230 | .hide { 231 | display: none; 232 | } 233 | 234 | .twitter-follow-button { 235 | position: fixed !important; 236 | top: 10px; 237 | left: 20px; 238 | z-index: 10; 239 | } -------------------------------------------------------------------------------- /packages/desktop/src/window/index.js: -------------------------------------------------------------------------------- 1 | require('./index.less'); 2 | 3 | const mokit = require('mokit'); 4 | const Mditor = require('mditor/src/client'); 5 | const drapable = require('./drapable'); 6 | const ipcRenderer = nodeRequire('electron').ipcRenderer; 7 | const pkg = require('../../package'); 8 | const UMLParser = require('../uml'); 9 | const utils = require('ntils'); 10 | const blobToBase64 = require('../common/blob2buffer'); 11 | 12 | //初始处理 13 | drapable(document.body); 14 | window.open = function (url) { 15 | remote.shell.openExternal(url); 16 | }; 17 | 18 | const baseElement = document.querySelector('base'); 19 | 20 | //插件或语法扩展 21 | let languages = Mditor.Parser.Prism.languages; 22 | let highlights = Mditor.Parser.highlights; 23 | highlights['uml'] = new UMLParser(); 24 | languages['editor'] = languages['yaml']; 25 | languages['shortcut'] = languages['yaml']; 26 | languages['slide'] = languages['yaml']; 27 | 28 | //context 29 | const ctx = window.ctx = mokit({ 30 | element: document.body, 31 | components: { 32 | Mditor 33 | }, 34 | 35 | /** 36 | * 组件就续时 37 | * @returns {void} 无返回 38 | */ 39 | onReady() { 40 | this.currentWindow = remote.getCurrentWindow(); 41 | this.mditor.removeCommand('toggleFullScreen'); 42 | this.overrideToolbar(); 43 | this.applyPreference(remote.getGlobal('preference')); 44 | this.applyLocale(remote.getGlobal('locale')); 45 | }, 46 | 47 | /** 48 | * 在右击时弹出内容菜单 49 | * @param {object} event 事件对象 50 | * @returns {void} 无返回 51 | */ 52 | onContextMenu(event) { 53 | if (event.target != this.mditor.editor.textarea) return; 54 | ipcRenderer.send('contextmenu'); 55 | }, 56 | 57 | /** 58 | * 重写帮助按钮 59 | * @returns {void} 无返回 60 | */ 61 | overrideToolbar() { 62 | //帮助按钮 63 | let helpBtn = this.mditor.toolbar.getItem('help'); 64 | helpBtn.handler = () => { 65 | remote.shell.openExternal(pkg.homepage); 66 | }; 67 | //图片按钮 68 | let imgBtn = this.mditor.toolbar.getItem('image'); 69 | imgBtn.handler = () => { 70 | remote.dialog.showOpenDialog(this.currentWindow, { 71 | filters: [{ 72 | name: 'Images', 73 | extensions: ['png', 'jpg', 'jpeg', 'gif'] 74 | }], 75 | properties: ['openFile', 'multiSelections'] 76 | }, this.insertImage.bind(this)); 77 | }; 78 | }, 79 | 80 | insertImage(filenames) { 81 | if (!filenames) return; 82 | if (!utils.isArray(filenames)) { 83 | filenames = [filenames]; 84 | } 85 | filenames = filenames.filter(item => !!item); 86 | if (!filenames || filenames.length < 1) return; 87 | let text = filenames.map(filename => { 88 | return `![${filename.split('/').pop()}](file://${filename})`; 89 | }).join(this.mditor.EOL); 90 | this.mditor.editor.insertBeforeText(text); 91 | }, 92 | 93 | openFile(filename) { 94 | ipcRenderer.send('open-file', { 95 | filename: filename, 96 | windowId: this.currentWindow.id 97 | }); 98 | }, 99 | 100 | onChanged() { 101 | ipcRenderer.send('content-changed', { 102 | filename: ctx.filename, 103 | windowId: this.currentWindow.id 104 | }); 105 | }, 106 | 107 | async onPaste(event) { 108 | let items = [].slice.call(event.clipboardData.items); 109 | let imageItems = items.filter(item => { 110 | return item.type.startsWith('image/'); 111 | }).map(item => ({ 112 | type: item.type, 113 | file: item.getAsFile() 114 | })); 115 | if (imageItems.length < 1) return; 116 | event.preventDefault(); 117 | await Promise.all(imageItems.map(item => { 118 | return blobToBase64(item.file).then(content => { 119 | item.content = content; 120 | }); 121 | })); 122 | imageItems.forEach(image => { 123 | ipcRenderer.send('save-image', { 124 | type: image.type, 125 | content: image.content 126 | }); 127 | }); 128 | }, 129 | 130 | toggleMaximize() { 131 | if (this.currentWindow.isMaximized()) { 132 | this.currentWindow.unmaximize(); 133 | } else { 134 | this.currentWindow.maximize(); 135 | } 136 | }, 137 | 138 | applyPreference(preference) { 139 | if (!preference) return; 140 | this.applyEditorPreference(preference.editor); 141 | this.applyShortcutPreference(preference.shortcut); 142 | }, 143 | 144 | applyEditorPreference(configs) { 145 | configs = configs || {}; 146 | if (!utils.isNumber(configs.tab)) configs.tab = 2; 147 | if (configs.tab < 1) { 148 | this.mditor.INDENT = '\t'; 149 | } else { 150 | this.mditor.INDENT = new Array(configs.tab).fill(' ').join(''); 151 | } 152 | this.mditor.editor.textarea.style.color = configs.color || ''; 153 | this.mditor.editor.$element.style.backgroundColor = configs.backgroundColor || ''; 154 | }, 155 | 156 | applyShortcutPreference(configs) { 157 | if (!configs) return; 158 | utils.each(configs, (cmd, key) => { 159 | if (!key || !cmd) return; 160 | this.mditor.shortcut.unbind(key); 161 | this.mditor.shortcut.bind(key, cmd); 162 | }); 163 | }, 164 | 165 | applyLocale(locale) { 166 | this.mditor.viewer.alert = locale.previewArea; 167 | this.mditor.toolbar.items.forEach(item => { 168 | let name = item.name.replace(/\-([a-z]{1})/ig, $1 => $1.slice(1).toUpperCase()); 169 | item.title = locale[name]; 170 | }); 171 | } 172 | 173 | }).start(); 174 | 175 | //在收到文件内容时 176 | ipcRenderer.on('file', function (event, info) { 177 | document.title = info.filename; 178 | baseElement.href = info.filename; 179 | ctx.filename = info.filename; 180 | ctx.mditor.value = info.content; 181 | ctx.mditor.editor.stack.init({ 182 | value: info.content 183 | }); 184 | }); 185 | 186 | //在收到执行命令时 187 | ipcRenderer.on('command', function (event, info) { 188 | ctx.mditor.execCommand(info.name); 189 | }); 190 | 191 | //在收到偏好设置时 192 | ipcRenderer.on('preference', function (event, preference) { 193 | ctx.applyPreference(preference); 194 | }); 195 | 196 | //在收到国际化设置时 197 | ipcRenderer.on('locale', function (event, locale) { 198 | ctx.applyLocale(locale); 199 | }); 200 | 201 | //在收到插入图片时 202 | ipcRenderer.on('image', function (event, info) { 203 | ctx.insertImage(info.filename); 204 | }); -------------------------------------------------------------------------------- /packages/embed/src/client/index.js: -------------------------------------------------------------------------------- 1 | const mokit = require('mokit'); 2 | const Toolbar = require('../toolbar'); 3 | const Editor = require('../editor'); 4 | const Viewer = require('../viewer'); 5 | const Finder = require('../finder'); 6 | const Shortcut = require('./shortcut'); 7 | const Parser = require('../common/parser'); 8 | 9 | require('font-awesome/css/font-awesome.css'); 10 | require('github-markdown-css/github-markdown.css'); 11 | require('prismjs/themes/prism.css'); 12 | require('./index.less'); 13 | 14 | const HIDDEN_CLASS_NAME = 'mditor-hidden'; 15 | 16 | const Mditor = new mokit.Component({ 17 | template: require('./index.html'), 18 | 19 | onInit() { 20 | this.PLATFORM = navigator.platform.toLowerCase(); 21 | this.EOL = this.PLATFORM == 'win32' ? '\r\n' : '\n'; 22 | this.CMD = this.PLATFORM.indexOf('mac') > -1 ? 'command' : 'ctrl'; 23 | this.INDENT = ' '; 24 | this.shortcut = new Shortcut(this); 25 | this.Parser = Parser; 26 | this.parser = new Parser(this); 27 | }, 28 | 29 | onReady() { 30 | this.shortcut.bind('tab', this.editor.addIndent.bind(this.editor)); 31 | this.shortcut.bind('shift+tab', this.editor.removeIndent.bind(this.editor)); 32 | this.shortcut.bind('enter', (event) => { 33 | this._ulAndQuoteAutoComplete(event); 34 | this._olAutoComplete(event); 35 | this._keepIndent(event); 36 | }, true); 37 | setTimeout(() => { 38 | this.$emit('ready'); 39 | }, 0); 40 | }, 41 | 42 | components: { 43 | Toolbar, 44 | Editor, 45 | Viewer, 46 | Finder 47 | }, 48 | 49 | props: { 50 | height: '400px', 51 | width: 'auto', 52 | preview: false, 53 | split: true, 54 | fullscreen: false 55 | }, 56 | 57 | data() { 58 | return { 59 | self: this, 60 | value: '' 61 | }; 62 | }, 63 | 64 | find(text) { 65 | this.finder.show(text); 66 | }, 67 | 68 | syncScroll() { 69 | if (!this.split || this.preview) return; 70 | let offsetHeight = this.editor.textarea.offsetHeight; 71 | let editorScrollHeight = this.editor.textarea.scrollHeight; 72 | let viewerScrollHeight = this.viewer.$element.scrollHeight; 73 | let editorScrollTop = this.editor.textarea.scrollTop; 74 | let viewerScrollTop = editorScrollTop * (viewerScrollHeight - offsetHeight) / (editorScrollHeight - offsetHeight); 75 | this.viewer.$element.scrollTop = viewerScrollTop; 76 | }, 77 | 78 | onChanged(event) { 79 | this.$emit('changed', event); 80 | this.syncScroll(); 81 | }, 82 | 83 | onInput(event) { 84 | this.$emit('input', event); 85 | }, 86 | 87 | onPaste(event) { 88 | this.$emit('paste', event); 89 | this.syncScroll(); 90 | }, 91 | 92 | onHeadDblClick(event) { 93 | if (event.target.tagName == 'I') return; 94 | this.$emit('head-dblclick', event); 95 | }, 96 | 97 | _keepIndent(event) { 98 | let text = this.editor.getBeforeTextInLine(); 99 | let parts = text.split(this.INDENT); 100 | if (parts.length < 2) return; 101 | let count = 0; 102 | let buffer = [this.EOL]; 103 | while (parts[count] === '' && 104 | count < (parts.length - 1)) { 105 | count++; 106 | buffer.push(this.INDENT); 107 | } 108 | this.editor.insertBeforeText(buffer.join('')); 109 | event.preventDefault(); 110 | }, 111 | 112 | _ulAndQuoteAutoComplete(event) { 113 | let text = this.editor.getBeforeTextInLine(); 114 | let prefix = text.substr(0, 2); 115 | if (prefix != '- ' && prefix != '* ' && prefix != '> ') return; 116 | if (text.length > prefix.length) { 117 | this.editor.insertBeforeText(this.EOL + prefix); 118 | } else { 119 | this.editor.selectBeforeText(prefix.length); 120 | this.editor.setSelectText(''); 121 | } 122 | event.preventDefault(); 123 | }, 124 | 125 | _olAutoComplete(event) { 126 | let exp = /^\d+\./; 127 | let text = this.editor.getBeforeTextInLine(); 128 | let trimedText = text.trim(); 129 | if (!exp.test(trimedText)) return; 130 | let num = trimedText.split('.')[0]; 131 | if (trimedText.length > num.length + 1) { 132 | this.editor.insertBeforeText(this.EOL + (parseInt(num) + 1) + '. '); 133 | } else { 134 | this.editor.selectBeforeText(text.length); 135 | this.editor.setSelectText(''); 136 | } 137 | event.preventDefault(); 138 | }, 139 | 140 | focus() { 141 | if (this.preview) { 142 | this.$element.focus(); 143 | } else { 144 | this.editor.focus(); 145 | } 146 | }, 147 | 148 | blur() { 149 | this.editor.blur(); 150 | }, 151 | 152 | addCommand(item) { 153 | if (!item.name || !item.handler) return; 154 | this.commands = this.commands || {}; 155 | this.commands[item.name] = item; 156 | if (item.key) { 157 | this.shortcut.bind(item.key, item.name, item.allowDefault, item.owner); 158 | } 159 | }, 160 | 161 | removeCommand(name) { 162 | this.commands = this.commands || {}; 163 | let item = this.commands[name]; 164 | if (!item) return; 165 | this.shortcut.unbind(item.key); 166 | this.commands[name] = null; 167 | delete this.commands[name]; 168 | }, 169 | 170 | execCommand(name, event) { 171 | event = event || {}; 172 | event.mditor = this; 173 | event.toolbar = this.toolbar; 174 | event.editor = this.editor; 175 | this.commands[name].handler.call(this, event); 176 | } 177 | 178 | }); 179 | 180 | Mditor.fromTextarea = function (textarea) { 181 | textarea.classList.add(HIDDEN_CLASS_NAME); 182 | let mditor = new Mditor(); 183 | mditor.value = textarea.value; 184 | mditor.$watch('value', () => { 185 | textarea.value = mditor.value; 186 | }); 187 | mditor.$mount(textarea); 188 | mditor.switchTextarea = function () { 189 | if (textarea.classList.contains(HIDDEN_CLASS_NAME)) { 190 | textarea.value = mditor.value; 191 | mditor.$element.classList.add(HIDDEN_CLASS_NAME); 192 | textarea.classList.remove(HIDDEN_CLASS_NAME); 193 | } else { 194 | mditor.value = textarea.value; 195 | textarea.classList.add(HIDDEN_CLASS_NAME); 196 | mditor.$element.classList.remove(HIDDEN_CLASS_NAME); 197 | } 198 | }; 199 | return mditor; 200 | }; 201 | 202 | Mditor.Parser = Parser; 203 | 204 | module.exports = window.Mditor = Mditor; -------------------------------------------------------------------------------- /packages/embed/lib/toolbar/items.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | module.exports = [{ 4 | name: 'bold', 5 | title: '粗体', 6 | key: 'shift+alt+b', 7 | /*istanbul ignore next*/handler: function handler() { 8 | this.editor.wrapSelectText('**', '**'); 9 | } 10 | }, { 11 | name: 'italic', 12 | title: '斜体', 13 | key: 'shift+alt+i', 14 | /*istanbul ignore next*/handler: function handler() { 15 | this.editor.wrapSelectText('*', '*'); 16 | } 17 | }, { 18 | name: 'underline', 19 | title: '下划线', 20 | key: 'shift+alt+e', 21 | /*istanbul ignore next*/handler: function handler() { 22 | this.editor.wrapSelectText('', ''); 23 | } 24 | }, { 25 | name: 'strikethrough', 26 | title: '删除线', 27 | key: 'shift+alt+d', 28 | /*istanbul ignore next*/handler: function handler() { 29 | this.editor.wrapSelectText('~~', '~~'); 30 | } 31 | }, { 32 | name: 'header', 33 | title: '标题', 34 | key: 'shift+alt+1', 35 | /*istanbul ignore next*/handler: function handler() { 36 | this.editor.wrapSelectText('# '); 37 | } 38 | }, { 39 | name: 'quote', 40 | icon: 'quote-left', 41 | title: '引用', 42 | key: 'shift+alt+q', 43 | /*istanbul ignore next*/handler: function handler() { 44 | var selectText = this.editor.getSelectText(); 45 | if (selectText.length < 1) { 46 | this.editor.wrapSelectText('> '); 47 | return; 48 | } 49 | var textArray = selectText.split(this.EOL); 50 | var buffer = []; 51 | textArray.forEach(function (line) { 52 | buffer.push('> ' + line + ' '); 53 | }); 54 | this.editor.setSelectText(buffer.join(this.EOL) + this.EOL); 55 | } 56 | }, { 57 | name: 'code', 58 | title: '代码', 59 | key: 'shift+alt+c', 60 | /*istanbul ignore next*/handler: function handler() { 61 | var lang = 'js' + this.EOL; 62 | var before = '```' + lang; 63 | var after = '``` ' + this.EOL; 64 | var text = this.editor.getSelectText().trim(); 65 | if (text.length > 0) { 66 | text += this.EOL; 67 | } 68 | this.editor.setSelectText(text); 69 | this.editor.wrapSelectText(before, after); 70 | var range = this.editor.getSelectRange(); 71 | var start = range.start - lang.length; 72 | var end = range.start - this.EOL.length; 73 | this.editor.setSelectRange(start, end); 74 | } 75 | }, { 76 | name: 'list-ol', 77 | title: '有序列表', 78 | key: 'shift+alt+o', 79 | /*istanbul ignore next*/handler: function handler() { 80 | var selectText = this.editor.getSelectText(); 81 | if (selectText.length < 1) { 82 | this.editor.wrapSelectText('1. '); 83 | return; 84 | } 85 | var textArray = selectText.split(this.EOL); 86 | var buffer = []; 87 | for (var i = 0; i < textArray.length; i++) { 88 | var line = textArray[i]; 89 | buffer.push(i + 1 + '. ' + line); 90 | } 91 | this.editor.setSelectText(buffer.join(this.EOL) + this.EOL); 92 | } 93 | }, { 94 | name: 'list-ul', 95 | title: '无序列表', 96 | key: 'shift+alt+u', 97 | /*istanbul ignore next*/handler: function handler() { 98 | var selectText = this.editor.getSelectText(); 99 | if (selectText.length < 1) { 100 | this.editor.wrapSelectText('- '); 101 | return; 102 | } 103 | var textArray = selectText.split(this.EOL); 104 | var buffer = []; 105 | textArray.forEach(function (line) { 106 | buffer.push('- ' + line); 107 | }); 108 | this.editor.setSelectText(buffer.join(this.EOL) + this.EOL); 109 | } 110 | }, { 111 | name: 'link', 112 | title: '链接', 113 | key: 'shift+alt+l', 114 | /*istanbul ignore next*/handler: function handler() { 115 | var text = this.editor.getSelectText(); 116 | if (!text || /^(https:|http:|ftp:|file:|mailto:|\/|\.)/i.test(text)) { 117 | this.editor.wrapSelectText('[link](', ')'); 118 | if (!text) return; 119 | var range = this.editor.getSelectRange(); 120 | var start = range.start - 6; 121 | this.editor.setSelectRange(start, start + 4); 122 | } else { 123 | this.editor.wrapSelectText('[', ']()'); 124 | var _range = this.editor.getSelectRange(); 125 | var index = _range.end + 2; 126 | this.editor.setSelectRange(index, index); 127 | } 128 | } 129 | }, { 130 | name: 'table', 131 | title: '表格', 132 | key: 'shift+alt+t', 133 | /*istanbul ignore next*/handler: function handler() { 134 | var buffer = ['column1 | column2 | column3 ', '------- | ------- | ------- ', 'column1 | column2 | column3 ', 'column1 | column2 | column3 ', 'column1 | column2 | column3 ']; 135 | this.editor.wrapSelectText(buffer.join(this.EOL) + this.EOL); 136 | } 137 | }, { 138 | name: 'line', 139 | title: '分隔线', 140 | icon: 'minus', 141 | key: 'shift+alt+h', 142 | /*istanbul ignore next*/handler: function handler() { 143 | this.editor.wrapSelectText('----' + this.EOL); 144 | } 145 | }, { 146 | name: 'image', 147 | title: '图片', 148 | key: 'shift+alt+p', 149 | /*istanbul ignore next*/handler: function handler() { 150 | this.editor.wrapSelectText('![alt](', ')'); 151 | } 152 | }, { 153 | name: 'help', 154 | title: '帮助', 155 | icon: 'question', 156 | key: 'shift+alt+/', 157 | /*istanbul ignore next*/handler: function handler() { 158 | window.open('http://mditor.com', '_blank'); 159 | } 160 | }, { 161 | name: 'toggleFullScreen', 162 | title: '全屏', 163 | icon: 'arrows-alt', 164 | key: 'shift+alt+f', 165 | control: true, 166 | state: 'fullscreen', 167 | owner: function /*istanbul ignore next*/owner(mditor) { 168 | return mditor.$element; 169 | }, 170 | /*istanbul ignore next*/handler: function handler() { 171 | this.fullscreen = !this.fullscreen; 172 | } 173 | }, { 174 | name: 'togglePreview', 175 | title: '预览', 176 | icon: 'desktop', 177 | key: 'shift+alt+v', 178 | control: true, 179 | state: 'preview', 180 | owner: function /*istanbul ignore next*/owner(mditor) { 181 | return mditor.$element; 182 | }, 183 | /*istanbul ignore next*/handler: function handler() { 184 | this.preview = !this.preview; 185 | if (this.preview) { 186 | this._split = this.split; 187 | this.split = false; 188 | } else { 189 | this.split = this._split; 190 | } 191 | } 192 | }, { 193 | name: 'toggleSplit', 194 | title: '分屏', 195 | icon: 'columns', 196 | key: 'shift+alt+s', 197 | control: true, 198 | state: 'split', 199 | owner: function /*istanbul ignore next*/owner(mditor) { 200 | return mditor.$element; 201 | }, 202 | /*istanbul ignore next*/handler: function handler() { 203 | this.split = !this.split; 204 | if (this.split) { 205 | this.preview = false; 206 | } 207 | } 208 | }]; -------------------------------------------------------------------------------- /packages/embed/lib/client/index.js: -------------------------------------------------------------------------------- 1 | /*istanbul ignore next*/'use strict'; 2 | 3 | var mokit = require('mokit'); 4 | var Toolbar = require('../toolbar'); 5 | var Editor = require('../editor'); 6 | var Viewer = require('../viewer'); 7 | var Finder = require('../finder'); 8 | var Shortcut = require('./shortcut'); 9 | var Parser = require('../common/parser'); 10 | 11 | require('font-awesome/css/font-awesome.css'); 12 | require('github-markdown-css/github-markdown.css'); 13 | require('prismjs/themes/prism.css'); 14 | require('./index.less'); 15 | 16 | var HIDDEN_CLASS_NAME = 'mditor-hidden'; 17 | 18 | var Mditor = new mokit.Component({ 19 | template: require('./index.html'), 20 | 21 | /*istanbul ignore next*/onInit: function onInit() { 22 | this.PLATFORM = navigator.platform.toLowerCase(); 23 | this.EOL = this.PLATFORM == 'win32' ? '\r\n' : '\n'; 24 | this.CMD = this.PLATFORM.indexOf('mac') > -1 ? 'command' : 'ctrl'; 25 | this.INDENT = ' '; 26 | this.shortcut = new Shortcut(this); 27 | this.Parser = Parser; 28 | this.parser = new Parser(this); 29 | }, 30 | /*istanbul ignore next*/onReady: function onReady() { 31 | /*istanbul ignore next*/var _this = this; 32 | 33 | this.shortcut.bind('tab', this.editor.addIndent.bind(this.editor)); 34 | this.shortcut.bind('shift+tab', this.editor.removeIndent.bind(this.editor)); 35 | this.shortcut.bind('enter', function (event) { 36 | /*istanbul ignore next*/_this._ulAndQuoteAutoComplete(event); 37 | /*istanbul ignore next*/_this._olAutoComplete(event); 38 | /*istanbul ignore next*/_this._keepIndent(event); 39 | }, true); 40 | setTimeout(function () { 41 | /*istanbul ignore next*/_this.$emit('ready'); 42 | }, 0); 43 | }, 44 | 45 | 46 | components: { 47 | Toolbar: Toolbar, 48 | Editor: Editor, 49 | Viewer: Viewer, 50 | Finder: Finder 51 | }, 52 | 53 | props: { 54 | height: '400px', 55 | width: 'auto', 56 | preview: false, 57 | split: true, 58 | fullscreen: false 59 | }, 60 | 61 | /*istanbul ignore next*/data: function data() { 62 | return { 63 | self: this, 64 | value: '' 65 | }; 66 | }, 67 | /*istanbul ignore next*/find: function find(text) { 68 | this.finder.show(text); 69 | }, 70 | /*istanbul ignore next*/syncScroll: function syncScroll() { 71 | if (!this.split || this.preview) return; 72 | var offsetHeight = this.editor.textarea.offsetHeight; 73 | var editorScrollHeight = this.editor.textarea.scrollHeight; 74 | var viewerScrollHeight = this.viewer.$element.scrollHeight; 75 | var editorScrollTop = this.editor.textarea.scrollTop; 76 | var viewerScrollTop = editorScrollTop * (viewerScrollHeight - offsetHeight) / (editorScrollHeight - offsetHeight); 77 | this.viewer.$element.scrollTop = viewerScrollTop; 78 | }, 79 | /*istanbul ignore next*/onChanged: function onChanged(event) { 80 | this.$emit('changed', event); 81 | this.syncScroll(); 82 | }, 83 | /*istanbul ignore next*/onInput: function onInput(event) { 84 | this.$emit('input', event); 85 | }, 86 | /*istanbul ignore next*/onPaste: function onPaste(event) { 87 | this.$emit('paste', event); 88 | this.syncScroll(); 89 | }, 90 | /*istanbul ignore next*/onHeadDblClick: function onHeadDblClick(event) { 91 | if (event.target.tagName == 'I') return; 92 | this.$emit('head-dblclick', event); 93 | }, 94 | /*istanbul ignore next*/_keepIndent: function _keepIndent(event) { 95 | var text = this.editor.getBeforeTextInLine(); 96 | var parts = text.split(this.INDENT); 97 | if (parts.length < 2) return; 98 | var count = 0; 99 | var buffer = [this.EOL]; 100 | while (parts[count] === '' && count < parts.length - 1) { 101 | count++; 102 | buffer.push(this.INDENT); 103 | } 104 | this.editor.insertBeforeText(buffer.join('')); 105 | event.preventDefault(); 106 | }, 107 | /*istanbul ignore next*/_ulAndQuoteAutoComplete: function _ulAndQuoteAutoComplete(event) { 108 | var text = this.editor.getBeforeTextInLine(); 109 | var prefix = text.substr(0, 2); 110 | if (prefix != '- ' && prefix != '* ' && prefix != '> ') return; 111 | if (text.length > prefix.length) { 112 | this.editor.insertBeforeText(this.EOL + prefix); 113 | } else { 114 | this.editor.selectBeforeText(prefix.length); 115 | this.editor.setSelectText(''); 116 | } 117 | event.preventDefault(); 118 | }, 119 | /*istanbul ignore next*/_olAutoComplete: function _olAutoComplete(event) { 120 | var exp = /^\d+\./; 121 | var text = this.editor.getBeforeTextInLine(); 122 | var trimedText = text.trim(); 123 | if (!exp.test(trimedText)) return; 124 | var num = trimedText.split('.')[0]; 125 | if (trimedText.length > num.length + 1) { 126 | this.editor.insertBeforeText(this.EOL + (parseInt(num) + 1) + '. '); 127 | } else { 128 | this.editor.selectBeforeText(text.length); 129 | this.editor.setSelectText(''); 130 | } 131 | event.preventDefault(); 132 | }, 133 | /*istanbul ignore next*/focus: function focus() { 134 | if (this.preview) { 135 | this.$element.focus(); 136 | } else { 137 | this.editor.focus(); 138 | } 139 | }, 140 | /*istanbul ignore next*/blur: function blur() { 141 | this.editor.blur(); 142 | }, 143 | /*istanbul ignore next*/addCommand: function addCommand(item) { 144 | if (!item.name || !item.handler) return; 145 | this.commands = this.commands || {}; 146 | this.commands[item.name] = item; 147 | if (item.key) { 148 | this.shortcut.bind(item.key, item.name, item.allowDefault, item.owner); 149 | } 150 | }, 151 | /*istanbul ignore next*/removeCommand: function removeCommand(name) { 152 | this.commands = this.commands || {}; 153 | var item = this.commands[name]; 154 | if (!item) return; 155 | this.shortcut.unbind(item.key); 156 | this.commands[name] = null; 157 | delete this.commands[name]; 158 | }, 159 | /*istanbul ignore next*/execCommand: function execCommand(name, event) { 160 | event = event || {}; 161 | event.mditor = this; 162 | event.toolbar = this.toolbar; 163 | event.editor = this.editor; 164 | this.commands[name].handler.call(this, event); 165 | } 166 | }); 167 | 168 | Mditor.fromTextarea = function (textarea) { 169 | textarea.classList.add(HIDDEN_CLASS_NAME); 170 | var mditor = new Mditor(); 171 | mditor.value = textarea.value; 172 | mditor.$watch('value', function () { 173 | textarea.value = mditor.value; 174 | }); 175 | mditor.$mount(textarea); 176 | mditor.switchTextarea = function () { 177 | if (textarea.classList.contains(HIDDEN_CLASS_NAME)) { 178 | textarea.value = mditor.value; 179 | mditor.$element.classList.add(HIDDEN_CLASS_NAME); 180 | textarea.classList.remove(HIDDEN_CLASS_NAME); 181 | } else { 182 | mditor.value = textarea.value; 183 | textarea.classList.add(HIDDEN_CLASS_NAME); 184 | mditor.$element.classList.remove(HIDDEN_CLASS_NAME); 185 | } 186 | }; 187 | return mditor; 188 | }; 189 | 190 | Mditor.Parser = Parser; 191 | 192 | module.exports = window.Mditor = Mditor; --------------------------------------------------------------------------------