Files
note2any/src/markdown/widget-box.ts
2025-10-09 21:19:57 +08:00

159 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* 文件markdown/widget-box.ts — 小部件盒子widget解析与样式注入。 */
import { Tokens, MarkedExtension } from "marked";
import { Extension } from "./extension";
import { NMPSettings } from "src/settings";
import { uevent } from "src/utils";
import { wxWidget } from 'src/wechat/weixin-api';
const widgetCache = new Map<string, string>();
export function cleanWidgetCache() {
widgetCache.clear();
}
export class WidgetBox extends Extension {
mapToString(map: Map<string, string>): string {
if (map.size === 0) return "";
return Array.from(map.entries())
.map(([key, value]) => `${key}=${value}`)
.join("&"); // 用 "&" 连接键值对,可换成其他分隔符
}
calcKey(id: string, title: string, style: Map<string, string>, content: string) {
const styleStr = this.mapToString(style);
const key = `${id}-${title}-${styleStr}-${content}`;
return key;
}
cacheWidget(id: string, title: string, style: Map<string, string>, content: string, result: string) {
const key = this.calcKey(id, title, style, content);
widgetCache.set(key, result);
}
getWidget(id: string, title: string, style: Map<string, string>, content: string) {
const key = this.calcKey(id, title, style, content);
if (!widgetCache.has(key)) {
return null;
}
return widgetCache.get(key);
}
getBoxTitle(text: string) {
let start = text.indexOf(']') + 1;
let end = text.indexOf('\n');
if (end === -1) end = text.length;
if (start >= end) return '';
return text.slice(start, end).trim();
}
getBoxId(text: string) {
const regex = /\[#(.*?)\]/g;
let m;
if( m = regex.exec(text)) {
return m[1];
}
return "";
}
matched(text: string) {
return this.getBoxId(text) != "";
}
parseStyle(text: string) {
const style = text.split(':').map((s) => s.trim());
if (style.length != 2) return null;
const key = style[0];
const value = style[1];
return {key, value};
}
parseBox(text: string) {
const lines = text.split('\n');
let style = new Map<string, string>();
let content = [];
let isStyle = false;
for (let line of lines) {
if (line === '===') {
isStyle = !isStyle;
continue;
}
if (isStyle) {
const s = this.parseStyle(line);
if (s) style.set(s.key, s.value);
} else {
content.push(line);
}
}
const contentStr = content.join('\n');
return { style, contentStr };
}
async reqContent(id: string, title: string, style: Map<string, string>, content: string) {
const params = JSON.stringify({
id,
title,
style: Object.fromEntries(style),
content
});
return wxWidget(NMPSettings.getInstance().authKey, params)
}
processColor(style: Map<string, string>) {
const keys = style.keys();
for (let key of keys) {
if (key.includes('color')) {
const value = style.get(key);
if (!value) continue;
if (value.startsWith('rgb') || value.startsWith('#')) {
continue;
}
style.set(key, '#' + value);
}
}
}
async renderer(token: Tokens.Blockquote) {
let boxId = this.getBoxId(token.text);
if (boxId == '') {
const body = this.marked.parser(token.tokens);
return `<blockquote>${body}</blockquote>`;;
}
const title = this.getBoxTitle(token.text);
let style = new Map<string, string>();
let content = '';
const index = token.text.indexOf('\n');
if (index > 0) {
const pared = this.parseBox(token.text.slice(index + 1))
style = pared.style;
content = await this.marked.parse(pared.contentStr);
}
this.processColor(style);
const cached = this.getWidget(boxId, title, style, content);
if (cached) {
uevent('render-widgets-cached');
return cached;
}
else {
const reqContent = await this.reqContent(boxId, title, style, content);
this.cacheWidget(boxId, title, style, content, reqContent);
uevent('render-widgets');
return reqContent;
}
}
markedExtension(): MarkedExtension {
return {
extensions: [{
name: 'blockquote',
level: 'block',
renderer: (token: Tokens.Generic) => {
return token.html;
},
}]
}
}
}