Support math rendering on MFM (#3260)

This commit is contained in:
Aya Morisawa 2018-11-16 17:03:52 +09:00 committed by syuilo
parent a4670f9396
commit 97d4c04276
7 changed files with 75 additions and 0 deletions

View File

@ -46,6 +46,7 @@
"@types/is-root": "1.0.0", "@types/is-root": "1.0.0",
"@types/is-url": "1.2.28", "@types/is-url": "1.2.28",
"@types/js-yaml": "3.11.2", "@types/js-yaml": "3.11.2",
"@types/katex": "0.5.0",
"@types/koa": "2.0.46", "@types/koa": "2.0.46",
"@types/koa-bodyparser": "5.0.1", "@types/koa-bodyparser": "5.0.1",
"@types/koa-compress": "2.0.8", "@types/koa-compress": "2.0.8",
@ -140,6 +141,7 @@
"jsdom": "13.0.0", "jsdom": "13.0.0",
"json5": "2.1.0", "json5": "2.1.0",
"json5-loader": "1.0.1", "json5-loader": "1.0.1",
"katex": "0.10.0",
"koa": "2.6.1", "koa": "2.6.1",
"koa-bodyparser": "4.2.1", "koa-bodyparser": "4.2.1",
"koa-compress": "3.0.0", "koa-compress": "3.0.0",

View File

@ -0,0 +1,26 @@
<template>
<span v-html="compiledFormula"></span>
</template>
<script lang="ts">
import Vue from 'vue';
import * as katex from 'katex';
export default Vue.extend({
props: {
formula: {
type: String,
required: true
}
},
computed: {
compiledFormula(): any {
return katex.renderToString(this.formula);
}
}
});
</script>
<style>
@import "../../../../../../node_modules/katex/dist/katex.min.css";
</style>

View File

@ -1,5 +1,6 @@
import Vue, { VNode } from 'vue'; import Vue, { VNode } from 'vue';
import { length } from 'stringz'; import { length } from 'stringz';
import MkFormula from './formula.vue';
import parse from '../../../../../mfm/parse'; import parse from '../../../../../mfm/parse';
import getAcct from '../../../../../misc/acct/render'; import getAcct from '../../../../../misc/acct/render';
import MkUrl from './url.vue'; 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': { case 'search': {
return [createElement(MkGoogle, { return [createElement(MkGoogle, {
props: { props: {

View File

@ -53,6 +53,12 @@ const handlers: { [key: string]: (window: any, token: any, mentionedRemoteUsers:
document.body.appendChild(element); document.body.appendChild(element);
}, },
math({ document }, { formula }) {
const element = document.createElement('code');
element.textContent = formula;
document.body.appendChild(element);
},
link({ document }, { url, title }) { link({ document }, { url, title }) {
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;

View File

@ -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;
}

View File

@ -8,6 +8,7 @@ import { TextElementCode } from './elements/code';
import { TextElementEmoji } from './elements/emoji'; import { TextElementEmoji } from './elements/emoji';
import { TextElementHashtag } from './elements/hashtag'; import { TextElementHashtag } from './elements/hashtag';
import { TextElementInlineCode } from './elements/inline-code'; import { TextElementInlineCode } from './elements/inline-code';
import { TextElementMath } from './elements/math';
import { TextElementLink } from './elements/link'; import { TextElementLink } from './elements/link';
import { TextElementMention } from './elements/mention'; import { TextElementMention } from './elements/mention';
import { TextElementQuote } from './elements/quote'; import { TextElementQuote } from './elements/quote';
@ -29,6 +30,7 @@ const elements = [
require('./elements/hashtag'), require('./elements/hashtag'),
require('./elements/code'), require('./elements/code'),
require('./elements/inline-code'), require('./elements/inline-code'),
require('./elements/math'),
require('./elements/quote'), require('./elements/quote'),
require('./elements/emoji'), require('./elements/emoji'),
require('./elements/search'), require('./elements/search'),
@ -42,6 +44,7 @@ export type TextElement = { type: 'text', content: string }
| TextElementEmoji | TextElementEmoji
| TextElementHashtag | TextElementHashtag
| TextElementInlineCode | TextElementInlineCode
| TextElementMath
| TextElementLink | TextElementLink
| TextElementMention | TextElementMention
| TextElementQuote | TextElementQuote

View File

@ -210,6 +210,15 @@ describe('Text', () => {
assert.equal(tokens[0].content, '`var x = "Strawberry Pasta";`'); 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', () => { it('search', () => {
const tokens1 = analyze('a b c 検索'); const tokens1 = analyze('a b c 検索');
assert.deepEqual([ assert.deepEqual([