import {LogCategory, logDebug, logTrace, logWarn} from "../log"; import { CodeToken, Env, FenceToken, HeadingOpenToken, ImageToken, LinkOpenToken, Options, ParagraphCloseToken, SubToken, SupToken, TextToken, Token } from "remarkable/lib"; import {tr} from "tc-shared/i18n/localize"; const { Remarkable } = require("remarkable"); export class MD2BBCodeRenderer { private static renderers: {[key: string]:(renderer: MD2BBCodeRenderer, token: Token) => string} = { "text": (renderer: MD2BBCodeRenderer, token: TextToken) => { renderer.currentLineCount += token.content.split("\n").length; return renderer.options().textProcessor(token.content); }, "softbreak": () => "\n", "hardbreak": () => "\n", "paragraph_open": () => "", "paragraph_close": (_, token: ParagraphCloseToken) => token.tight ? "" : "\n", "strong_open": () => "[b]", "strong_close": () => "[/b]", "em_open": () => "[i]", "em_close": () => "[/i]", "del_open": () => "[s]", "del_close": () => "[/s]", "sup": (renderer: MD2BBCodeRenderer, token: SupToken) => "[sup]" + renderer.options().textProcessor(token.content) + "[/sup]", "sub": (renderer: MD2BBCodeRenderer, token: SubToken) => "[sub]" + renderer.options().textProcessor(token.content) + "[/sub]", "bullet_list_open": () => "[ul]", "bullet_list_close": () => "[/ul]", "ordered_list_open": () => "[ol]", "ordered_list_close": () => "[/ol]", "list_item_open": () => "[li]", "list_item_close": () => "[/li]", "table_open": () => "[table]", "table_close": () => "[/table]", "thead_open": () => "", "thead_close": () => "", "tbody_open": () => "", "tbody_close": () => "", "tr_open": () => "[tr]", "tr_close": () => "[/tr]", "th_open": (renderer: MD2BBCodeRenderer, token: any) => "[th" + (token.align ? ("=" + token.align) : "") + "]", "th_close": () => "[/th]", "td_open": () => "[td]", "td_close": () => "[/td]", "link_open": (renderer: MD2BBCodeRenderer, token: LinkOpenToken) => "[url" + (token.href ? ("=" + token.href) : "") + "]", "link_close": () => "[/url]", "image": (renderer: MD2BBCodeRenderer, token: ImageToken) => { renderer.currentLineCount += 1; return "[img=" + (token.src) + "]" + (token.alt || token.src) + "[/img]"; }, //footnote_ref //"content": "==Marked text==", //mark_open //mark_close //++Inserted text++ "ins_open": () => "[u]", "ins_close": () => "[/u]", //[code]teaspeak.de[/code] "code": (renderer: MD2BBCodeRenderer, token: CodeToken) => "[i-code]" + token.content + "[/i-code]", "fence": (renderer: MD2BBCodeRenderer, token: FenceToken) => "[code" + (token.params ? ("=" + token.params) : "") + "]" + token.content + "[/code]", "heading_open": (renderer: MD2BBCodeRenderer, token: HeadingOpenToken) => "[size=" + (9 - Math.min(4, token.hLevel)) + "]", "heading_close": () => "[/size][hr]", "hr": () => "[hr]", //> Experience real-time editing with Remarkable! "blockquote_open": () => "[quote]", "blockquote_close": () => "[/quote]" }; private _options; currentLineCount: number; reset() { this._options = undefined; this.currentLineCount = 0; } render(tokens: Token[], options: Options, env: Env): string { this._options = options; let result = ''; for(let index = 0; index < tokens.length; index++) { if(tokens[index].lines?.length) { while(this.currentLineCount < tokens[index].lines[0]) { this.currentLineCount += 1; result += "\n"; } } if (tokens[index].type === 'inline') { /* we're just ignoring the inline fact */ result += this.render((tokens[index] as any).children, options, env); } else { result += this.renderToken(tokens[index]); } } this._options = undefined; return result; } private renderToken(token: Token) { logDebug(LogCategory.GENERAL, tr("Render Markdown token: %o"), token); const renderer = MD2BBCodeRenderer.renderers[token.type]; if(typeof(renderer) === "undefined") { logWarn(LogCategory.CHAT, tr("Missing markdown to bbcode renderer for token %s: %o"), token.type, token); return 'content' in token ? this.options().textProcessor(token.content) : ""; } return renderer(this, token); } options() : any { return this._options; } } const md2bbCodeRenderer = new MD2BBCodeRenderer(); const remarkableRenderer = new Remarkable("full", { typographer: true }); remarkableRenderer.renderer = md2bbCodeRenderer as any; remarkableRenderer.inline.ruler.disable([ 'newline', 'autolink' ]); export function renderMarkdownAsBBCode(message: string, textProcessor: (text: string) => string) : string { remarkableRenderer.set({ textProcessor: textProcessor } as any); md2bbCodeRenderer.reset(); let result = remarkableRenderer.render(message); logTrace(LogCategory.CHAT, tr("Markdown render result:\n%s"), result); return result; }