This repository has been archived by the owner on Jul 25, 2023. It is now read-only.
generated from Flysoft-Studio/QQNTim-Plugin-Template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f12e75b
commit 71b988c
Showing
70 changed files
with
455 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
{ | ||
"name": "qqntim-plugin-template", | ||
"name": "qqntim-plugin-markdown", | ||
"private": true, | ||
"version": "2.0.0", | ||
"version": "1.0.0", | ||
"packageManager": "[email protected]", | ||
"license": "MIT", | ||
"scripts": { | ||
|
@@ -17,15 +17,24 @@ | |
"format": "rome format . --write" | ||
}, | ||
"devDependencies": { | ||
"@flysoftbeta/qqntim-typings": "^3.0.0", | ||
"@flysoftbeta/qqntim-typings": "^3.1.2", | ||
"@types/katex": "^0.16.1", | ||
"@types/markdown-it": "^12.2.3", | ||
"@types/node": "^20.4.2", | ||
"@types/react": "^18.2.15", | ||
"@types/react-dom": "^18.2.7", | ||
"@types/sanitize-html": "^2.9.0", | ||
"@yarnpkg/sdks": "^3.0.0-rc.48", | ||
"esbuild": "^0.18.12", | ||
"fs-extra": "^11.1.1", | ||
"rome": "^12.1.3", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^5.1.6" | ||
}, | ||
"dependencies": { | ||
"html-entities": "^2.4.0", | ||
"katex": "^0.16.8", | ||
"markdown-it": "^13.0.1", | ||
"sanitize-html": "^2.11.0" | ||
} | ||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,57 @@ | ||
/* Put your stylesheet here. */ | ||
.message-content.markdown-enabled img { | ||
vertical-align: bottom; | ||
max-width: 100%; | ||
} | ||
|
||
.message-content.markdown-enabled img:not(.markdown-face) { | ||
width: 200px; | ||
} | ||
|
||
.message-content.markdown-enabled { | ||
font-size: initial !important; | ||
} | ||
|
||
.message-content.markdown-enabled h1, | ||
.message-content.markdown-enabled h2 { | ||
display: block !important; | ||
margin-inline-start: 0px !important; | ||
margin-inline-end: 0px !important; | ||
font-weight: bold !important; | ||
line-height: initial !important; | ||
} | ||
|
||
.message-content.markdown-enabled h1 { | ||
font-size: 2em !important; | ||
margin-block-start: 0.67em !important; | ||
margin-block-end: 0.67em !important; | ||
} | ||
|
||
.message-content.markdown-enabled h2 { | ||
font-size: 1.5em !important; | ||
margin-block-start: 0.83em !important; | ||
margin-block-end: 0.83em !important; | ||
} | ||
|
||
.message-content.markdown-enabled h3 { | ||
font-size: 1.17em !important; | ||
margin-block-start: 1em !important; | ||
margin-block-end: 1em !important; | ||
} | ||
|
||
.message-content.markdown-enabled h4 { | ||
font-size: initial !important; | ||
margin-block-start: 1.33em !important; | ||
margin-block-end: 1.33em !important; | ||
} | ||
|
||
.message-content.markdown-enabled h5 { | ||
font-size: 0.83em !important; | ||
margin-block-start: 1.67em !important; | ||
margin-block-end: 1.67em !important; | ||
} | ||
|
||
.message-content.markdown-enabled h6 { | ||
font-size: 0.67em !important; | ||
margin-block-start: 2.33em !important; | ||
margin-block-end: 2.33em !important; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,103 @@ | ||
import { getPluginConfig } from "./config"; | ||
import * as qqntim from "qqntim/renderer"; | ||
import MarkdownIt from "markdown-it"; | ||
import sanitizeHtml from "sanitize-html"; | ||
import { PluginConfig, getPluginConfig } from "./config"; | ||
import { env, utils } from "qqntim/renderer"; | ||
import { s } from "./utils/sep"; | ||
import { decode } from "html-entities"; | ||
import katex, { KatexOptions } from "katex"; | ||
import { randomUUID } from "crypto"; | ||
|
||
export default class Entry implements QQNTim.Entry.Renderer { | ||
private md = new MarkdownIt({ html: true }); | ||
private config: PluginConfig; | ||
constructor() { | ||
const config = getPluginConfig(qqntim.env.config.plugins.config); | ||
console.log("[Template] Hello world!", qqntim); | ||
console.log("[Template] 当前插件配置:", config); | ||
this.config = getPluginConfig(env.config.plugins.config); | ||
} | ||
private renderMarkdown(text: string) { | ||
text = text.trim(); | ||
|
||
// 搜索 TeX 标签 | ||
const expressions = new Map<string, [string, KatexOptions]>(); | ||
for (const tag of ["$$", "$"]) { | ||
let texStart = 0; | ||
do { | ||
texStart = text.indexOf(tag, texStart); | ||
const texEnd = text.indexOf(tag, texStart + tag.length); | ||
if (texEnd == -1) break; | ||
const expression = decode(text.substring(texStart + tag.length, texEnd), { level: "html5" }); | ||
const id = randomUUID(); | ||
expressions.set(id, [expression, { throwOnError: false, displayMode: tag == "$$" }]); | ||
|
||
text = `${text.slice(0, texStart)}TEX-${id}${text.slice(texEnd + tag.length)}`; | ||
texStart = texEnd + tag.length; | ||
} while (texStart != -1); | ||
} | ||
|
||
const rawHtml = this.md.render(text); | ||
|
||
// 过滤不安全的 HTML 标签 | ||
let safeHtml = sanitizeHtml(rawHtml, { | ||
allowedSchemes: ["appimg"], | ||
allowedTags: [...sanitizeHtml.defaults.allowedTags, "img"], | ||
allowedAttributes: { | ||
...sanitizeHtml.defaults.allowedAttributes, | ||
img: [...sanitizeHtml.defaults.allowedAttributes.img, "draggable"], | ||
}, | ||
allowedClasses: { | ||
img: ["markdown-face"], | ||
}, | ||
allowVulnerableTags: false, | ||
}); | ||
|
||
// 渲染并应用 TeX 标签 | ||
for (const [id, [expression, options]] of expressions) { | ||
safeHtml = safeHtml.replace(`TEX-${id}`, katex.renderToString(expression, options)); | ||
} | ||
return safeHtml; | ||
} | ||
onWindowLoaded(): void { | ||
// 添加 KaTeX 相关样式 | ||
const style = document.createElement("link"); | ||
style.href = encodeURI(`file://${__dirname.replaceAll(s, "/")}/katex.min.css`); | ||
style.type = "text/css"; | ||
style.rel = "stylesheet"; | ||
document.head.appendChild(style); | ||
|
||
// 获取表情资源路径 | ||
utils | ||
.ntCall("ns-ntApi", "nodeIKernelMsgService/getEmojiResourcePath", [{ type: 1 }]) | ||
.then((args: any) => args.resourcePath.replaceAll(s, "/") as string) | ||
.then((emojiResDir) => { | ||
new MutationObserver(() => { | ||
const elements = document.querySelectorAll<HTMLElement>(".message-content:not(.markdown-patched)"); | ||
for (const element of elements) { | ||
element.classList.add("markdown-patched"); | ||
const msg = element.__VUE__?.[0]?.props?.msgRecord; | ||
if (msg) { | ||
const msgElements = msg?.elements; | ||
|
||
// 判断消息是否需要渲染 | ||
const flags = this.config.markdownFlags.split(","); | ||
let isMarkdown = this.config.renderEverything; | ||
if (!isMarkdown) for (const msgElement of msgElements) if (msgElement) for (const flag of flags) if (msgElement?.textElement?.content?.includes(flag)) isMarkdown = true; | ||
if (!isMarkdown) continue; | ||
element.classList.add("markdown-enabled"); | ||
|
||
// 将消息中携带的图片和表情全部转换为 HTML 标签 | ||
let text = ""; | ||
for (const msgElement of msgElements) | ||
if (msgElement) | ||
if (msgElement?.textElement) { | ||
let content = msgElement.textElement.content as string; | ||
for (const flag of flags) content = content.replaceAll(flag, ""); | ||
text += content; | ||
} else if (msgElement?.picElement) text += `<img src="${encodeURI(`appimg://${msgElement.picElement.sourcePath}`)}" alt="图片" draggable="true">`; | ||
else if (msgElement?.faceElement) text += `<img class="markdown-face" src="appimg://${emojiResDir}/gif/s${msgElement.faceElement.faceIndex}.gif" alt="${msgElement.faceElement.faceText || "表情"}" width="24px" draggable="false">`; | ||
|
||
element.innerHTML = this.renderMarkdown(text); | ||
} | ||
} | ||
}).observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true }); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { sep as s } from "path"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.