Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions markdown-it/info.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"name": "Markdown-it markdown parser",
"identifier": "markdown-it",
"script": "markdown-it.qml",
"resources": ["markdown-it.js", "markdown-it-deflist.js"],
"authors": ["@milan-rusev", "@bessw"],
"version": "1.4",
"resources": ["markdown-it.js", "markdown-it-deflist.js", "markdown-it-katex.js"],
"authors": ["@milan-rusev", "@bessw", "@cjendantix"],
"version": "1.5",
"minAppVersion": "20.6.0",
"description": "This script replaces the default markdown renderer with markdown-it.\n\n<b>Dependencies</b>\n<a href=\"https://github.com/markdown-it/markdown-it\">markdown-it.js</a> (v8.4.2 bundled with the script)\n\n<b>Usage</b>\nFor the possible configuration options check <a href=\"https://github.com/markdown-it/markdown-it/tree/master/lib/presets\">here</a>.\n\n<b>Important</b>\nThis script currently only works with <a href=\"https://github.com/qownnotes/scripts/issues/77\"><b>legacy media links</b></a>. You can turn them on in the <i>General Settings</i>.\n\nImportant note: You need to use legacy image linking with this script, otherwise there will be no images shown in the preview!"
"description": "This script replaces the default markdown renderer with markdown-it and allows for optional LaTeX rendering support with the Markdown-It KaTeX plugin. (NOTE: LaTeX defaults to rendering with MathML ONLY). \n\n<b>Dependencies</b>\n<a href=\"https://github.com/markdown-it/markdown-it\">markdown-it.js</a> (v8.4.2 bundled with the script)\n<a href=\"https://github.com/mdit-plugins/mdit-plugins/tree/main/packages/katex\">Markdown-It KaTeX plugin</a> (v0.18.0 bundled with the script)\n\n<b>Usage</b>\nFor the possible configuration options check <a href=\"https://github.com/markdown-it/markdown-it/tree/master/lib/presets\">here</a>.\n\n<b>Important</b>\nThis script currently only works with <a href=\"https://github.com/qownnotes/scripts/issues/77\"><b>legacy media links</b></a>. You can turn them on in the <i>General Settings</i>.\n\nImportant note: You need to use legacy image linking with this script, otherwise there will be no images shown in the preview!"
}
1 change: 1 addition & 0 deletions markdown-it/markdown-it-katex.js

Large diffs are not rendered by default.

131 changes: 91 additions & 40 deletions markdown-it/markdown-it.qml
Original file line number Diff line number Diff line change
@@ -1,57 +1,85 @@
import QtQml 2.0
import QOwnNotesTypes 1.0

import "markdown-it.js" as MarkdownIt
import QtQml 2.0
import "markdown-it-deflist.js" as MarkdownItDeflist
import "markdown-it-katex.js" as MarkdownItKatex
import "markdown-it.js" as MarkdownIt

QtObject {
property string customStylesheet
property variant md
property string options
property variant settingsVariables: [
{
"identifier": "options",
"name": "Markdown-it options",
"description": "For available options and default values see <a href='https://github.com/markdown-it/markdown-it/blob/master/lib/presets'>markdown-it presets</a>.",
"type": "text",
"default": "{" + "\n" + " //html: false, // Enable HTML tags in source" + "\n" + " //xhtmlOut: false, // Use '/' to close single tags (<br />)" + "\n" + " //breaks: false, // Convert '\\n' in paragraphs into <br>" + "\n" + " //langPrefix: 'language-', // CSS language prefix for fenced blocks" + "\n" + " //linkify: false, // autoconvert URL-like texts to links" + "\n" + "" + "\n" + " // Enable some language-neutral replacements + quotes beautification" + "\n" + " //typographer: false," + "\n" + "" + "\n" + " // Double + single quotes replacement pairs, when typographer enabled," + "\n" + " // and smartquotes on. Could be either a String or an Array." + "\n" + " //" + "\n" + " // For example, you can use '«»„“' for Russian, '„“‚‘' for German," + "\n" + " // and ['«\\xA0', '\\xA0»', '‹\\xA0', '\\xA0›'] for French (including nbsp)." + "\n" + " //quotes: '\\u201c\\u201d\\u2018\\u2019', /* “”‘’ */" + "\n" + "" + "\n" + " // Highlighter function. Should return escaped HTML," + "\n" + " // or '' if the source string is not changed and should be escaped externaly." + "\n" + " // If result starts with <pre... internal wrapper is skipped." + "\n" + " //" + "\n" + " // function (/*str, lang*/) { return ''; }" + "\n" + " //" + "\n" + " //highlight: null," + "\n" + "" + "\n" + " //maxNesting: 100 // Internal protection, recursion limit" + "\n" + "}"
},
{
"identifier": "useDeflistPlugin",
"name": "Definition lists",
"text": "Enable the Mardown-it definition list (<dl>) plugin",
"type": "boolean",
"default": false
},
{
"identifier": "customStylesheet",
"name": "Custom stylesheet",
"description": "Please enter your custom stylesheet:",
"type": "text",
"default": null
},
]
property variant settingsVariables: [{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You changed a lot of whitespaces in this and the following lines. That's what I meant with code formatting changes.

Copy link
Contributor Author

@CJendantix CJendantix May 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. The diff looks cleaner in VSCode. That section all I did was add a boolean option titled LaTeX Support that would enable or disable KaTeX.

"identifier": "options",
"name": "Markdown-it options",
"description": "For available options and default values see <a href='https://github.com/markdown-it/markdown-it/blob/master/lib/presets'>markdown-it presets</a>.",
"type": "text",
"default": "{" + "\n" + " //html: false, // Enable HTML tags in source" + "\n" + " //xhtmlOut: false, // Use '/' to close single tags (<br />)" + "\n" + " //breaks: false, // Convert '\\n' in paragraphs into <br>" + "\n" + " //langPrefix: 'language-', // CSS language prefix for fenced blocks" + "\n" + " //linkify: false, // autoconvert URL-like texts to links" + "\n" + "" + "\n" + " // Enable some language-neutral replacements + quotes beautification" + "\n" + " //typographer: false," + "\n" + "" + "\n" + " // Double + single quotes replacement pairs, when typographer enabled," + "\n" + " // and smartquotes on. Could be either a String or an Array." + "\n" + " //" + "\n" + " // For example, you can use '«»„“' for Russian, '„“‚‘' for German," + "\n" + " // and ['«\\xA0', '\\xA0»', '‹\\xA0', '\\xA0›'] for French (including nbsp)." + "\n" + " //quotes: '\\u201c\\u201d\\u2018\\u2019', /* “”‘’ */" + "\n" + "" + "\n" + " // Highlighter function. Should return escaped HTML," + "\n" + " // or '' if the source string is not changed and should be escaped externaly." + "\n" + " // If result starts with <pre... internal wrapper is skipped." + "\n" + " //" + "\n" + " // function (/*str, lang*/) { return ''; }" + "\n" + " //" + "\n" + " //highlight: null," + "\n" + "" + "\n" + " //maxNesting: 100 // Internal protection, recursion limit" + "\n" + "}"
}, {
"identifier": "useDeflistPlugin",
"name": "Definition lists",
"text": "Enable the Markdown-it definition list (<dl>) plugin",
"type": "boolean",
"default": false
}, {
"identifier": "useKatexPlugin",
"name": "LaTeX Support",
"text": "Enable the Markdown-it definition list KaTeX plugin",
"type": "boolean",
"default": false
}, {
"identifier": "customStylesheet",
"name": "Custom stylesheet",
"description": "Please enter your custom stylesheet:",
"type": "text",
"default": null
}]
property bool useDeflistPlugin
property bool useKatexPlugin

function init() {
var optionsObj = eval("(" + options + ")");
// md = new MarkdownIt.markdownit(optionsObj);
md = new this.markdownit(optionsObj); // workaround because its a node module and qml-browserify didn't work
md = new this.markdownit(optionsObj);
if (useDeflistPlugin)
md.use(this.markdownitDeflist);

if (useDeflistPlugin) {
// md.use(MarkdownItDeflist.markdownitDeflist);
md.use(this.markdownitDeflist); // workaround because its a node module and qml-browserify didn't work
}
if (useKatexPlugin)
this.markdownItKatex(md, {
"output": "mathml"
});

//Allow file:// url scheme
var validateLinkOrig = md.validateLink;
var GOOD_PROTO_RE = /^(file):/;
md.validateLink = function (url) {
md.validateLink = function(url) {
var str = url.trim().toLowerCase();
return GOOD_PROTO_RE.test(str) ? true : validateLinkOrig(url);
};
}

function resolvePath(base, relative) {
const baseParts = base.replace(/\/+$/, '').split('/');
const relParts = relative.replace(/^\.\/+/, '').split('/');
for (const part of relParts) {
if (part === '..')
baseParts.pop();
else if (part !== '.' && part !== '')
baseParts.push(part);
}
return baseParts.join('/');
}

function isProtocolUrl(url) {
return /^[a-zA-Z][\w+.-]*:\/\//.test(url);
}

function isWindowsAbsolute(path) {
return /^[a-zA-Z]:[\\/]/.test(path);
}

function isUnixAbsolute(path) {
return path.startsWith('/');
}

/**
* This function is called when the markdown html of a note is generated
*
Expand All @@ -67,22 +95,45 @@ QtObject {
*/
function noteToMarkdownHtmlHook(note, html, forExport) {
var mdHtml = md.render(note.noteText);

//Insert root folder in attachments and media relative urls
var path = script.currentNoteFolderPath();
if (script.platformIsWindows()) {
if (script.platformIsWindows())
path = "/" + path;
}
mdHtml = mdHtml.replace(new RegExp("href=\"file://attachments/", "gi"), "href=\"file://" + path + "/attachments/");
mdHtml = mdHtml.replace(new RegExp("src=\"file://media/", "gi"), "src=\"file://" + path + "/media/");

mdHtml = mdHtml.replace(/(\b(?:src|href|data-[\w-]+)\s*=\s*["'])([^"']+)["']/gi, (_, prefix, rawPath) => {
// Convert backslashes to forward slashes for URL

if (isProtocolUrl(rawPath))
return `${prefix}${rawPath}"`;

let finalPath;
if (isUnixAbsolute(rawPath) || isWindowsAbsolute(rawPath))
// Absolute path (Unix or Windows)
finalPath = rawPath.replace(/\\/g, '/');
else
// Relative path → resolve against base
finalPath = resolvePath(basePath, rawPath.replace(/^\.\/+/, ''));
return `${prefix}file://${finalPath}"`;
});
// Don't attempt to render in the preview, it doesn't support mathml or complex css
if (!forExport && useKatexPlugin)
mdHtml = mdHtml.replace(/(<math\b[^>]*>)([\s\S]*?)(<\/math>)/gi, (fullMatch, openMathTag, mathInner, closeMathTag) => {
let blockPresent = /\bdisplay="block"/i.test(openMathTag);
let out = blockPresent ? '<br><i>' + openMathTag : '&nbsp;<i>' + openMathTag;
out += mathInner.replace(/(<semantics\b[^>]*>)([\s\S]*?)(<\/semantics>)/gi, (semiMatch, openSemi, semiInner, closeSemi) => {
const cleaned = semiInner.replace(/<mrow\b[^>]*>[\s\S]*?<\/mrow>/gi, '');
return openSemi + cleaned + closeSemi;
});
out += blockPresent ? closeMathTag + '</i><br>' : closeMathTag + '</i>&nbsp;';
return out;
});

//Get original styles
var head = html.match(new RegExp("<head>(?:.|\n)*?</head>"))[0];
//Add custom styles
head = head.replace("</style>", "table {border-spacing: 0; border-style: solid; border-width: 1px; border-collapse: collapse; margin-top: 0.5em;} th, td {padding: 0 5px;}" + customStylesheet + "</style>");

mdHtml = "<html>" + head + "<body>" + mdHtml + "</body></html>";

return mdHtml;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you add that on purpose?

Copy link
Contributor Author

@CJendantix CJendantix May 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that was the QML formatter's doing, though I don't imagine it's a problem.

}