diff --git a/README.md b/README.md
index 518fae9..5af7186 100644
--- a/README.md
+++ b/README.md
@@ -107,3 +107,7 @@ Normal test with HTML Entities & " ' < > .
## 注意事项
- 如果在使用插件时遇到问题,您可以通过 [发起 Issue](https://github.com/d0j1a1701/LiteLoaderQQNT-Markdown/issues/new) 向我们进行反馈。届时请尽可能附上诸如系统版本,插件列表, LiteLoaderQQNT 设置页版本信息截图等可以帮助分析问题的信息。如果你还安装了远程调试插件,可以再附上 Devtools 信息。
+
+## Contributing
+
+如果您想要为本项目贡献代码,或者想了解本项目的代码相关细节,欢迎阅读 [项目开发文档](/docs/dev/renderer.md) 。
\ No newline at end of file
diff --git a/diagram/.gitignore b/diagram/.gitignore
new file mode 100644
index 0000000..1fd4817
--- /dev/null
+++ b/diagram/.gitignore
@@ -0,0 +1 @@
+*.bkp
\ No newline at end of file
diff --git a/diagram/frag_processor.drawio b/diagram/frag_processor.drawio
new file mode 100644
index 0000000..284980a
--- /dev/null
+++ b/diagram/frag_processor.drawio
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/dev/msg_rendering_process.md b/docs/dev/msg_rendering_process.md
new file mode 100644
index 0000000..948a74c
--- /dev/null
+++ b/docs/dev/msg_rendering_process.md
@@ -0,0 +1,58 @@
+- [Workflow](#workflow)
+ - [MsgProcessInfo](#msgprocessinfo)
+ - [FragmentProcessFunc](#fragmentprocessfunc)
+- [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.
+
+# Refs
+
+For more info about the process, check out source code file:
+
+- [msgpiece_processor.ts](/src/render/msgpiece_processor.ts)
+- [renderer.jsx](/src/renderer.jsx)
\ No newline at end of file
diff --git a/docs/dev/renderer.md b/docs/dev/renderer.md
index aa0d8c6..e8c466a 100644
--- a/docs/dev/renderer.md
+++ b/docs/dev/renderer.md
@@ -1,13 +1,14 @@
![React](https://img.shields.io/badge/React-blue?style=for-the-badge&logo=react&logoColor=white)
![JavaScript](https://img.shields.io/badge/JavaScript-yellow?style=for-the-badge&logo=javascript&logoColor=white)
![Typescript](https://img.shields.io/badge/TypeScript-blue?style=for-the-badge&logo=typescript&logoColor=white)
-![Webpack](https://img.shields.io/badge/Webpack-grey?style=for-the-badge&logo=react&logoColor=white)
+![Webpack](https://img.shields.io/badge/Webpack-grey?style=for-the-badge&logo=webpack&logoColor=white)
- [Introduction](#introduction)
- [Start Developing](#start-developing)
- [Logging](#logging)
- [Create Release Version](#create-release-version)
- [UI Development](#ui-development)
+- [Read More](#read-more)
- [Content Rendering Test Example](#content-rendering-test-example)
@@ -78,6 +79,12 @@ Currently only `.js` file extension is supported, using `.jsx` may cause webpack
> This might be solved by provide custom package resolve rules to webpack but I didn't look into it.
+# Read More
+
+[Message Rendering Process](./msg_rendering_process.md)
+
+[HTML Entities Processing](./msg_rendering_process.md)
+
# Content Rendering Test Example
You could use Markdown below as a quick Markdown Rendering Test.
diff --git a/docs/known_issue.md b/docs/known_issue.md
new file mode 100644
index 0000000..5b2cfb7
--- /dev/null
+++ b/docs/known_issue.md
@@ -0,0 +1,29 @@
+This document is used to record some Known Issue of this plugin.
+
+# Compatability Issue With Other Plugin
+
+Currently we found that sometimes it would cause incorrect rendering behaviour when using this plugin with `LiteTools`(another QQNT plugin). And user may experience:
+
+- Incorrect LiteTools Slot rendering position. For example, the *Message Send Time* and *Withdrawed* slot may be rendered with incorrect position / alignment.
+
+Currently the workaround for the LiteTool Time slot is to use JavaScript to observe the `offsetHeight` of each message box. If it over a specific value, we consider this message a multiple-line message and set the `flex-direction: column` to the root message box, otherwise set `flex-direction: row`.
+
+This require change message box div into `flex` which may cause other rendering issue, and this workaround should be replaced by a better solution.
+
+# Post Rendering Function Exec Order Issue
+
+Currently, we call `renderSingleMsgBox()` function inside `render()`, then after we call some post-rendering functions, the whole process is like:
+
+```js
+msgBoxs.forEach((box){
+ renderSingleMsgBox(box); // actually async
+})
+
+// these function may run first before every msgbox finish rendering
+changeDirectionToColumnWhenLargerHeight();
+elementDebugLogger();
+```
+
+However the issue is the *MarkdownIt* render function returns Promise, so the `renderSingleMsgBox()` is an async function. And so, the post-rendering function below actually may run first while the message is not fully rendererd.
+
+Currently we do NOT implement any workaround on this since the test show this issue don't affect the rendering result for now. However this is something that should be fixed in the future.
\ No newline at end of file
diff --git a/src/render/msgpiece_processor.ts b/src/render/msgpiece_processor.ts
new file mode 100644
index 0000000..049be53
--- /dev/null
+++ b/src/render/msgpiece_processor.ts
@@ -0,0 +1,124 @@
+import { useSettingsStore } from '@/states/settings';
+import { escapeHtml, purifyHtml, unescapeHtml } from '@/utils/htmlProc';
+import { mditLogger } from '@/utils/logger';
+
+type ReplaceFunc = (parentElement: HTMLElement, id: string) => any;
+
+const TEXT_ELEMENT_MATCHER = 'text-element';
+const IMG_ELEMENT_MATCHER = 'pic-element';
+
+/**
+ * Data type used by renderer to determine how to render and replace an element.
+ */
+export interface MsgProcessInfo {
+ mark: string;
+ replace?: ReplaceFunc;
+ id?: string;
+}
+
+/**
+ * Function type that used to process children elements inside QQNT message box.
+ */
+type FragmentProcessFunc = (element: HTMLElement, index: number) => MsgProcessInfo | undefined;
+
+function textElementProcessor(element: Element): MsgProcessInfo | undefined {
+ // return undefine if type not match
+ if (!(element.tagName == 'SPAN') || !element.classList.contains(TEXT_ELEMENT_MATCHER) || element.querySelector('.text-element--at')) {
+ return undefined;
+ }
+
+ mditLogger('debug', 'ElementMatch', 'Source', element);
+ mditLogger('debug', 'Element', 'Match', 'spanTextProcessor');
+
+ // text processor
+ let settings = useSettingsStore.getState();
+ function entityProcesor(x: string) {
+ if (settings.unescapeAllHtmlEntites == true) {
+ return unescapeHtml(x);
+ }
+ if (settings.unescapeGtInText == true) {
+ return x.replaceAll('>', '>');
+ }
+ return x;
+ }
+
+ return {
+ mark: Array.from(element.getElementsByTagName("span"))
+ .map((element) => element.innerHTML)
+ .reduce((acc, x) => acc + entityProcesor(x), ''),
+ };
+}
+
+/**
+ * This fucked up everything.
+ */
+const picElementProcessor = replaceFuncGenerator({ filter: (e) => e.classList.contains(IMG_ELEMENT_MATCHER), placeholder: (id) => (` `) });
+
+const spanReplaceProcessor = replaceFuncGenerator({ filter: (e) => (e.tagName == 'SPAN') });
+
+
+interface replaceFuncGeneratorProps {
+ /**
+ * The generated processort with only deal with elements which this filter returns `true`.
+ */
+ filter: (element: HTMLElement) => boolean,
+ /**
+ * Custom function to generate placeholder text based on id.
+ */
+ placeholder?: (id: string) => string,
+ /**
+ * Custom replace function. Use defaul one if `undefined`.
+ */
+ replace?: (parent: HTMLElement, id: string) => any,
+}
+
+function replaceFuncGenerator(
+ props: replaceFuncGeneratorProps,
+): FragmentProcessFunc {
+ let {
+ filter,
+ placeholder,
+ } = props;
+ placeholder ??= (id) => (``);
+
+ return function (element: HTMLElement, index: number) {
+ if (!filter(element)) {
+ return undefined;
+ }
+
+ let id = `placeholder-${index}`;
+ let replace = props.replace;
+
+ replace ??= (parent: HTMLElement, id: string) => {
+ try {
+ // here oldNode may be `undefined` or `null`.
+ // Plugin will broke without this try catch block.
+ mditLogger('debug', 'Try replace oldNode with element:', element);
+ mditLogger('debug', 'Search placeholder with id', id);
+
+ const oldNode = parent.querySelector(`#${id}`);
+ mditLogger('debug', 'Old node found', oldNode);
+
+ oldNode.replaceWith(element);
+ mditLogger('debug', 'Replace success:', element);
+ } catch (e) {
+ mditLogger('error', 'Replace failed on element:', element, e);
+ }
+ };
+
+ return {
+ mark: placeholder(id),
+ id: id,
+ replace: replace,
+ }
+ }
+}
+
+/**
+ * Triggered from begin to end, preemptive.
+ */
+export const processorList: FragmentProcessFunc[] = [
+ picElementProcessor,
+ textElementProcessor,
+ spanReplaceProcessor,
+];
\ No newline at end of file
diff --git a/src/renderer.jsx b/src/renderer.jsx
index ddb7fe7..82f7406 100644
--- a/src/renderer.jsx
+++ b/src/renderer.jsx
@@ -1,5 +1,5 @@
// 运行在 Electron 渲染进程 下的页面脚本
-var { createRoot } = require("react-dom/client");
+const { createRoot } = require("react-dom/client");
import React from 'react';
import { renderToString } from 'react-dom/server';
@@ -26,12 +26,13 @@ import { useSettingsStore } from '@/states/settings';
// Utils
import { debounce } from 'throttle-debounce';
import { mditLogger, elementDebugLogger } from './utils/logger';
-
+import { processorList } from '@/render/msgpiece_processor';
const markdownRenderedClassName = 'markdown-rendered';
-const plugin_path = LiteLoader.plugins["markdown_it"].path.plugin;
+const markdownIgnoredPieceClassName = 'mdit-ignored';
+let markdownItIns = undefined;
-var markdownItIns = undefined;
+onLoad();
function generateMarkdownIns() {
const settings = useSettingsStore.getState();
@@ -39,7 +40,7 @@ function generateMarkdownIns() {
return markdownItIns;
}
mditLogger('info', 'Generating new markdown-it renderer...');
- var localMarkdownItIns = markdownIt({
+ let localMarkdownItIns = markdownIt({
html: true, // 在源码中启用 HTML 标签
xhtmlOut: true, // 使用 '/' 来闭合单标签 (比如
)。
// 这个选项只对完全的 CommonMark 模式兼容。
@@ -71,14 +72,15 @@ function generateMarkdownIns() {
const debouncedRender = debounce(
- 20,
+ 50,
render,
- { atBegin: true },
+ { atBegin: false },
);
// 将所有 span 合并
// 并使用 markdown-it 渲染
function render() {
+ // return;
mditLogger('debug', 'renderer() triggered');
const settings = useSettingsStore.getState();
@@ -87,16 +89,49 @@ function render() {
".message-content"
);
- function entityProcesor(x) {
- if (settings.unescapeAllHtmlEntites == true) {
- return unescapeHtml(x);
- }
- if (settings.unescapeGtInText == true) {
- return x.replaceAll('>', '>');
+ let newlyFoundMsgList = Array.from(elements)
+ // 跳过已渲染的消息
+ .filter((messageBox) => (!messageBox.classList.contains(markdownRenderedClassName)))
+ // 跳过空消息
+ .filter((messageBox) => messageBox.childNodes.length > 0);
+
+ mditLogger('debug', 'Newly found message count:', newlyFoundMsgList.length);
+
+ for (let msgBox of newlyFoundMsgList) {
+ try {
+ renderSingleMsgBox(msgBox);
+ } catch (e) {
+ mditLogger('debug', 'Render msgbox failed', e);
}
- return x;
}
+ // code that runs after renderer work finished.
+ changeDirectionToColumnWhenLargerHeight();
+ elementDebugLogger();
+}
+
+/**
+ * Markdown body process function used in render() to add openExternal()
+ * behaviour to all links inside rendered markdownBody.
+ */
+function handleExternalLink(markdownBody) {
+ markdownBody.querySelectorAll("a").forEach((e) => {
+ e.classList.add("markdown_it_link");
+ e.classList.add("text-link");
+ e.onclick = async (event) => {
+ event.preventDefault();
+ const href = event
+ .composedPath()[0]
+ .href.replace("app://./renderer/", "");
+ await LiteLoader.api.openExternal(href);
+ return false;
+ };
+ });
+}
+
+async function renderSingleMsgBox(messageBox) {
+ const settings = useSettingsStore.getState();
+
function renderedHtmlProcessor(x) {
if ((settings.forceEnableHtmlPurify() ?? settings.enableHtmlPurify) == true) {
mditLogger('debug', `Purified ${x}`);
@@ -105,123 +140,93 @@ function render() {
return x;
}
- var newlyFoundMsgList = Array.from(elements)
- // 跳过已渲染的消息
- .filter((messageBox) => (!messageBox.classList.contains(markdownRenderedClassName)))
- // 跳过空消息
- .filter((messageBox) => messageBox.childNodes.length > 0);
+ if (messageBox.classList.contains(markdownRenderedClassName)) {
+ return;
+ }
+ // 标记已渲染 markdown,防止重复执行导致性能损失
+ messageBox.classList.add(markdownRenderedClassName);
+ // Get all children of message box. Return if length is zero.
+ const spanElem = Array.from(messageBox.children);
+ mditLogger('debug', 'SpanElem:', spanElem);
+ if (spanElem.length == 0) return;
+
+ // 坐标位置,以备后续将 html 元素插入文档中
+ const posBase = document.createElement('span')
+ spanElem[0].before(posBase)
+
+ // Here using entityProcess which may finally call DOMParser().parseFromString(input, "text/html");
+ // This may introduce XSS attack vulnurability, however we will use DOMPurify to prevent all
+ // dangerous HTML elements when rendering rendered markdown.
+ const markPieces = spanElem.map((msgPiece, index) => {
+ mditLogger('debug', 'PieceProcessor', 'index:', index);
+ mditLogger('debug', 'PieceProcessor', 'Original Piece:', msgPiece);
+ let retInfo = undefined;
+
+ // try apply pieces processor in order. Stop once a processor could process current msgPiece
+ processorList.some(function (procFunc) {
+ let funcRet = procFunc(msgPiece, index);
+ retInfo = funcRet;
+
+ return retInfo !== undefined;
+ });
- mditLogger('debug', 'Newly found message count:', newlyFoundMsgList.length);
+ // if undefined, this element should be ignored and not be removed in later process.
+ if (retInfo === undefined) {
+ msgPiece.classList.add(markdownIgnoredPieceClassName);
+ }
+ mditLogger('debug', 'PieceProcessor', 'Piece processor return:', retInfo);
+ return retInfo;
+ });
- newlyFoundMsgList.forEach(async (messageBox) => {
- // 标记已渲染 markdown,防止重复执行导致性能损失
- messageBox.classList.add(markdownRenderedClassName)
-
- // 消息都在 span 里
- const spanElem = Array.from(messageBox.childNodes)
- .filter((e) => e.tagName == 'SPAN')
-
- if (spanElem.length == 0) return
-
- // 坐标位置,以备后续将 html 元素插入文档中
- const posBase = document.createElement('span')
- spanElem[0].before(posBase)
-
- // Here using entityProcess which may finally call DOMParser().parseFromString(input, "text/html");
- // This may introduce XSS attack vulnurability, however we will use DOMPurify to prevent all
- // dangerous HTML elements when rendering rendered markdown.
- const markPieces = spanElem.map((msgPiece, index) => {
- if (msgPiece.className.includes("text-element") && !msgPiece.querySelector('.text-element--at')) {
- return {
- mark: Array.from(msgPiece.getElementsByTagName("span"))
- .map((e) => e.innerHTML)
- .reduce((acc, x) => acc + entityProcesor(x), ''),
- replace: null
- }
- } else {
- const id = "placeholder-" + index
- return {
- mark: ``,
- replace: (parent) => {
- try {
- // here oldNode may be `undefined`
- // Plugin will broke without this try catch block.
- const oldNode = parent.querySelector(`#${id}`);
- oldNode.replaceWith(msgPiece);
- } catch (e) {
- ;
- }
- }
- }
+ // 渲染 markdown
+ const marks = markPieces.filter(p => p !== undefined).map((p) => p.mark).reduce((acc, p) => acc + p, "");
+ mditLogger('debug', 'MarkdownRender Input:', marks);
+ let renderedHtml = renderedHtmlProcessor(await generateMarkdownIns().render(marks));
+ mditLogger('debug', 'MarkdownRender Output:', renderedHtml)
+
+ // 移除旧元素
+ spanElem
+ .filter((e) => messageBox.hasChildNodes(e))
+ .forEach((e) => {
+ // do not remove formerly ignored elements.s
+ if (e.classList.contains(markdownIgnoredPieceClassName)) {
+ mditLogger('debug', 'Remove Ignore Triggered:', e);
+ return;
}
+ messageBox.removeChild(e);
});
- // 渲染 markdown
- const marks = markPieces.map((p) => p.mark).reduce((acc, p) => acc + p, "");
- var renderedHtml = renderedHtmlProcessor(await generateMarkdownIns().render(marks))
-
- // 移除旧元素
- spanElem
- .filter((e) => messageBox.hasChildNodes(e))
- .forEach((e) => {
- messageBox.removeChild(e)
- })
-
- // 将原有元素替换回内容
- const markdownBody = document.createElement('div');
- // some themes rely on this class to render
- markdownBody.innerHTML = `
${renderedHtml}
`;
- markPieces.filter((p) => p.replace != null)
- .forEach((p) => {
- p.replace(markdownBody)
- })
-
- // Handle click of Copy Code Button
- addOnClickHandleForCopyButton(markdownBody);
-
- // Handle click of Copy Latex Button
- addOnClickHandleForLatexBlock(markdownBody);
-
- // 在外部浏览器打开连接
- markdownBody.querySelectorAll("a").forEach((e) => {
- e.classList.add("markdown_it_link");
- e.classList.add("text-link");
- e.onclick = async (event) => {
- event.preventDefault();
- const href = event
- .composedPath()[0]
- .href.replace("app://./renderer/", "");
- await LiteLoader.api.openExternal(href);
- return false;
- };
- })
+ // 将原有元素替换回内容
+ const markdownBody = document.createElement('div');
+ // some themes rely on this class to render
+ markdownBody.innerHTML = `${renderedHtml}
`;
+ markPieces.filter((p) => (p?.replace !== undefined))
+ .forEach((p) => {
+ p.replace(markdownBody, p.id);
+ });
- // 放回内容
- Array.from(markdownBody.childNodes)
- .forEach((elem) => {
- posBase.before(elem)
- })
- messageBox.removeChild(posBase);
- });
- // code that runs after renderer work finished.
- changeDirectionToColumnWhenLargerHeight();
- elementDebugLogger();
-}
-function loadCSSFromURL(url, id) {
- const link = document.createElement("link");
- link.rel = "stylesheet";
- link.href = url;
- link.id = id;
- document.head.appendChild(link);
-}
-onLoad();
+ // Handle click of Copy Code Button
+ addOnClickHandleForCopyButton(markdownBody);
+
+ // Handle click of Copy Latex Button
+ addOnClickHandleForLatexBlock(markdownBody);
+
+ // 在外部浏览器打开连接
+ handleExternalLink(markdownBody);
+
+ // 放回内容
+ Array.from(markdownBody.childNodes)
+ .forEach((elem) => {
+ posBase.before(elem)
+ })
+ messageBox.removeChild(posBase);
+}
function _onLoad() {
- const settings = useSettingsStore.getState();
const plugin_path = LiteLoader.plugins.markdown_it.path.plugin;
loadCSSFromURL(`local:///${plugin_path}/src/style/markdown.css`);
@@ -229,7 +234,8 @@ function _onLoad() {
loadCSSFromURL(`local:///${plugin_path}/src/style/hljs-github-dark.css`, 'github-hl-dark');
loadCSSFromURL(`local:///${plugin_path}/src/style/hljs-github.css`, 'github-hl-adaptive');
- var _ = useSettingsStore.subscribe(
+ // Change fenced code block theme based on settings.
+ let _ = useSettingsStore.subscribe(
state => (state.codeHighligtThemeFollowSystem),
(isFollowSystem) => {
if (isFollowSystem) {
@@ -240,6 +246,8 @@ function _onLoad() {
}
});
+
+ // Observe change of message list. Once changed, trigger render() function.
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
@@ -252,24 +260,32 @@ function _onLoad() {
}
}
});
+ observer.observe(document.body, { childList: true, subtree: true });
+}
- const targetNode = document.body;
- const config = { childList: true, subtree: true };
- observer.observe(targetNode, config);
+/**
+ * Util function used in onLoad() to load local CSS.
+ */
+function loadCSSFromURL(url, id) {
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = url;
+ link.id = id;
+ document.head.appendChild(link);
}
function onLoad() {
try {
- console.log('[MarkdownIt] OnLoad() triggered');
+ mditLogger('debug', '[MarkdownIt] OnLoad() triggered');
return _onLoad();
} catch (e) {
- console.log(e);
+ mditLogger('error', e);
}
}
// 打开设置界面时触发
function onSettingWindowCreated(view) {
- var root = React.createRoot(view);
+ let root = React.createRoot(view);
root.render();
}
diff --git a/src/utils/logger.ts b/src/utils/logger.ts
index a7952e6..45a3e2c 100644
--- a/src/utils/logger.ts
+++ b/src/utils/logger.ts
@@ -53,8 +53,20 @@ export function mditLoggerGenerator(options: MditLoggerOptions = defaultMditLogg
...params);
}
- if (outputToFileSettingEnabled() && options.fileOutput) {
- markdown_it.log(consoleFunction, ...params);
+ try {
+ if (outputToFileSettingEnabled() && options.fileOutput) {
+
+ let serializedParams = params.map((param) => {
+ if (typeof param !== 'string') {
+ return JSON.stringify(param);
+ }
+ return param;
+ });
+
+ markdown_it.log(consoleFunction, ...serializedParams);
+ }
+ } catch (e) {
+ ;
}
return undefined;
diff --git a/src/utils/logger_main.ts b/src/utils/logger_main.ts
index c63000e..3f422f7 100644
--- a/src/utils/logger_main.ts
+++ b/src/utils/logger_main.ts
@@ -1,5 +1,5 @@
import * as path from 'path';
-import { existsSync, mkdirSync, createWriteStream } from 'fs';
+import { existsSync, mkdirSync, rmSync, createWriteStream } from 'fs';
import { writeFile } from 'fs/promises';
import { LiteLoaderInterFace } from '@/utils/liteloader_type';
@@ -34,6 +34,9 @@ export function generateMainProcessLogerWriter() {
console.log(`[markdown-it] logFolderPath: ${logFolderPath}`);
console.log(`[markdown-it] logFilePath: ${logFilePath}`);
+ // clear former log file
+ rmSync(logFolderPath, { recursive: true });
+
// create dir if not exists
try {
if (!existsSync(logFolderPath)) {
@@ -52,9 +55,9 @@ export function generateMainProcessLogerWriter() {
var argsStr = args.reduce(function (str, value) {
if (typeof value === 'string') {
- return str + value;
+ return str + value + ' ';
}
- return str + JSON.stringify(value);
+ return str + JSON.stringify(value) + ' ';
}, '');
var logStr = `${consoleMode.toUpperCase()} | ${timeStr} | ${argsStr}`;