From 97d4c04276dc27186479e1d0f098e5297beea8bf Mon Sep 17 00:00:00 2001 From: Aya Morisawa Date: Fri, 16 Nov 2018 17:03:52 +0900 Subject: [PATCH] Support math rendering on MFM (#3260) --- package.json | 2 ++ .../app/common/views/components/formula.vue | 26 +++++++++++++++++++ .../components/misskey-flavored-markdown.ts | 9 +++++++ src/mfm/html.ts | 6 +++++ src/mfm/parse/elements/math.ts | 20 ++++++++++++++ src/mfm/parse/index.ts | 3 +++ test/mfm.ts | 9 +++++++ 7 files changed, 75 insertions(+) create mode 100644 src/client/app/common/views/components/formula.vue create mode 100644 src/mfm/parse/elements/math.ts diff --git a/package.json b/package.json index 1f4413054..2a862ee77 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@types/is-root": "1.0.0", "@types/is-url": "1.2.28", "@types/js-yaml": "3.11.2", + "@types/katex": "0.5.0", "@types/koa": "2.0.46", "@types/koa-bodyparser": "5.0.1", "@types/koa-compress": "2.0.8", @@ -140,6 +141,7 @@ "jsdom": "13.0.0", "json5": "2.1.0", "json5-loader": "1.0.1", + "katex": "0.10.0", "koa": "2.6.1", "koa-bodyparser": "4.2.1", "koa-compress": "3.0.0", diff --git a/src/client/app/common/views/components/formula.vue b/src/client/app/common/views/components/formula.vue new file mode 100644 index 000000000..930f16b47 --- /dev/null +++ b/src/client/app/common/views/components/formula.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/src/client/app/common/views/components/misskey-flavored-markdown.ts b/src/client/app/common/views/components/misskey-flavored-markdown.ts index a985a80ec..cbc938cf5 100644 --- a/src/client/app/common/views/components/misskey-flavored-markdown.ts +++ b/src/client/app/common/views/components/misskey-flavored-markdown.ts @@ -1,5 +1,6 @@ import Vue, { VNode } from 'vue'; import { length } from 'stringz'; +import MkFormula from './formula.vue'; import parse from '../../../../../mfm/parse'; import getAcct from '../../../../../misc/acct/render'; import MkUrl from './url.vue'; @@ -199,6 +200,14 @@ export default Vue.component('misskey-flavored-markdown', { })]; } + case 'math': { + return [createElement(MkFormula, { + props: { + formula: token.formula + } + })]; + } + case 'search': { return [createElement(MkGoogle, { props: { diff --git a/src/mfm/html.ts b/src/mfm/html.ts index df9959dc4..cb7c7e285 100644 --- a/src/mfm/html.ts +++ b/src/mfm/html.ts @@ -53,6 +53,12 @@ const handlers: { [key: string]: (window: any, token: any, mentionedRemoteUsers: document.body.appendChild(element); }, + math({ document }, { formula }) { + const element = document.createElement('code'); + element.textContent = formula; + document.body.appendChild(element); + }, + link({ document }, { url, title }) { const a = document.createElement('a'); a.href = url; diff --git a/src/mfm/parse/elements/math.ts b/src/mfm/parse/elements/math.ts new file mode 100644 index 000000000..b10efe515 --- /dev/null +++ b/src/mfm/parse/elements/math.ts @@ -0,0 +1,20 @@ +/** + * Math + */ + +export type TextElementMath = { + type: 'math'; + content: string; + formula: string; +}; + +export default function(text: string) { + const match = text.match(/^\$(.+?)\$/); + if (!match) return null; + const math = match[0]; + return { + type: 'math', + content: math, + formula: match[1] + } as TextElementMath; +} diff --git a/src/mfm/parse/index.ts b/src/mfm/parse/index.ts index f2beec14f..46e66ac4c 100644 --- a/src/mfm/parse/index.ts +++ b/src/mfm/parse/index.ts @@ -8,6 +8,7 @@ import { TextElementCode } from './elements/code'; import { TextElementEmoji } from './elements/emoji'; import { TextElementHashtag } from './elements/hashtag'; import { TextElementInlineCode } from './elements/inline-code'; +import { TextElementMath } from './elements/math'; import { TextElementLink } from './elements/link'; import { TextElementMention } from './elements/mention'; import { TextElementQuote } from './elements/quote'; @@ -29,6 +30,7 @@ const elements = [ require('./elements/hashtag'), require('./elements/code'), require('./elements/inline-code'), + require('./elements/math'), require('./elements/quote'), require('./elements/emoji'), require('./elements/search'), @@ -42,6 +44,7 @@ export type TextElement = { type: 'text', content: string } | TextElementEmoji | TextElementHashtag | TextElementInlineCode + | TextElementMath | TextElementLink | TextElementMention | TextElementQuote diff --git a/test/mfm.ts b/test/mfm.ts index 83f04075e..7bd81a2a6 100644 --- a/test/mfm.ts +++ b/test/mfm.ts @@ -210,6 +210,15 @@ describe('Text', () => { assert.equal(tokens[0].content, '`var x = "Strawberry Pasta";`'); }); + it('math', () => { + const fomula = 'x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}.'; + const text = `$${fomula}$`; + const tokens = analyze(text); + assert.deepEqual([ + { type: 'math', content: text, formula: fomula } + ], tokens); + }); + it('search', () => { const tokens1 = analyze('a b c 検索'); assert.deepEqual([