`, this.#shadow, {host: this});
// clang-format on
}
}
customElements.define('devtools-markdown-view', MarkdownView);
declare global {
interface HTMLElementTagNameMap {
'devtools-markdown-view': MarkdownView;
}
}
/**
* Default renderer is used for the IssuesPanel and allows only well-known images and links to be embedded.
*/
export class MarkdownLitRenderer {
#customClasses: Record> = {};
addCustomClasses(customClasses: Record): void {
for (const [type, className] of Object.entries(customClasses)) {
if (!this.#customClasses[type]) {
this.#customClasses[type] = new Set();
}
this.#customClasses[type].add(className);
}
}
removeCustomClasses(customClasses: Record): void {
for (const [type, className] of Object.entries(customClasses)) {
if (this.#customClasses[type]) {
this.#customClasses[type].delete(className);
}
}
}
protected customClassMapForToken(type: Marked.Marked.Token['type']): Lit.Directive.DirectiveResult {
const classNames = this.#customClasses[type] || new Set();
const classInfo = Object.fromEntries([...classNames].map(className => [className, true]));
return Lit.Directives.classMap(classInfo);
}
renderChildTokens(token: Marked.Marked.Token): Lit.LitTemplate[] {
if ('tokens' in token && token.tokens) {
return token.tokens.map(token => this.renderToken(token));
}
throw new Error('Tokens not found');
}
/**
* Unescape will get rid of the escaping done by Marked to avoid double escaping due to escaping it also with lit.
* Table taken from: front_end/third_party/marked/package/src/helpers.js
*/
unescape(text: string): string {
const escapeReplacements = new Map([
['&', '&'],
['<', '<'],
['>', '>'],
['"', '"'],
[''', '\''],
]);
return text.replace(/&(amp|lt|gt|quot|#39);/g, (matchedString: string) => {
const replacement = escapeReplacements.get(matchedString);
return replacement ? replacement : matchedString;
});
}
renderText(token: Marked.Marked.Token): Lit.TemplateResult {
if ('tokens' in token && token.tokens) {
return html`${this.renderChildTokens(token)}`;
}
// Due to unescaping, unescaped html entities (see escapeReplacements' keys) will be rendered
// as their corresponding symbol while the rest will be rendered as verbatim.
// Marked's escape function can be found in front_end/third_party/marked/package/src/helpers.js
return html`${this.unescape('text' in token ? token.text : '')}`;
}
renderHeading(heading: Marked.Marked.Tokens.Heading): Lit.TemplateResult {
const customClass = this.customClassMapForToken('heading');
switch (heading.depth) {
case 1:
return html`
${this.renderText(heading)}
`;
case 2:
return html`
${this.renderText(heading)}
`;
case 3:
return html`
${this.renderText(heading)}
`;
case 4:
return html`
${this.renderText(heading)}
`;
case 5:
return html`
${this.renderText(heading)}
`;
default:
return html`
${this.renderText(heading)}
`;
}
}
renderCodeBlock(token: Marked.Marked.Tokens.Code): Lit.TemplateResult {
// clang-format off
return html``;
// clang-format on
}
templateForToken(token: Marked.Marked.MarkedToken): Lit.LitTemplate|null {
switch (token.type) {
case 'paragraph':
return html`