Skip to content

Commit

Permalink
feat: Add Fenced code and LaTeX copy button. (#52)
Browse files Browse the repository at this point in the history
* feat: Add fenced code lang display and copy button.

* fix: Add space between settings title and desc.

* feat: Add copy button for latex block.

* fix: Fix the font color issue when using with TG theme.
  • Loading branch information
nfnfgo authored Jul 4, 2024
1 parent 817908b commit 76f24b0
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 12 deletions.
47 changes: 46 additions & 1 deletion src/components/code_block.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ export function HighLightedCodeBlock({ content, lang, markdownItIns }) {
Finalcontent = hljs.highlight(contentPreprocess(content), { language: lang, ignoreIllegals: true }).value;
} catch (e) {
console.debug(`[Markdown-it] hljs error: ${e}`);
}
}

return (<pre className='hljs hl-code-block'>
<button className='lang_copy'>
<p className='lang'>{lang}</p>
<p className='copy'>复制</p>
</button>
<code dangerouslySetInnerHTML={{ __html: Finalcontent }}></code>
</pre>);
}
Expand All @@ -52,4 +56,45 @@ export function renderInlineCodeBlockString(tokens, idx, options, env, slf) {
return '<code' + slf.renderAttrs(token) + '>' +
token.content +
'</code>';
}

/**
* Find all Copy Button and add click handler to it. Will directly mutate the received
* HTML element.
*
* @param {HTMLElement} element
*/
export function addOnClickHandleForCopyButton(element) {
var buttons = element.querySelectorAll('pre.hl-code-block>button.lang_copy');
Array.from(buttons)
.forEach(function (copyButton) {
try {
// get content of this code block
var codeContent = copyButton.parentElement.querySelector('code').textContent;
copyButton.onclick = () => { navigator.clipboard.writeText(codeContent) };
} catch (e) {
;
}
});
}

/**
* Find all Copy Button of Latex block and add hanlder to it.
*
* @param {HTMLElement} element
*/
export function addOnClickHandleForLatexBlock(element) {
var buttons = element.querySelectorAll('div.katex-block-rendered>button.copy_latex');


Array.from(buttons)
.forEach(function (copyButton) {
try {
// find tex annotation
var latexAnno = copyButton.parentElement.querySelector('annotation[encoding="application/x-tex"]').textContent;
copyButton.onclick = () => { navigator.clipboard.writeText(latexAnno) };
} catch (e) {
;
}
});
}
1 change: 1 addition & 0 deletions src/components/setting_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ function TextAndCaptionBlock({ title, caption }) {
data-type='secondary'
style={{
// "text-wrap": "wrap",
'margin-top': '3px',
"word-break": "break-word"
}}
><p style={{
Expand Down
17 changes: 15 additions & 2 deletions src/lib/markdown-it-katex.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ for rendering output.
'use strict';

var katex = require('katex');
import React from 'react';
import { renderToString } from 'react-dom/server';

// Test if potential opening or closing delimieter
// Assumes that there is a "$" at state.src[pos]
Expand Down Expand Up @@ -247,7 +249,7 @@ function unescapeHtml(unsafe) {
.replace(/&#039;/g, "\'");
}

module.exports = function math_plugin(md, options) {
export default function math_plugin(md, options) {
// Default options

options = options || {};
Expand Down Expand Up @@ -279,7 +281,8 @@ module.exports = function math_plugin(md, options) {
latex = unescapeHtml(latex); // work with QQNT Markdown-it
options.displayMode = true;
try {
return `<p class="katex-block ${options.blockClass}">` + katex.renderToString(latex, options) + "</p>";
// return `<p class="katex-block katex_rendered ${options.blockClass}">` + katex.renderToString(latex, options) + "</p>";
return renderToString(<KatexBlockComponent latex={latex} options={options} />);
}
catch (error) {
if (options.throwOnError) { console.log(error); }
Expand All @@ -300,3 +303,13 @@ module.exports = function math_plugin(md, options) {
md.renderer.rules.math_inline = inlineRenderer;
md.renderer.rules.math_block = blockRenderer;
};


function KatexBlockComponent({ latex, options }) {
return (
<div className='katex-block-rendered'>
<button className='copy_latex'>复制公式</button>
<p className='katex-block' dangerouslySetInnerHTML={{ __html: katex.renderToString(latex, options) }}></p>
</div>
);
}
49 changes: 41 additions & 8 deletions src/renderer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import katex from '@/lib/markdown-it-katex';
import { escapeHtml, purifyHtml, unescapeHtml } from '@/utils/htmlProc';

// Components
import { HighLightedCodeBlock, renderInlineCodeBlockString } from './components/code_block';
import {
HighLightedCodeBlock,
addOnClickHandleForCopyButton,
renderInlineCodeBlockString,
addOnClickHandleForLatexBlock
} from './components/code_block';

// States
import { useSettingsStore } from '@/states/settings';
Expand Down Expand Up @@ -122,15 +127,21 @@ function render() {
return {
mark: `<span id="${id}"></span>`,
replace: (parent) => {
const oldNode = parent.querySelector(`#${id}`)
oldNode.replaceWith(msgPiece)
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.map((p) => p.mark).reduce((acc, p) => acc + p, "")
const marks = markPieces.map((p) => p.mark).reduce((acc, p) => acc + p, "");
var renderedHtml = renderedHtmlProcessor(await generateMarkdownIns().render(marks))

// 移除旧元素
Expand All @@ -142,12 +153,19 @@ function render() {

// 将原有元素替换回内容
const markdownBody = document.createElement('div');
markdownBody.innerHTML = renderedHtml;
// some themes rely on this class to render
markdownBody.innerHTML = `<div class="text-normal">${renderedHtml}</div>`;
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");
Expand Down Expand Up @@ -183,7 +201,7 @@ function loadCSSFromURL(url, id) {

onLoad();

function onLoad() {
function _onLoad() {
const settings = useSettingsStore.getState();
const plugin_path = LiteLoader.plugins.markdown_it.path.plugin;

Expand All @@ -206,7 +224,12 @@ function onLoad() {
const observer = new MutationObserver((mutationsList) => {
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
render();
// avoid error in render break users QQNT.
try { render(); }
catch (e) {
;
}

}
}
});
Expand All @@ -216,13 +239,23 @@ function onLoad() {
observer.observe(targetNode, config);
}

function onLoad() {
try {
console.log('[MarkdownIt] OnLoad() triggered');
return _onLoad();
} catch (e) {
console.log(e);
}
}

// 打开设置界面时触发
function onSettingWindowCreated(view) {
var root = React.createRoot(view);
root.render(<SettingPage></SettingPage>);
}

export {
onSettingWindowCreated
onSettingWindowCreated,
onLoad,
}

63 changes: 62 additions & 1 deletion src/style/markdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,67 @@ span>p>img {
height: auto;
}

pre.hl-code-block {
/* Border radius for fenced code block */
pre.hl-code-block,
div.katex-block-rendered {
position: relative;
min-width: 5rem;
min-height: 2rem;
border-radius: 5px;
}

/* language and copy button style */
pre.hl-code-block>button.lang_copy,
div.katex-block-rendered>button.copy_latex {
z-index: 10;
position: absolute;
right: 2px;
top: 2px;
min-width: 4rem;
min-height: 1.5rem;
backdrop-filter: blur(8px);
border-radius: 4px;
border-width: 0px;
box-shadow: 3px 10px 15px -3px rgb(0 0 0 / 0.1), 2px 4px 6px -6px rgb(0 0 0 / 0.1);

transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 20ms;
}

pre.hl-code-block>button.lang_copy:active,
div.katex-block-rendered>button.copy_latex:active {
transform: scale(0.85);
}


@media(prefers-color-scheme: light) {

pre.hl-code-block>button.lang_copy,
div.katex-block-rendered>button.copy_latex {
background-color: rgba(255, 255, 255, 0.5);
color: black;
}
}

@media(prefers-color-scheme: dark) {

pre.hl-code-block>button.lang_copy,
div.katex-block-rendered>button.copy_latex {
background-color: rgba(0, 0, 0, 0.5);
color: white;
}
}

pre.hl-code-block:not(:hover)>button.lang_copy,
div.katex-block-rendered:not(:hover)>button.copy_latex {
display: none;
}

pre.hl-code-block>button.lang_copy:hover>p.lang {
display: none;
}

pre.hl-code-block>button.lang_copy:not(:hover)>p.copy {
display: none;
}

0 comments on commit 76f24b0

Please sign in to comment.