Files
LiveSplitOne/src/util/Markdown.tsx
T
Christopher Serr 349d95ebb0 Context Menu, CSS Modules and React 19 (#1015)
This replaces the context menu with our own. By doing that we no longer
need to force install our npm packages, as there are no longer any
conflicts. In fact, as part of this change, I updated us to the latest
React 19 and even enabled the React compiler.

I also explored CSS modules for the first time, which are going to allow
us to reduce conflicts between CSS rules and possibly even allow us to
fully remove SASS, as the need for it is going to be reduced.

I also found out that the latest Rust nightly is broken due to their
update to LLVM 20. So I pinned the version to the last working nightly
version.
2025-02-20 21:40:55 +00:00

52 lines
1.8 KiB
TypeScript

import * as React from "react";
import markdownit from "markdown-it";
import { emoteList } from "../api/EmoteList";
const UNSAFE = markdownit({ html: true, breaks: false, linkify: true });
const SAFE = markdownit({ html: false, breaks: true, linkify: true });
const unsafeDefault = UNSAFE.renderer.rules.link_open || function (tokens, idx, options, _env, self) {
return self.renderToken(tokens, idx, options);
};
UNSAFE.renderer.rules.link_open = function (tokens, idx, options, env, self) {
tokens[idx].attrSet('target', '_blank');
return unsafeDefault(tokens, idx, options, env, self);
};
const safeDefault = SAFE.renderer.rules.link_open || function (tokens, idx, options, _env, self) {
return self.renderToken(tokens, idx, options);
};
SAFE.renderer.rules.link_open = function (tokens, idx, options, env, self) {
tokens[idx].attrSet('target', '_blank');
return safeDefault(tokens, idx, options, env, self);
};
function replaceTwitchEmotes(text: string): string {
return text.replace(/[A-Za-z0-9<):(\\;_>#/\]|]+/g, (matched) => {
const emoteId = emoteList[matched];
if (emoteId === undefined) {
return matched;
}
const url = `https://static-cdn.jtvnw.net/emoticons/v1/${emoteId}/1.0`;
return `![${matched}](${url})`;
});
}
export function replaceFlag(countryCode: string): React.JSX.Element {
const url = `https://www.speedrun.com/images/flags/${countryCode}.png`;
return <img className="flag" src={url} alt={countryCode} />;
}
export const Markdown = React.memo(renderMarkdown);
export function renderMarkdown({ markdown, unsafe }: { markdown: string,
unsafe?: boolean,
}): React.JSX.Element {
const markdownWithEmotes = replaceTwitchEmotes(markdown);
const html = (unsafe ? UNSAFE : SAFE).render(markdownWithEmotes);
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}