├── .eslintignore ├── .eslintrc ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmignore ├── .prettierrc ├── .storybook ├── .babelrc └── config.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bonus.png ├── config ├── env.js ├── jest │ ├── cssTransform.js │ └── fileTransform.js ├── paths.js ├── webpack.config.js ├── webpack.config.lib.js └── webpackDevServer.config.js ├── group.png ├── main.js ├── package.json ├── public ├── favicon.svg ├── index.html └── manifest.json ├── scripts ├── build.js ├── start.js └── test.js ├── src ├── App.css ├── App.js ├── App.test.js ├── Lib.js ├── component │ ├── Dialog │ │ ├── AboutDialog.js │ │ ├── FormDialog.js │ │ ├── HistoryDialog.js │ │ ├── ImageDialog.js │ │ ├── LinkDialog.js │ │ ├── SitDownDialog.js │ │ ├── TutorialDialog.js │ │ ├── VersionDialog.css │ │ └── VersionDialog.js │ ├── ImageHosting │ │ ├── AliOSS.js │ │ ├── GitHub.js │ │ ├── Gitee.js │ │ └── QiniuOSS.js │ ├── LocalHistory │ │ ├── index.js │ │ ├── indexdb.js │ │ ├── localHistory.css │ │ └── util.js │ ├── MenuLeft │ │ ├── CodeTheme.css │ │ ├── CodeTheme.js │ │ ├── File.js │ │ ├── File │ │ │ ├── ExportMarkdown.js │ │ │ ├── ExportPdf.js │ │ │ └── ImportFile.js │ │ ├── Function.js │ │ ├── Function │ │ │ ├── History.js │ │ │ ├── Reset.js │ │ │ ├── Search.js │ │ │ └── SitDown.js │ │ ├── Help.js │ │ ├── Help │ │ │ ├── About.js │ │ │ ├── Document.js │ │ │ ├── Question.js │ │ │ └── Version.js │ │ ├── LogIn.js │ │ ├── Login.css │ │ ├── Paragraph.js │ │ ├── Pattern.js │ │ ├── Pattern │ │ │ ├── Bold.js │ │ │ ├── Code.js │ │ │ ├── Del.js │ │ │ ├── Font.js │ │ │ ├── Form.js │ │ │ ├── Format.js │ │ │ ├── Image.js │ │ │ ├── InlineCode.js │ │ │ ├── Italic.js │ │ │ ├── Link.js │ │ │ └── LinkToFoot.js │ │ ├── Setting.js │ │ ├── Setting │ │ │ ├── ContainImgName.js │ │ │ └── SyncScroll.js │ │ ├── Theme.css │ │ ├── Theme.js │ │ ├── Tutorial.js │ │ ├── View.js │ │ ├── View │ │ │ ├── EditArea.js │ │ │ ├── FullScreen.js │ │ │ ├── PreviewArea.js │ │ │ └── ThemeArea.js │ │ └── common.css │ ├── SearchBox │ │ ├── SearchBox.css │ │ └── index.js │ └── Sidebar │ │ ├── Juejin.css │ │ ├── Juejin.js │ │ ├── PreviewType.css │ │ ├── PreviewType.js │ │ ├── Wechat.css │ │ ├── Wechat.js │ │ ├── Zhihu.css │ │ └── Zhihu.js ├── icon │ ├── Close.js │ ├── Copy.js │ ├── Down.js │ ├── Environment.js │ ├── FontCase.js │ ├── GitHub.js │ ├── Inbox.js │ ├── Juejin.js │ ├── Mobile.js │ ├── More.js │ ├── PC.js │ ├── Rabbit.js │ ├── Replace.js │ ├── ReplaceAll.js │ ├── Smile.js │ ├── User.js │ ├── Wechat.js │ ├── Zhihu.js │ ├── index.css │ └── index.js ├── index.css ├── index.d.ts ├── index.js ├── layout │ ├── Dialog.js │ ├── EditorMenu.css │ ├── EditorMenu.js │ ├── Footer.css │ ├── Footer.js │ ├── Navbar.css │ ├── Navbar.js │ ├── Sidebar.css │ ├── Sidebar.js │ └── StyleEditor.js ├── logo.svg ├── serviceWorker.js ├── store │ ├── content.js │ ├── dialog.js │ ├── footer.js │ ├── imageHosting.js │ ├── navbar.js │ ├── title.js │ ├── userInfo.js │ └── view.js ├── template │ ├── basic.js │ ├── code │ │ ├── atomOneDark.js │ │ ├── atomOneLight.js │ │ ├── github.js │ │ ├── monokai.js │ │ ├── vs2015.js │ │ └── xcode.js │ ├── content.md │ ├── index.js │ ├── macCode │ │ ├── macAtomOneDark.js │ │ ├── macAtomOneLight.js │ │ ├── macGithub.js │ │ ├── macMonokai.js │ │ ├── macVs2015.js │ │ └── macXcode.js │ └── markdown │ │ ├── custom.js │ │ └── normal.js └── utils │ ├── appContext.js │ ├── constant.js │ ├── converter.js │ ├── editorKeyEvents.js │ ├── helper.js │ ├── hotkey.js │ ├── imageHosting.js │ ├── langHighlight.js │ ├── markdown-it-imageflow.js │ ├── markdown-it-li.js │ ├── markdown-it-linkfoot.js │ ├── markdown-it-math.js │ ├── markdown-it-multiquote.js │ ├── markdown-it-removepre.js │ ├── markdown-it-span.js │ ├── markdown-it-table-container.js │ ├── mdMirror.css │ ├── pluginCenter.js │ ├── sitdownConverter.js │ └── styleMirror.css ├── stories ├── allImageHosting.js ├── defaultImageHosting.js ├── index.js ├── noneImageHosting.js └── online.js ├── watch.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | /config/ 2 | /scripts/ 3 | *.test.* 4 | /main.js 5 | *.d.ts -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "plugin:prettier/recommended"], // eslint扩展规则 3 | "parserOptions": { 4 | "ecmaVersion": 7, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "jsx": true 8 | } 9 | }, 10 | "parser": "babel-eslint", // 解决ES6 improt会报错 11 | "env": { 12 | // eg如果不配置browser,window就会被eslint报undefined的错 13 | "es6": true, 14 | "browser": true, 15 | "node": true 16 | }, 17 | "plugins": ["react", "jsx-a11y", "import"], 18 | "rules": { 19 | "quotes": [0, "double"], 20 | "class-methods-use-this": 0, 21 | "import/no-named-as-default": 0, 22 | "react/jsx-filename-extension": [ 23 | "error", 24 | { 25 | "extensions": [".js", ".jsx"] 26 | } 27 | ], 28 | "react/prop-types": 0, 29 | "react/destructuring-assignment": 0, 30 | "react/no-array-index-key": 0, 31 | "no-use-before-define": 0, 32 | "no-restricted-syntax": 0, 33 | "no-var": 0, 34 | "vars-on-top": 0, 35 | "no-plusplus": 0, 36 | "no-continue": 0, 37 | "prefer-template": 0, 38 | "object-shorthand": 0, 39 | "no-else-return": 0, 40 | "no-param-reassign": 0, 41 | "no-unused-vars": 1, 42 | "jsx-a11y/anchor-is-valid": 0, 43 | "react/jsx-props-no-spreading": 0, 44 | "import/no-extraneous-dependencies": 0, 45 | "import/order": 0, 46 | "jsx-a11y/mouse-events-have-key-events": 0, 47 | "no-lonely-if": 0, 48 | "one-var": 0, 49 | "react/prefer-stateless-function": 0, 50 | "react/jsx-wrap-multilines": 0, 51 | "no-unused-expressions": 0, 52 | "react/no-danger": 0, 53 | "no-console": 0, 54 | "camelcase": 0, 55 | "import/no-dynamic-require": 1, 56 | "global-require": 1, 57 | "react/forbid-prop-types": 1, 58 | "jsx-a11y/accessible-emoji": 0, 59 | "jsx-a11y/click-events-have-key-events": 0, 60 | "jsx-a11y/no-static-element-interactions": 0 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 提交 Bug 3 | about: 提交 Bug 用于改进本项目 4 | title: '' 5 | labels: bug 6 | assignees: guanpengchn 7 | 8 | --- 9 | 10 | **运行环境:** 11 | 12 | |操作系统|浏览器| 13 | |---|---| 14 | |macOS|Chrome| 15 | 16 | **Markdown文本内容:** 17 | 18 | 例如: 19 | 20 | ```md 21 | - 默认文本1 22 | - 默认文本2 23 | - 默认文本3 24 | ``` 25 | 26 | **错误详情:** 27 | 28 | 例如:块级公式转换错误,控制台报错A is undefined -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 新特性 3 | about: 有新特性的需要 4 | title: '' 5 | labels: feature 6 | assignees: guanpengchn 7 | 8 | --- 9 | 10 | **新特性描述:** -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | dist 26 | package-lock.json 27 | lib -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | config 3 | dist 4 | node_modules 5 | public 6 | scripts 7 | src 8 | .eslintrc 9 | .gitignore 10 | .prettierrc 11 | logo.png 12 | main.js 13 | package.json 14 | package-lock.json 15 | yarn.lock 16 | .npmignore -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": false, 7 | "jsxSingleQuote": false, 8 | "trailingComma": "all", 9 | "bracketSpacing": false, 10 | "jsxBracketSameLine": false, 11 | "arrowParens": "always" 12 | } -------------------------------------------------------------------------------- /.storybook/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env", 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { configure } from '@storybook/react'; 2 | 3 | function loadStories() { 4 | require('../stories/index.js'); 5 | // You can require as many stories as you need. 6 | } 7 | 8 | configure(loadStories, module); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 |

Markdown Nice

7 | 8 | ## 简介 9 | 10 | - 支持自定义样式的 Markdown 编辑器 11 | - 支持微信公众号、知乎和稀土掘金 12 | - 欢迎[在线使用](https://mdnice.com/) 13 | - 有疑问请参考 [如何有效的解决 mdnice 相关问题?](https://github.com/mdnice/markdown-nice/issues/163) 14 | 15 | ## 主题 16 | 17 | [Markdown Nice 主题列表](https://product.mdnice.com/themes/) 18 | 19 | > 欢迎提交主题,提供更多文章示例~~ 20 | 21 | ## 关于 22 | 23 | `mdnice`组建了**推文群**,欢迎反馈意见和公众号大佬们一起交流,关注公众号回复「排版」拉你入群。 24 | 25 | | 入群码 | 26 | | ------------------------------------------------------------------------------------------------ | 27 | | | 28 | 29 | ## 友情链接 30 | 31 | - [BlogHelper](https://github.com/ystcode/BlogHelper):一键发布本地文章到主流博客平台的托盘助手 32 | - [qrbtf](https://github.com/ciaochaos/qrbtf):艺术二维码生成器 33 | - [编程如画](https://draw.mdnice.com/):「编程如画」博客 34 | -------------------------------------------------------------------------------- /bonus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdnice/markdown-nice/6525a5aba371209c2840593e8f537b4a69137a4b/bonus.png -------------------------------------------------------------------------------- /config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | var dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. Variable expansion is supported in .env files. 31 | // https://github.com/motdotla/dotenv 32 | // https://github.com/motdotla/dotenv-expand 33 | dotenvFiles.forEach(dotenvFile => { 34 | if (fs.existsSync(dotenvFile)) { 35 | require('dotenv-expand')( 36 | require('dotenv').config({ 37 | path: dotenvFile, 38 | }) 39 | ); 40 | } 41 | }); 42 | 43 | // We support resolving modules according to `NODE_PATH`. 44 | // This lets you use absolute paths in imports inside large monorepos: 45 | // https://github.com/facebook/create-react-app/issues/253. 46 | // It works similar to `NODE_PATH` in Node itself: 47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 50 | // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 51 | // We also resolve them to make sure all tools using them work consistently. 52 | const appDirectory = fs.realpathSync(process.cwd()); 53 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 54 | .split(path.delimiter) 55 | .filter(folder => folder && !path.isAbsolute(folder)) 56 | .map(folder => path.resolve(appDirectory, folder)) 57 | .join(path.delimiter); 58 | 59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 60 | // injected into the application via DefinePlugin in Webpack configuration. 61 | const REACT_APP = /^REACT_APP_/i; 62 | 63 | function getClientEnvironment(publicUrl) { 64 | const raw = Object.keys(process.env) 65 | .filter(key => REACT_APP.test(key)) 66 | .reduce( 67 | (env, key) => { 68 | env[key] = process.env[key]; 69 | return env; 70 | }, 71 | { 72 | // Useful for determining whether we’re running in production mode. 73 | // Most importantly, it switches React into the correct mode. 74 | NODE_ENV: process.env.NODE_ENV || 'development', 75 | // Useful for resolving the correct path to static assets in `public`. 76 | // For example, . 77 | // This should only be used as an escape hatch. Normally you would put 78 | // images into the `src` and `import` them in code to get their paths. 79 | PUBLIC_URL: publicUrl, 80 | } 81 | ); 82 | // Stringify all values so we can feed into Webpack DefinePlugin 83 | const stringified = { 84 | 'process.env': Object.keys(raw).reduce((env, key) => { 85 | env[key] = JSON.stringify(raw[key]); 86 | return env; 87 | }, {}), 88 | }; 89 | 90 | return { raw, stringified }; 91 | } 92 | 93 | module.exports = getClientEnvironment; 94 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | const assetFilename = JSON.stringify(path.basename(filename)); 11 | 12 | if (filename.match(/\.svg$/)) { 13 | return `module.exports = { 14 | __esModule: true, 15 | default: ${assetFilename}, 16 | ReactComponent: (props) => ({ 17 | $$typeof: Symbol.for('react.element'), 18 | type: 'svg', 19 | ref: null, 20 | key: null, 21 | props: Object.assign({}, props, { 22 | children: ${assetFilename} 23 | }) 24 | }), 25 | };`; 26 | } 27 | 28 | return `module.exports = ${assetFilename};`; 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const url = require("url"); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebook/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(inputPath, needsSlash) { 15 | const hasSlash = inputPath.endsWith("/"); 16 | if (hasSlash && !needsSlash) { 17 | return inputPath.substr(0, inputPath.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${inputPath}/`; 20 | } else { 21 | return inputPath; 22 | } 23 | } 24 | 25 | const getPublicUrl = (appPackageJson) => envPublicUrl || require(appPackageJson).homepage; 26 | 27 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 28 | // "public path" at which the app is served. 29 | // Webpack needs to know it to put the right