feat(export/markdown): add spellcheck=false to inline code

This commit is contained in:
Elian Doran 2026-01-03 23:19:58 +02:00
parent a613980ea4
commit 4710a6af41
No known key found for this signature in database
3 changed files with 32 additions and 17 deletions

View File

@ -1,7 +1,8 @@
import sanitizeHtml from "sanitize-html";
import { sanitizeUrl } from "@braintree/sanitize-url";
import optionService from "./options.js";
import { ALLOWED_PROTOCOLS, SANITIZER_DEFAULT_ALLOWED_TAGS } from "@triliumnext/commons";
import sanitizeHtml from "sanitize-html";
import optionService from "./options.js";
// intended mainly as protection against XSS via import
// secondarily, it (partly) protects against "CSS takeover"
@ -25,7 +26,7 @@ function sanitize(dirtyHtml: string) {
}
// Get allowed tags from options, with fallback to default list if option not yet set
let allowedTags;
let allowedTags: readonly string[];
try {
allowedTags = JSON.parse(optionService.getOption("allowedHtmlTags"));
} catch (e) {
@ -38,11 +39,12 @@ function sanitize(dirtyHtml: string) {
// to minimize document changes, compress H
return sanitizeHtml(dirtyHtml, {
allowedTags,
allowedTags: allowedTags as string[],
allowedAttributes: {
"*": ["class", "style", "title", "src", "href", "hash", "disabled", "align", "alt", "center", "data-*"],
input: ["type", "checked"],
img: ["width", "height"]
img: ["width", "height"],
code: [ "spellcheck" ]
},
allowedStyles: {
"*": {

View File

@ -308,4 +308,10 @@ $$`;
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
});
it("adds spellcheck=false to inline code", () => {
const input = `This is some inline code: \`const x = 10;\``;
const expected = /*html*/`<p>This is some inline code: <code spellcheck="false">const x = 10;</code></p>`;
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
});
});

View File

@ -1,14 +1,17 @@
"use strict";
import { parse, Renderer, use, type Tokens } from "marked";
import htmlSanitizer from "../html_sanitizer.js";
import importUtils from "./utils.js";
import { getMimeTypeFromMarkdownName, MIME_TYPE_AUTO } from "@triliumnext/commons";
import { ADMONITION_TYPE_MAPPINGS } from "../export/markdown.js";
import utils from "../utils.js";
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
import wikiLinkTransclusion from "./markdown/wikilink_transclusion.js";
import { parse, Renderer, type Tokens,use } from "marked";
import { ADMONITION_TYPE_MAPPINGS } from "../export/markdown.js";
import htmlSanitizer from "../html_sanitizer.js";
import utils from "../utils.js";
import wikiLinkInternalLink from "./markdown/wikilink_internal_link.js";
import wikiLinkTransclusion from "./markdown/wikilink_transclusion.js";
import importUtils from "./utils.js";
const escape = utils.escapeHtml;
/**
* Keep renderer code up to date with https://github.com/markedjs/marked/blob/master/src/Renderer.ts.
@ -34,7 +37,7 @@ class CustomMarkdownRenderer extends Renderer {
}
// Escape the HTML.
text = utils.escapeHtml(text);
text = escape(text);
// Unescape &quot
text = text.replace(/&quot;/g, '"');
@ -57,9 +60,9 @@ class CustomMarkdownRenderer extends Renderer {
}
override checkbox({ checked }: Tokens.Checkbox): string {
return '<input type="checkbox"'
+ (checked ? 'checked="checked" ' : '')
+ 'disabled="disabled">';
return `<input type="checkbox"${
checked ? 'checked="checked" ' : ''
}disabled="disabled">`;
}
override listitem(item: Tokens.ListItem): string {
@ -117,6 +120,10 @@ class CustomMarkdownRenderer extends Renderer {
return `<blockquote>${body}</blockquote>`;
}
codespan({ text }: Tokens.Codespan): string {
return `<code spellcheck="false">${escape(text)}</code>`;
}
}
function renderToHtml(content: string, title: string) {
@ -136,7 +143,7 @@ function renderToHtml(content: string, title: string) {
let html = parse(processedText, {
async: false,
renderer: renderer
renderer
}) as string;
// After rendering, replace placeholders back with the formula HTML