From b8b6dcf68d9ce305bf3568b7432fbac23aca027a Mon Sep 17 00:00:00 2001 From: Liu Jiangyi Date: Mon, 22 Jun 2020 00:00:48 +0800 Subject: [PATCH 1/6] feat: markdown render for thread author --- package-lock.json | 51 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/Flows.js | 3 ++- src/Markdown.js | 8 ++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/Markdown.js diff --git a/package-lock.json b/package-lock.json index e5996db..4af2a5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7899,6 +7899,14 @@ "object.assign": "^4.1.0" } }, + "katex": { + "version": "0.6.0", + "resolved": "https://r.cnpmjs.org/katex/download/katex-0.6.0.tgz", + "integrity": "sha1-EkGOCRIcBckgQbazuftrqyE8tvM=", + "requires": { + "match-at": "^0.1.0" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -7958,6 +7966,14 @@ "type-check": "~0.3.2" } }, + "linkify-it": { + "version": "3.0.2", + "resolved": "https://r.cnpmjs.org/linkify-it/download/linkify-it-3.0.2.tgz", + "integrity": "sha1-9V7ri8HTrnVASeEkqzu1bZd5f7g=", + "requires": { + "uc.micro": "^1.0.1" + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -8182,6 +8198,31 @@ "object-visit": "^1.0.0" } }, + "markdown-it": { + "version": "11.0.0", + "resolved": "https://r.cnpmjs.org/markdown-it/download/markdown-it-11.0.0.tgz", + "integrity": "sha1-2/wwNj5D11brxSw4WGuRuQBGuHY=", + "requires": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdown-it-katex": { + "version": "2.0.3", + "resolved": "https://r.cnpmjs.org/markdown-it-katex/download/markdown-it-katex-2.0.3.tgz", + "integrity": "sha1-17hqGuoLnWSW+rTnkZoY/e9YnDk=", + "requires": { + "katex": "^0.6.0" + } + }, + "match-at": { + "version": "0.1.1", + "resolved": "https://r.cnpmjs.org/match-at/download/match-at-0.1.1.tgz", + "integrity": "sha1-JdBA0pF3dwTV5lVru3kjDsLeBUA=" + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -8197,6 +8238,11 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://r.cnpmjs.org/mdurl/download/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -12396,6 +12442,11 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://r.cnpmjs.org/uc.micro/download/uc.micro-1.0.6.tgz", + "integrity": "sha1-nEEagCpAmpH8bPdAgbq6NLJEmaw=" + }, "uglify-js": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", diff --git a/package.json b/package.json index 8342d91..b663dad 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "fix-orientation": "^1.1.0", "gh-pages": "^3.0.0", "load-script": "^1.0.0", + "markdown-it": "^11.0.0", + "markdown-it-katex": "^2.0.3", "pressure": "^2.1.2", "react": "^16.9.0", "react-dom": "^16.9.0", diff --git a/src/Flows.js b/src/Flows.js index 35aa88d..98bef47 100644 --- a/src/Flows.js +++ b/src/Flows.js @@ -7,6 +7,7 @@ import './Flows.css'; import LazyLoad from './react-lazyload/src'; import {AudioWidget} from './AudioWidget'; import {TokenCtx, ReplyForm} from './UserAction'; +import renderMd from './Markdown' import {API, THUHOLE_API_ROOT} from './flows_api'; @@ -129,7 +130,7 @@ class FlowItem extends PureComponent { render() { let props=this.props; - let parts=props.parts||split_text(props.info.text,[ + let parts=props.parts||split_text(renderMd(props.info.text),[ ['url_pid',URL_PID_RE], ['url',URL_RE], ['pid',PID_RE], diff --git a/src/Markdown.js b/src/Markdown.js new file mode 100644 index 0000000..be96ad6 --- /dev/null +++ b/src/Markdown.js @@ -0,0 +1,8 @@ +import MarkdownIt from 'markdown-it' + +let md = new MarkdownIt({ + html: false, + linkify: false // avoid collision with text_splitter.js +}) + +export default (text) => md.render(text) \ No newline at end of file From b37dd8249839a8016cb7dba4999d5162d29a4acf Mon Sep 17 00:00:00 2001 From: Liu Jiangyi Date: Mon, 22 Jun 2020 12:01:08 +0800 Subject: [PATCH 2/6] fix: markdown --- package-lock.json | 308 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + src/Common.js | 84 ++++++++++++- src/Flows.js | 10 +- src/Markdown.js | 8 -- 5 files changed, 393 insertions(+), 19 deletions(-) delete mode 100644 src/Markdown.js diff --git a/package-lock.json b/package-lock.json index 4af2a5c..db92134 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2191,6 +2191,11 @@ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, + "bail": { + "version": "1.0.5", + "resolved": "https://r.cnpmjs.org/bail/download/bail-1.0.5.tgz", + "integrity": "sha1-tvoTNASjksvB+MS/Y/WVM1Hnp3Y=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2650,6 +2655,21 @@ "supports-color": "^5.3.0" } }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://r.cnpmjs.org/character-entities/download/character-entities-1.2.4.tgz", + "integrity": "sha1-4Sw5Obfq9OWxXnrUxeKOHUjFsWs=" + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://r.cnpmjs.org/character-entities-legacy/download/character-entities-legacy-1.1.4.tgz", + "integrity": "sha1-lLwYRdznClu50uzHSHJWYSk9j8E=" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://r.cnpmjs.org/character-reference-invalid/download/character-reference-invalid-1.1.4.tgz", + "integrity": "sha1-CDMpzaDq4nKrPbvzfpo4LBOvFWA=" + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -3321,6 +3341,11 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "collapse-white-space": { + "version": "1.0.6", + "resolved": "https://r.cnpmjs.org/collapse-white-space/download/collapse-white-space-1.0.6.tgz", + "integrity": "sha1-5jYpwAFmZXkgYNu+t5xCI50sUoc=" + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -6095,6 +6120,53 @@ } } }, + "html-to-react": { + "version": "1.4.3", + "resolved": "https://r.cnpmjs.org/html-to-react/download/html-to-react-1.4.3.tgz", + "integrity": "sha1-FDChy1ge8pUziS7HCi/cRVSxf/0=", + "requires": { + "domhandler": "^3.0", + "htmlparser2": "^4.1.0", + "lodash.camelcase": "^4.3.0", + "ramda": "^0.27" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://r.cnpmjs.org/domelementtype/download/domelementtype-2.0.1.tgz", + "integrity": "sha1-H4vf6R9aeAYydOgDtL3O326U+U0=" + }, + "domhandler": { + "version": "3.0.0", + "resolved": "https://r.cnpmjs.org/domhandler/download/domhandler-3.0.0.tgz", + "integrity": "sha1-Uc0T78ox2pW7sMW+46SDAOMzs+k=", + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.1.0", + "resolved": "https://r.cnpmjs.org/domutils/download/domutils-2.1.0.tgz", + "integrity": "sha1-et4yAa9DcD/eFUlS46ho60tjXxY=", + "requires": { + "dom-serializer": "^0.2.1", + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0" + } + }, + "htmlparser2": { + "version": "4.1.0", + "resolved": "https://r.cnpmjs.org/htmlparser2/download/htmlparser2-4.1.0.tgz", + "integrity": "sha1-mk7xYfLkYl6/ffvmwKL1LRilnng=", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + } + } + }, "html-webpack-plugin": { "version": "4.0.0-beta.5", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz", @@ -6402,6 +6474,20 @@ "kind-of": "^3.0.2" } }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/is-alphabetical/download/is-alphabetical-1.0.4.tgz", + "integrity": "sha1-nn1rlJFr4iFTdF0YTCmMv5hqaG0=" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/is-alphanumerical/download/is-alphanumerical-1.0.4.tgz", + "integrity": "sha1-frmiQx+FX2se8aeOMm31FWlsTb8=", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6459,6 +6545,11 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/is-decimal/download/is-decimal-1.0.4.tgz", + "integrity": "sha1-ZaOllYocW2OnBuGzM9fNn2MNP6U=" + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -6509,6 +6600,11 @@ "is-extglob": "^2.1.1" } }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/is-hexadecimal/download/is-hexadecimal-1.0.4.tgz", + "integrity": "sha1-zDXJdYjaS9Saju3WvECC1E3LI6c=" + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -6610,11 +6706,21 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/is-whitespace-character/download/is-whitespace-character-1.0.4.tgz", + "integrity": "sha1-CFjt2UqVWUx8ndC1wXTsbkXuSqc=" + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, + "is-word-character": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/is-word-character/download/is-word-character-1.0.4.tgz", + "integrity": "sha1-zg5zIW+YWZBgWS9i/zE1TdvrAjA=" + }, "is-wsl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", @@ -8080,6 +8186,11 @@ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://r.cnpmjs.org/lodash.camelcase/download/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -8198,6 +8309,11 @@ "object-visit": "^1.0.0" } }, + "markdown-escapes": { + "version": "1.0.4", + "resolved": "https://r.cnpmjs.org/markdown-escapes/download/markdown-escapes-1.0.4.tgz", + "integrity": "sha1-yVQV70UUmddgK5EJXzyOiXX3hTU=" + }, "markdown-it": { "version": "11.0.0", "resolved": "https://r.cnpmjs.org/markdown-it/download/markdown-it-11.0.0.tgz", @@ -8233,6 +8349,14 @@ "safe-buffer": "^5.1.2" } }, + "mdast-add-list-metadata": { + "version": "1.0.1", + "resolved": "https://r.cnpmjs.org/mdast-add-list-metadata/download/mdast-add-list-metadata-1.0.1.tgz", + "integrity": "sha1-lec2QM4vwfoty37EQ9CeK/59tM8=", + "requires": { + "unist-util-visit-parents": "1.1.2" + } + }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", @@ -9142,6 +9266,19 @@ "safe-buffer": "^5.1.1" } }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://r.cnpmjs.org/parse-entities/download/parse-entities-1.2.2.tgz", + "integrity": "sha1-wxvw9lO2ZhNU+Jc1WcuG3R1e31A=", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -10383,6 +10520,11 @@ "performance-now": "^2.1.0" } }, + "ramda": { + "version": "0.27.0", + "resolved": "https://r.cnpmjs.org/ramda/download/ramda-0.27.0.tgz", + "integrity": "sha1-kV3CmGXAgAvz9puP1sJ5iYtZ3kM=" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -10598,6 +10740,33 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" }, + "react-markdown": { + "version": "4.3.1", + "resolved": "https://r.cnpmjs.org/react-markdown/download/react-markdown-4.3.1.tgz", + "integrity": "sha1-OfBjO5SgJ0RbhsmBEULQU4EwDy8=", + "requires": { + "html-to-react": "^1.3.4", + "mdast-add-list-metadata": "1.0.1", + "prop-types": "^15.7.2", + "react-is": "^16.8.6", + "remark-parse": "^5.0.0", + "unified": "^6.1.5", + "unist-util-visit": "^1.3.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "prop-types": { + "version": "15.7.2", + "resolved": "https://r.cnpmjs.org/prop-types/download/prop-types-15.7.2.tgz", + "integrity": "sha1-UsQedbjIfnK52TYOAga5ncv/psU=", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + } + } + }, "react-scripts": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.1.1.tgz", @@ -10831,6 +11000,28 @@ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" }, + "remark-parse": { + "version": "5.0.0", + "resolved": "https://r.cnpmjs.org/remark-parse/download/remark-parse-5.0.0.tgz", + "integrity": "sha1-TAd/nkmQRNHVwT+A16mM97koXZU=", + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -10893,6 +11084,11 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://r.cnpmjs.org/replace-ext/download/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -11827,6 +12023,11 @@ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==" }, + "state-toggle": { + "version": "1.0.3", + "resolved": "https://r.cnpmjs.org/state-toggle/download/state-toggle-1.0.3.tgz", + "integrity": "sha1-4SOxaojhQxObCcaFIiG8mBWRff4=" + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -12361,6 +12562,11 @@ "punycode": "^2.1.0" } }, + "trim": { + "version": "0.0.1", + "resolved": "https://r.cnpmjs.org/trim/download/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + }, "trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npm.taobao.org/trim-repeated/download/trim-repeated-1.0.0.tgz", @@ -12374,6 +12580,16 @@ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, + "trim-trailing-lines": { + "version": "1.1.3", + "resolved": "https://r.cnpmjs.org/trim-trailing-lines/download/trim-trailing-lines-1.1.3.tgz", + "integrity": "sha1-fwc5iB/3Zle3d24Qh0EoAEtiWpQ=" + }, + "trough": { + "version": "1.0.5", + "resolved": "https://r.cnpmjs.org/trough/download/trough-1.0.5.tgz", + "integrity": "sha1-uLY5zvrX0LsqvTfUM/+Ck++l9AY=" + }, "ts-pnp": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.2.tgz", @@ -12468,6 +12684,15 @@ } } }, + "unherit": { + "version": "1.1.3", + "resolved": "https://r.cnpmjs.org/unherit/download/unherit-1.1.3.tgz", + "integrity": "sha1-bJtQPytBsmIzDIDpHIYUq9qmnCI=", + "requires": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -12492,6 +12717,19 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==" }, + "unified": { + "version": "6.2.0", + "resolved": "https://r.cnpmjs.org/unified/download/unified-6.2.0.tgz", + "integrity": "sha1-f71jD3GRJtZ9QMZEt+P2FwNfbbo=", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^2.0.0", + "x-is-string": "^0.1.0" + } + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -12529,6 +12767,47 @@ "imurmurhash": "^0.1.4" } }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://r.cnpmjs.org/unist-util-is/download/unist-util-is-3.0.0.tgz", + "integrity": "sha1-2ehDgcJGjoJinkpb6dfQWi3TJM0=" + }, + "unist-util-remove-position": { + "version": "1.1.4", + "resolved": "https://r.cnpmjs.org/unist-util-remove-position/download/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha1-7ANzSLYQLIl3A+7m0ClMpHVaICA=", + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://r.cnpmjs.org/unist-util-stringify-position/download/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha1-Pzf881EnncvKdICrWIm7ioMu4cY=" + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://r.cnpmjs.org/unist-util-visit/download/unist-util-visit-1.4.1.tgz", + "integrity": "sha1-RySqqEhububibX/zyGhZYNVgseM=", + "requires": { + "unist-util-visit-parents": "^2.0.0" + }, + "dependencies": { + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://r.cnpmjs.org/unist-util-visit-parents/download/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha1-JeQ+VTEhZvM0jK5nQ1iHgdESwek=", + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, + "unist-util-visit-parents": { + "version": "1.1.2", + "resolved": "https://r.cnpmjs.org/unist-util-visit-parents/download/unist-util-visit-parents-1.1.2.tgz", + "integrity": "sha1-9uOv7ovb+WHA5vAo6jwEgAKMPQY=" + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -12732,6 +13011,30 @@ "extsprintf": "^1.2.0" } }, + "vfile": { + "version": "2.3.0", + "resolved": "https://r.cnpmjs.org/vfile/download/vfile-2.3.0.tgz", + "integrity": "sha1-5i2OcrIOg8MkvGxnJ47ickiL+Eo=", + "requires": { + "is-buffer": "^1.1.4", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-location": { + "version": "2.0.6", + "resolved": "https://r.cnpmjs.org/vfile-location/download/vfile-location-2.0.6.tgz", + "integrity": "sha1-iidPOUEbhxnqVyiALhDZ4N/xUZ4=" + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://r.cnpmjs.org/vfile-message/download/vfile-message-1.1.1.tgz", + "integrity": "sha1-WDOuB4od+i2W6WR4hs0ymTqzE+E=", + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + }, "vm-browserify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", @@ -13368,6 +13671,11 @@ "async-limiter": "~1.0.0" } }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://r.cnpmjs.org/x-is-string/download/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index b663dad..6fc1cf8 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,14 @@ "copy-to-clipboard": "^3.0.8", "fix-orientation": "^1.1.0", "gh-pages": "^3.0.0", + "html-to-react": "^1.4.3", "load-script": "^1.0.0", "markdown-it": "^11.0.0", "markdown-it-katex": "^2.0.3", "pressure": "^2.1.2", "react": "^16.9.0", "react-dom": "^16.9.0", + "react-markdown": "^4.3.1", "react-scripts": "^3.1.1", "react-timeago": "^4.1.9" }, diff --git a/src/Common.js b/src/Common.js index 3a2d07a..5a35397 100644 --- a/src/Common.js +++ b/src/Common.js @@ -2,7 +2,14 @@ import React, {Component, PureComponent} from 'react'; import {format_time,Time,TitleLine} from './infrastructure/widgets'; import {THUHOLE_API_ROOT} from './flows_api'; +import HtmlToReact from 'html-to-react' + import './Common.css'; +import { URL_PID_RE, URL_RE, split_text } from './text_splitter'; +import ProcessNodeDefinitions from 'html-to-react/lib/process-node-definitions'; + +import ReactMarkdown from 'react-markdown' +import htmlParser from 'react-markdown/plugins/html-parser' export {format_time,Time,TitleLine}; @@ -26,11 +33,13 @@ export function ColoredSpan(props) { ) } + +function normalize_url(url) { + return /^https?:\/\//.test(url) ? url : 'http://'+url; +} + export class HighlightedText extends PureComponent { render() { - function normalize_url(url) { - return /^https?:\/\//.test(url) ? url : 'http://'+url; - } return (
                 {this.props.parts.map((part,idx)=>{
@@ -51,6 +60,75 @@ export class HighlightedText extends PureComponent {
     }
 }
 
+// props: text, show_pid, color_picker
+export class HighlightedMarkdown extends PureComponent {
+    render() {
+        const processDefs = new HtmlToReact.ProcessNodeDefinitions(React)
+        const astInst = [
+            {
+                shouldProcessNode (node) {
+                    return node.name === 'link' // urls
+                },
+                processNode (node) {
+                    const originalHref = node.attribs.href
+                    if (URL_PID_RE.test(originalHref)) { // url_pid
+                        return (
+                            /##
+                        )
+                    } else if (URL_RE.test(originalHref)) { // url
+                        return (
+                            
+                                {originalHref}
+                            
+                        )
+                    } else {// other cases are invalid, e.g. javascript:alert, etc
+                        return ()
+                    }
+                }
+            },
+            {
+                shouldProcessNode (node) {
+                    return node.name === 'text' // pid, nickname, search
+                },
+                processNode (node) {
+                    const originalText = node.data
+                    const splitted = split_text(originalText, [
+                        ['pid',PID_RE],
+                        ['nickname',NICKNAME_RE],
+                    ])
+
+                    return (
+                        <>
+                            {splitted.map(([rule, p], idx) => {
+                                
+                                    {rule==='pid' ? {e.preventDefault(); this.props.show_pid(p.substring(1));}}>{p} :
+                                    rule==='nickname' ? {p} :
+                                    rule==='search' ? {p} :
+                                    p}
+                                
+                            })}
+                        
+                    )
+                }
+            },
+            {
+                shouldProcessNode: () => true,
+                processNode: processDefs.processDefaultNode
+            }
+        ]
+        return (
+            
+        )
+    }
+}
+
 window.TEXTAREA_BACKUP={};
 
 export class SafeTextarea extends Component {
diff --git a/src/Flows.js b/src/Flows.js
index 98bef47..04b9e68 100644
--- a/src/Flows.js
+++ b/src/Flows.js
@@ -2,7 +2,7 @@ import React, {Component, PureComponent} from 'react';
 import copy from 'copy-to-clipboard';
 import {ColorPicker} from './color_picker';
 import {split_text, NICKNAME_RE, PID_RE, URL_RE, URL_PID_RE} from './text_splitter';
-import {format_time, build_highlight_re, Time, TitleLine, HighlightedText, ClickHandler, ColoredSpan} from './Common';
+import {format_time, build_highlight_re, Time, TitleLine, HighlightedText, ClickHandler, ColoredSpan, HighlightedMarkdown} from './Common';
 import './Flows.css';
 import LazyLoad from './react-lazyload/src';
 import {AudioWidget} from './AudioWidget';
@@ -130,12 +130,6 @@ class FlowItem extends PureComponent {
 
     render() {
         let props=this.props;
-        let parts=props.parts||split_text(renderMd(props.info.text),[
-            ['url_pid',URL_PID_RE],
-            ['url',URL_RE],
-            ['pid',PID_RE],
-            ['nickname',NICKNAME_RE],
-        ]);
         return (
             
{!!props.is_quote && @@ -176,7 +170,7 @@ class FlowItem extends PureComponent {
- + {props.info.type==='image' &&

{props.img_clickable ? diff --git a/src/Markdown.js b/src/Markdown.js deleted file mode 100644 index be96ad6..0000000 --- a/src/Markdown.js +++ /dev/null @@ -1,8 +0,0 @@ -import MarkdownIt from 'markdown-it' - -let md = new MarkdownIt({ - html: false, - linkify: false // avoid collision with text_splitter.js -}) - -export default (text) => md.render(text) \ No newline at end of file From e984dfeccbb92d338c6d568e682a3e87b268c51f Mon Sep 17 00:00:00 2001 From: Liu Jiangyi Date: Mon, 22 Jun 2020 17:52:52 +0800 Subject: [PATCH 3/6] fixed markdown, tested --- src/Common.js | 62 ++++++++++++++++--------------------------------- src/Flows.js | 10 +------- src/Markdown.js | 8 +++++++ 3 files changed, 29 insertions(+), 51 deletions(-) create mode 100644 src/Markdown.js diff --git a/src/Common.js b/src/Common.js index 5a35397..f00b742 100644 --- a/src/Common.js +++ b/src/Common.js @@ -5,11 +5,9 @@ import {THUHOLE_API_ROOT} from './flows_api'; import HtmlToReact from 'html-to-react' import './Common.css'; -import { URL_PID_RE, URL_RE, split_text } from './text_splitter'; -import ProcessNodeDefinitions from 'html-to-react/lib/process-node-definitions'; +import { URL_PID_RE, URL_RE, PID_RE, NICKNAME_RE, split_text } from './text_splitter'; -import ReactMarkdown from 'react-markdown' -import htmlParser from 'react-markdown/plugins/html-parser' +import renderMd from './Markdown' export {format_time,Time,TitleLine}; @@ -61,38 +59,20 @@ export class HighlightedText extends PureComponent { } // props: text, show_pid, color_picker -export class HighlightedMarkdown extends PureComponent { +export class HighlightedMarkdown extends Component { render() { + const props = this.props const processDefs = new HtmlToReact.ProcessNodeDefinitions(React) - const astInst = [ + const processInstructions = [ { shouldProcessNode (node) { - return node.name === 'link' // urls - }, - processNode (node) { - const originalHref = node.attribs.href - if (URL_PID_RE.test(originalHref)) { // url_pid - return ( - /## - ) - } else if (URL_RE.test(originalHref)) { // url - return ( - - {originalHref} - - ) - } else {// other cases are invalid, e.g. javascript:alert, etc - return () - } - } - }, - { - shouldProcessNode (node) { - return node.name === 'text' // pid, nickname, search + return node.type === 'text' // pid, nickname, search }, processNode (node) { const originalText = node.data const splitted = split_text(originalText, [ + ['url_pid', URL_PID_RE], + ['url',URL_RE], ['pid',PID_RE], ['nickname',NICKNAME_RE], ]) @@ -100,12 +80,15 @@ export class HighlightedMarkdown extends PureComponent { return ( <> {splitted.map(([rule, p], idx) => { - - {rule==='pid' ? {e.preventDefault(); this.props.show_pid(p.substring(1));}}>{p} : - rule==='nickname' ? {p} : + return ( + { + rule==='url_pid' ? /## : + rule==='url' ? {p} : + rule==='pid' ? {e.preventDefault(); props.show_pid(p.substring(1));}}>{p} : + rule==='nickname' ? {p} : rule==='search' ? {p} : p} - + ) })} ) @@ -116,16 +99,11 @@ export class HighlightedMarkdown extends PureComponent { processNode: processDefs.processDefaultNode } ] - return ( - - ) + const renderedMarkdown = renderMd(this.props.text) + const parser = new HtmlToReact.Parser() + console.log(`prerender:${renderedMarkdown}`) + + return parser.parseWithInstructions(renderedMarkdown, node => node.type !== 'script', processInstructions) } } diff --git a/src/Flows.js b/src/Flows.js index 04b9e68..39e5674 100644 --- a/src/Flows.js +++ b/src/Flows.js @@ -7,7 +7,6 @@ import './Flows.css'; import LazyLoad from './react-lazyload/src'; import {AudioWidget} from './AudioWidget'; import {TokenCtx, ReplyForm} from './UserAction'; -import renderMd from './Markdown' import {API, THUHOLE_API_ROOT} from './flows_api'; @@ -75,13 +74,6 @@ class Reply extends PureComponent { } render() { - let parts=split_text(this.props.info.text,[ - ['url_pid',URL_PID_RE], - ['url',URL_RE], - ['pid',PID_RE], - ['nickname',NICKNAME_RE], - ]); - return (

- +
); diff --git a/src/Markdown.js b/src/Markdown.js new file mode 100644 index 0000000..d803c66 --- /dev/null +++ b/src/Markdown.js @@ -0,0 +1,8 @@ +import MarkdownIt from 'markdown-it' + +let md = new MarkdownIt({ + html: false, + linkify: false +}) + +export default (text) => md.render(text) \ No newline at end of file From 01225a36951201596aaaf143290b04a7bbb1a0ae Mon Sep 17 00:00:00 2001 From: Liu Jiangyi Date: Mon, 22 Jun 2020 18:09:46 +0800 Subject: [PATCH 4/6] feat: KaTeX support --- package.json | 2 +- src/Common.js | 6 ++++++ src/Markdown.js | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6fc1cf8..c189b29 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "deploy": "gh-pages -d build -m $(TZ=Asia/Shanghai date +\"%y%m%d%H%M%S\")", "eject": "react-scripts eject" }, - "homepage": "https://://cdn.jsdelivr.net/gh/thuhole/webhole@gh-pages", + "homepage": "https://cdn.jsdelivr.net/gh/thuhole/webhole@gh-pages", "browserslist": { "production": [ ">0.2%", diff --git a/src/Common.js b/src/Common.js index f00b742..bf1e3ee 100644 --- a/src/Common.js +++ b/src/Common.js @@ -64,6 +64,12 @@ export class HighlightedMarkdown extends Component { const props = this.props const processDefs = new HtmlToReact.ProcessNodeDefinitions(React) const processInstructions = [ + { + shouldProcessNode: (node) => node.name === 'img', + processNode (node) { + return (
[图片]
) + } + }, { shouldProcessNode (node) { return node.type === 'text' // pid, nickname, search diff --git a/src/Markdown.js b/src/Markdown.js index d803c66..1fc6c87 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -1,8 +1,14 @@ import MarkdownIt from 'markdown-it' +import MarkdownItKaTeX from 'markdown-it-katex' + +import 'katex/dist/katex.min.css' let md = new MarkdownIt({ html: false, linkify: false +}).use(MarkdownItKaTeX, { + "throwOnError" : false, + "errorColor" : "#aa0000" }) export default (text) => md.render(text) \ No newline at end of file From 44a54bfb1b9887aabbda5adaeb51cbc6fe04b1da Mon Sep 17 00:00:00 2001 From: Liu Jiangyi Date: Mon, 22 Jun 2020 18:28:47 +0800 Subject: [PATCH 5/6] fix: heading display --- src/Common.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Common.js b/src/Common.js index bf1e3ee..f66e2a2 100644 --- a/src/Common.js +++ b/src/Common.js @@ -65,11 +65,22 @@ export class HighlightedMarkdown extends Component { const processDefs = new HtmlToReact.ProcessNodeDefinitions(React) const processInstructions = [ { - shouldProcessNode: (node) => node.name === 'img', + shouldProcessNode: (node) => node.name === 'img', // disable images processNode (node) { return (
[图片]
) } }, + { + shouldProcessNode: (node) => (/^h[123456]$/.test(node.name)), + processNode (node, children, index) { + let currentLevel = +(node.name[1]) + if (currentLevel < 3) currentLevel = 3; + const HeadingTag = `h${currentLevel}` + return ( + {children} + ) + } + }, { shouldProcessNode (node) { return node.type === 'text' // pid, nickname, search @@ -107,7 +118,6 @@ export class HighlightedMarkdown extends Component { ] const renderedMarkdown = renderMd(this.props.text) const parser = new HtmlToReact.Parser() - console.log(`prerender:${renderedMarkdown}`) return parser.parseWithInstructions(renderedMarkdown, node => node.type !== 'script', processInstructions) } From a19f1f2d2074b4e57c2fbd893f93b6974c0a5958 Mon Sep 17 00:00:00 2001 From: Liu Jiangyi Date: Mon, 22 Jun 2020 19:20:10 +0800 Subject: [PATCH 6/6] fix: line break --- src/Markdown.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Markdown.js b/src/Markdown.js index 1fc6c87..d689a03 100644 --- a/src/Markdown.js +++ b/src/Markdown.js @@ -5,7 +5,8 @@ import 'katex/dist/katex.min.css' let md = new MarkdownIt({ html: false, - linkify: false + linkify: false, + breaks: true }).use(MarkdownItKaTeX, { "throwOnError" : false, "errorColor" : "#aa0000"