diff --git a/diagram/frag_processor.drawio b/diagram/frag_processor.drawio index 284980a..b061410 100644 --- a/diagram/frag_processor.drawio +++ b/diagram/frag_processor.drawio @@ -1,33 +1,30 @@ - + - + - - - - + - + - + - + - + - + @@ -35,10 +32,10 @@ - + - + @@ -46,87 +43,73 @@ - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - + - - - - + + - - + + - - + + - - + + - - + + - - + + - + - - - + + + + + + - - + + - - + + - - + + diff --git a/diagram/frag_processor_2.3.5.drawio b/diagram/frag_processor_2.3.5.drawio new file mode 100644 index 0000000..284980a --- /dev/null +++ b/diagram/frag_processor_2.3.5.drawio @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/dev/msg_rendering_process.md b/docs/dev/msg_rendering_process.md index 95b2a15..d4cd6b9 100644 --- a/docs/dev/msg_rendering_process.md +++ b/docs/dev/msg_rendering_process.md @@ -1,55 +1,56 @@ - [Workflow](#workflow) - - [MsgProcessInfo](#msgprocessinfo) - - [FragmentProcessFunc](#fragmentprocessfunc) + - [Fragments](#fragments) + - [Fragment Processor](#fragment-processor) + - [Children Replacement](#children-replacement) - [Rendered Flag](#rendered-flag) +- [Global \& MessageRendered Callback](#global--messagerendered-callback) - [Refs](#refs) # Workflow -Currently the plugin does NOT rendering all content inside a message box. We only deal with contents that may need go through the markdown renderer. Also, since some element should NOT be considered as Markdown when rendering and should keep what it look like throughout the rendering, we introduced the *Replacer*. +Currently the plugin does NOT rendering all content inside a message box. We only deal with contents that may need go through the markdown renderer. Also, since some element should NOT be considered as Markdown when rendering and should keep what it look like throughout the rendering, we introduced the concept of **Fragement Processor**(FragProcessor). -![image](https://github.com/d0j1a1701/LiteLoaderQQNT-Markdown/assets/61616918/c5f986c4-5bb5-410f-8366-c1cffd6c2c3e) +## Fragments -As you see, we consider the `childern` of the message box as pieces. Then we have a list of `FragProcessor` which take in charge of convert the list of HTML children (or you can say: list of pieces) into list of `MsgProcessInfo` object. +As you see, we **consider the `childern` of the message box as a "fragment"**. Then we have **a list of `FragProcessor`, each takes in charge of render a certain type of fragments**. -## MsgProcessInfo +## Fragment Processor -This refers to object like: `{mark:..., replace:...}`: +Definition: -- `mark` Determine how it looks like for Markdown Renderer. -- `replace` Determine replace behaviour after markdown render process. - -This decide what this element looks like when passed to Markdown Renderer. For example, an element `123` should converted to: - -```js -{mark: '123', replace: undefined} -``` - -And an element that should be replaced later `` could be converted to: - -```js -{ - mark: '', - replace: (parent)=>{...} -} +```typescript +type FragmentProcessFunc = ( + parent: HTMLElement, + element: HTMLElement, + index: number, +) => FragmentProcessFuncRetType | undefined; ``` -## FragmentProcessFunc +When `render()` function is triggered, a provided list of Fragment Processor will be iterated from begin to end **respectively and preemptively**. -The work of generating `MsgProcessInfo` based on HTML pieces is done by a set of function with type `FragmentProcessFunc`. +This means, for a single fragment, once a processor successfully returned a not `undefined` value *(actually it should be `FragmentProcessFuncRetType` obejct)*, the loop is end and the return value is used for this fragment. -There is a list of `FragmentProcessFunc`. When iterating pieces, all `FragmentProcessFunc` inside the list will be **triggered from begin to end respectively and preemptively**, which means once a processor successfully returned a `MsgProcessInfo` object, process interrupt and program will continue with next piece. +![Fragment Processor](https://github.com/user-attachments/assets/670dd3f2-7660-4a59-99bb-9e985f19d323) ------ +## Children Replacement -![image](https://github.com/d0j1a1701/LiteLoaderQQNT-Markdown/assets/61616918/04247260-f2eb-46f3-aae2-1dfa487d8312) +Let's look into the return type of `FragmentProcessFunc`: +```typescript +interface FragmentProcessFuncRetType { + original: HTMLElement; + rendered: HTMLElement; +} +``` -After the process above, we will get a list of `MsgProcessInfo` object. What we need to do is: +As you see, it specified the `original` SPAN element, and a new `rendered` element. For now, we just replace the `original` child of `messageBox` with `rendered`. -- First, accumulate all `mark` from the list, passed it to *Markdown Renderer*, then get a `renderedHtml`. -- Iterating `replace` field of `MsgProcessInfo` list, if not `undefined`, execute the replace function. +> **Notice** +> +> Keep in mind that the `original` HTML element passed to Fragment Processor could be directly updated. +> +> In some cases, Fragment Processor function may directly mutated the `original` HTML element due to performance or other consideration. In this case, `original===rendered` should be true in the returned `FragmentProcessFuncRetType`. # Rendered Flag @@ -68,9 +69,22 @@ if (messageBox.classList.contains(markdownRenderedClassName)) { messageBox.classList.add(markdownRenderedClassName); ``` +# Global & MessageRendered Callback + +- For the task that need to be done directly after a message is rendered, put it at the end of `renderSingleMsgBox()` function. +- For the task that need to be done after a whole rendering is finished, put it at the end of `renderer()` function. + +> TODO: +> +> Add a more general callback protocol and manager for rendering process. + # Refs For more info about the process, check out source code file: - [msgpiece_processor.ts](/src/render/msgpiece_processor.ts) -- [renderer.tsx](/src/renderer.tsx) \ No newline at end of file +- [renderer.tsx](/src/renderer.tsx) + +For outdated doc: + +- [Message Rendering Process Doc 2.3.5](./msg_rendering_process_2.3.5.md) \ No newline at end of file diff --git a/docs/dev/msg_rendering_process_2.3.5.md b/docs/dev/msg_rendering_process_2.3.5.md new file mode 100644 index 0000000..95b2a15 --- /dev/null +++ b/docs/dev/msg_rendering_process_2.3.5.md @@ -0,0 +1,76 @@ +- [Workflow](#workflow) + - [MsgProcessInfo](#msgprocessinfo) + - [FragmentProcessFunc](#fragmentprocessfunc) +- [Rendered Flag](#rendered-flag) +- [Refs](#refs) + + +# Workflow + +Currently the plugin does NOT rendering all content inside a message box. We only deal with contents that may need go through the markdown renderer. Also, since some element should NOT be considered as Markdown when rendering and should keep what it look like throughout the rendering, we introduced the *Replacer*. + +![image](https://github.com/d0j1a1701/LiteLoaderQQNT-Markdown/assets/61616918/c5f986c4-5bb5-410f-8366-c1cffd6c2c3e) + +As you see, we consider the `childern` of the message box as pieces. Then we have a list of `FragProcessor` which take in charge of convert the list of HTML children (or you can say: list of pieces) into list of `MsgProcessInfo` object. + +## MsgProcessInfo + +This refers to object like: `{mark:..., replace:...}`: + +- `mark` Determine how it looks like for Markdown Renderer. +- `replace` Determine replace behaviour after markdown render process. + +This decide what this element looks like when passed to Markdown Renderer. For example, an element `123` should converted to: + +```js +{mark: '123', replace: undefined} +``` + +And an element that should be replaced later `` could be converted to: + +```js +{ + mark: '', + replace: (parent)=>{...} +} +``` + +## FragmentProcessFunc + +The work of generating `MsgProcessInfo` based on HTML pieces is done by a set of function with type `FragmentProcessFunc`. + +There is a list of `FragmentProcessFunc`. When iterating pieces, all `FragmentProcessFunc` inside the list will be **triggered from begin to end respectively and preemptively**, which means once a processor successfully returned a `MsgProcessInfo` object, process interrupt and program will continue with next piece. + +----- + +![image](https://github.com/d0j1a1701/LiteLoaderQQNT-Markdown/assets/61616918/04247260-f2eb-46f3-aae2-1dfa487d8312) + + +After the process above, we will get a list of `MsgProcessInfo` object. What we need to do is: + +- First, accumulate all `mark` from the list, passed it to *Markdown Renderer*, then get a `renderedHtml`. +- Iterating `replace` field of `MsgProcessInfo` list, if not `undefined`, execute the replace function. + +# Rendered Flag + +We use a special HTML `class` text to mark a message box as `rendered` in `renderSingleMsgBox()` function. When `render()` triggered, it first detect all message box inside UI, then filter the ones that already been rendered. This approach could not only improve the performance but also solve some async issue. + +![image](https://github.com/user-attachments/assets/dedce85a-b26e-419c-b740-a42984a06238) + +`renderSingleMsgBox()` function is an `async` function. And `render()` will not use `await` to call that function, means if we do nothing, two `renderSingleMsgBox()` could be called at the same time to process the same message box. To ensure only one `renderSingleMsgBox()` function called for each message box, we could put the Rendered detection at the begining of the function: + +```js +// skip rendered message +if (messageBox.classList.contains(markdownRenderedClassName)) { + return; +} +// mark current message as rendered +messageBox.classList.add(markdownRenderedClassName); +``` + +# Refs + +For more info about the process, check out source code file: + +- [msgpiece_processor.ts](/src/render/msgpiece_processor.ts) +- [renderer.tsx](/src/renderer.tsx) \ No newline at end of file diff --git a/docs/plug_install.md b/docs/plug_install.md index 1a69992..a92a658 100644 --- a/docs/plug_install.md +++ b/docs/plug_install.md @@ -1,11 +1,27 @@ # 安装本插件 -这里列出三种安装本插件的方法。 +在开始之前,请确保您已经按照 [LiteLoaderQQNT官方安装教程](https://liteloaderqqnt.github.io/guide/install.html) 成功安装并正常运行 LiteLoaderQQNT. +在成功安装 LiteLoaderQQNT 之后,您可以继续根据下方的教程安装本插件。 + +- 通过 插件列表查看插件 安装。 (推荐) - 通过 [PluginInstaller](https://github.com/xinyihl/LiteLoaderQQNT-PluginInstaller/tree/main) 安装。(推荐) - 通过下载 Release 中提供的ZIP压缩文件进行安装。 - 通过`git clone`直接克隆本项目。 +# 通过 插件列表查看插件 安装 + +> 正常情况下,推荐使用这种方式进行安装。 + +插件列表查看插件 本身就是一个 LiteLoaderQQNT 插件。首先根据插件官方文档的指引,安装 [插件列表查看](https://github.com/ltxhhz/LL-plugin-list-viewer/tree/main) 插件。 + +然后重启QQ,进入设置 > 插件列表查看,找到 `Markdown-it` 插件,点击安装。显示安装成功后,再次重启QQ即可。 + +![Plugin View Plug](https://github.com/user-attachments/assets/60b36c62-1899-4a88-b4c1-5cd4bb296968) + +> 上图为 插件列表中查看 中显示的本插件截图。 + + # 通过 PluginInstaller 安装 > 正常情况下,推荐使用这种方式进行安装。 diff --git a/manifest.json b/manifest.json index 0d17cec..ac0235b 100644 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "slug": "markdown_it", "icon": "./icon.png", "description": "为 QQNT 提供 Markdown 渲染", - "version": "2.3.5", + "version": "2.4.0", "authors": [ { "name": "d0j1a_1701", @@ -16,7 +16,7 @@ "repo": "d0j1a1701/LiteLoaderQQNT-Markdown", "branch": "v4", "release": { - "tag": "2.3.5", + "tag": "2.4.0", "file": "Release.zip" } }, diff --git a/src/render/msgpiece_processor.tsx b/src/render/msgpiece_processor.tsx index dcba3ea..3dc0f8e 100644 --- a/src/render/msgpiece_processor.tsx +++ b/src/render/msgpiece_processor.tsx @@ -81,7 +81,12 @@ function getMarkdownIns() { /** * Function type that used to process children elements inside QQNT message box. */ -type FragmentProcessFunc = (parent: HTMLElement, element: HTMLElement, index: number) => FragmentProcessFuncRetType | undefined; +type FragmentProcessFunc = ( + parent: HTMLElement, + element: HTMLElement, + index: number, +) => FragmentProcessFuncRetType | undefined; + interface FragmentProcessFuncRetType { original: HTMLElement;