/* 文件:markdown/callouts.ts — 支持 callout(提示框)语法的解析与渲染。 */
import { Tokens, MarkedExtension} from "marked";
import { Extension } from "./extension";
import AssetsManager from "src/assets";
import { wxWidget } from 'src/wechat/weixin-api';
const icon_note = ``
const icon_abstract = ``
const icon_info = ``
const icon_todo = ``
const icon_tip = ``
const icon_success = ``
const icon_question = ``
const icon_warning = ``
const icon_failure = ``
const icon_danger = ``
const icon_bug = ``
const icon_example = ``
const icon_quote = ``
/*
note,
abstract, summary, tldr
info
todo
tip
hint, important
success, check, done
question, help, faq
warning, caution, attention
failure, fail, missing
danger, error
bug
example
quote, cite
*/
type CalloutInfo = {icon: string, style: string}
const CalloutTypes = new Map(Object.entries({
note: {
icon: icon_note,
style: 'note-callout-note',
},
abstract: {
icon: icon_abstract,
style: 'note-callout-abstract',
},
summary: {
icon: icon_abstract,
style: 'note-callout-abstract',
},
tldr: {
icon: icon_abstract,
style: 'note-callout-abstract',
},
info: {
icon: icon_info,
style: 'note-callout-note',
},
todo: {
icon: icon_todo,
style: 'note-callout-note',
},
tip: {
icon: icon_tip,
style: 'note-callout-abstract',
},
hint: {
icon: icon_tip,
style: 'note-callout-abstract',
},
important: {
icon: icon_tip,
style: 'note-callout-abstract',
},
success: {
icon: icon_success,
style: 'note-callout-success',
},
check: {
icon: icon_success,
style: 'note-callout-success',
},
done: {
icon: icon_success,
style: 'note-callout-success',
},
question: {
icon: icon_question,
style: 'note-callout-question',
},
help: {
icon: icon_question,
style: 'note-callout-question',
},
faq: {
icon: icon_question,
style: 'note-callout-question',
},
warning: {
icon: icon_warning,
style: 'note-callout-question',
},
caution: {
icon: icon_warning,
style: 'note-callout-question',
},
attention: {
icon: icon_warning,
style: 'note-callout-question',
},
failure: {
icon: icon_failure,
style: 'note-callout-failure',
},
fail: {
icon: icon_failure,
style: 'note-callout-failure',
},
missing: {
icon: icon_failure,
style: 'note-callout-failure',
},
danger: {
icon: icon_danger,
style: 'note-callout-failure',
},
error: {
icon: icon_danger,
style: 'note-callout-failure',
},
bug: {
icon: icon_bug,
style: 'note-callout-failure',
},
example: {
icon: icon_example,
style: 'note-callout-example',
},
quote: {
icon: icon_quote,
style: 'note-callout-quote',
},
cite: {
icon: icon_quote,
style: 'note-callout-quote',
}
}));
function GetCallout(type: string) {
return CalloutTypes.get(type);
};
function matchCallouts(text:string) {
const regex = /\[\!(.*?)\]/g;
let m;
if( m = regex.exec(text)) {
return m[1];
}
return "";
}
function GetCalloutTitle(callout:string, text:string) {
let title = callout.charAt(0).toUpperCase() + callout.slice(1).toLowerCase();
let start = text.indexOf(']') + 1;
if (text.indexOf(']-') > 0 || text.indexOf(']+') > 0) {
start = start + 1;
}
let end = text.indexOf('\n');
if (end === -1) end = text.length;
if (start >= end) return title;
const customTitle = text.slice(start, end).trim();
if (customTitle !== '') {
title = customTitle;
}
return title;
}
export class CalloutRenderer extends Extension {
matched(text: string) {
return matchCallouts(text) != '';
}
async renderer(token: Tokens.Blockquote) {
let callout = matchCallouts(token.text);
if (callout == '') {
const body = this.marked.parser(token.tokens);
return `${body}
`;;
}
const title = GetCalloutTitle(callout, token.text);
const index = token.text.indexOf('\n');
let body = '';
if (index > 0) {
token.text = token.text.slice(index+1)
body = await this.marked.parse(token.text);
}
const setting = AssetsManager.getInstance().expertSettings.render?.callout as { [key: string]: any };
if (setting && callout.toLocaleLowerCase() in setting) {
const authkey = this.settings.authKey;
const widget = setting[callout.toLocaleLowerCase()];
if (typeof widget === 'number') {
return await wxWidget(authkey, JSON.stringify({
id: `${widget}`,
title,
content: body,
}));
}
if (typeof widget === 'object') {
const {id, style} = widget;
return await wxWidget(authkey, JSON.stringify({
id: `${id}`,
title,
style: style || {},
content: body,
}));
}
}
let info = GetCallout(callout.toLowerCase());
if (info == null) {
const svg = await this.assetsManager.loadIcon(callout);
if (svg) {
info = {icon: svg, style: 'note-callout-custom'}
}
else {
info = GetCallout('note');
}
}
return ``;
}
markedExtension(): MarkedExtension {
return {
async: true,
walkTokens: async (token: Tokens.Generic) => {
if (token.type !== 'blockquote') {
return;
}
token.html = await this.renderer(token as Tokens.Blockquote);
},
extensions:[{
name: 'blockquote',
level: 'block',
renderer: (token: Tokens.Generic)=> {
return token.html;
},
}]
}
}
}