Fixes to Markdown export (#8417)

This commit is contained in:
Elian Doran 2026-01-17 14:19:36 +02:00 committed by GitHub
commit 34b2df705b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 190 additions and 144 deletions

View File

@ -126,7 +126,7 @@
"swagger-jsdoc": "6.2.8",
"time2fa": "1.4.2",
"tmp": "0.2.5",
"turndown": "7.2.2",
"turnish": "1.7.1",
"unescape": "1.0.1",
"vite": "7.3.1",
"ws": "8.19.0",

View File

@ -1,55 +1,53 @@
<p>
<meta>Trilium can import ENEX files, which are used by Evernote for backup/export.
<p>Trilium can import ENEX files, which are used by Evernote for backup/export.
One ENEX file represents the content (notes and resources) of one notebook.</p>
<h2>Export ENEX from Evernote</h2>
<p>To export ENEX files from Evernote, you can use:</p>
<ul>
<li data-list-item-id="eabf6814a75b1ddc665781ed732fa65ff">Evernote desktop application. See Evernote&nbsp;documentation. Note that
<li>Evernote desktop application. See Evernote&nbsp;documentation. Note that
the limitation of this method is that you can only export 100 notes at
a time or one notebook at a time.</li>
<li data-list-item-id="e7c66b50692c80eb3a325841ae5218eb7">A third-party&nbsp;evernote-backup CLI tool. This tool can export all
<li>A third-party&nbsp;evernote-backup CLI tool. This tool can export all
of your notebooks in bulk.</li>
</ul>
<h2>Import ENEX in Trilium</h2>
<p>Once you have your ENEX files, do the following to import them in Trilium:</p>
<ol>
<li data-list-item-id="e86484b3caf72ef69321bc8639024a991">In the Trilium note tree, right-click the note under which you want to
<li>In the Trilium note tree, right-click the note under which you want to
import one or more of your ENEX files. The notes in the files will be imported
as child notes of the selected note.</li>
<li data-list-item-id="ec8593d763d8375e841f68731e454b548">Click&nbsp;Import into note.</li>
<li data-list-item-id="e1fb52a96a47f9d881342148ace1353ce">Choose your ENEX file or files and click&nbsp;Import.</li>
<li data-list-item-id="e44b2f10dc3fdd60956774647c440c9c3">During the import, you will see "Import in progress" message. If the import
<li>Click&nbsp;Import into note.</li>
<li>Choose your ENEX file or files and click&nbsp;Import.</li>
<li>During the import, you will see "Import in progress" message. If the import
is successful, the message will change to “Import finished successfully”
and then disappear.</li>
<li data-list-item-id="ebcec83cfa6d093376cd6fbcfff3db532">We recommend you to check the imported notes and their attachments to
<li>We recommend you to check the imported notes and their attachments to
verify that you havent lost any data.</li>
</ol>
<p>A non-exhaustive list of what the importer preserves:</p>
<ul>
<li data-list-item-id="e3180fcb6ac86b4543e514a9209c5e789">Attachments</li>
<li data-list-item-id="e7e9720e6bbadf04dde505b2ec56159ec">The hierarchy of headings (these are shifted to start with H2 because
<li>Attachments</li>
<li>The hierarchy of headings (these are shifted to start with H2 because
H1 is reserved for note title, see&nbsp;Headings)</li>
<li data-list-item-id="e6798ed61930845577e1f0750378bc52c">Tables</li>
<li data-list-item-id="ef9ddc278d5a093dfd5488c2e07b6278d">Bulleted lists</li>
<li data-list-item-id="ed8ef6d14f74b4fffd08bd66d9c859d36">Numbered lists</li>
<li data-list-item-id="e989884d7888ef1d6ce062c4e71b68636">Bold</li>
<li data-list-item-id="e438a5188d95036c5bfa91def39b38259">Italics</li>
<li data-list-item-id="eda9f7b42b3e02ba4e54a805b2def949b">Strikethrough</li>
<li data-list-item-id="e095a40c46c7f8d1083e9d74258ee85e4">Highlights</li>
<li data-list-item-id="e822d52533a7e7876e0963e043de49e48">Font colors</li>
<li data-list-item-id="ece8974b757a014f25208911706a0462d">Soft line breaks</li>
<li data-list-item-id="eb22c11732fea1beee2976ede9d81ca5e">External links</li>
<li>Tables</li>
<li>Bulleted lists</li>
<li>Numbered lists</li>
<li>Bold</li>
<li>Italics</li>
<li>Strikethrough</li>
<li>Highlights</li>
<li>Font colors</li>
<li>Soft line breaks</li>
<li>External links</li>
</ul>
<p>However, we do not guarantee that all of your formatting will be imported
100% correctly.</p>
<h2>Limitations</h2>
<ul>
<li data-list-item-id="ece66c1841631b7250ddb6c101d28afd6">The size limit of one import is 250Mb. If the total size of your files
<li>The size limit of one import is 250Mb. If the total size of your files
is larger, you can increase the&nbsp;upload limit, or divide your files,
and run the import as many times as necessary.</li>
<li data-list-item-id="ece322cd2ca2b9332154a25f48c16309d">All resources (except for images) are created as notes attachments.</li>
<li
data-list-item-id="ee13e15fa378905573e13b348441be574">If you have HTML inside ENEX files, the HTML formatting may be broken
<li>All resources (except for images) are created as notes attachments.</li>
<li>If you have HTML inside ENEX files, the HTML formatting may be broken
or lost after import in Trilium. You can report major problems at&nbsp;Trilium
issue tracker.</li>
</ul>
@ -59,24 +57,24 @@
<p>If you want to restore the internal links in Trilium after you import
all of your ENEX files, you can use or adapt this custom script:&nbsp;
<a
class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/mHbBMPDPkVV5/syuSEKf2rUGr/_help_dj3j8dG4th4l">Process internal links by title</a>
class="reference-link" href="#root/_help_dj3j8dG4th4l">Process internal links by title</a>
</p>
<p>The script does the following:</p>
<ol>
<li data-list-item-id="e6c3f7477c6a241ef97cb9bf15623a2c0">It finds all Evernote internal links.</li>
<li data-list-item-id="e928015e4a024f40ebdb85b2e5b77844f">For each one, it checks if its link text matches a note title, and if
<li>It finds all Evernote internal links.</li>
<li>For each one, it checks if its link text matches a note title, and if
yes, it replaces the Evernote link with an internal Trilium link. If not,
it leaves the Evernote link in place.</li>
<li data-list-item-id="e5fb070b954d7cba355c3c9065e8e4b1f">If it finds more than one note with a matching note title, it leaves the
<li>If it finds more than one note with a matching note title, it leaves the
Evernote link in place.</li>
<li data-list-item-id="eea5e5efb229b5a731e3c980160de29f8">It outputs the results in a log that you can see in the respective code
note in Trilium.&nbsp;</li>
<li>It outputs the results in a log that you can see in the respective code
note in Trilium.</li>
</ol>
<p>The script has the following limitations:</p>
<ul>
<li data-list-item-id="e467615aa997504c5aa55f2f169d0028f">It will not fix links to anchors and links to notes that you renamed in
<li>It will not fix links to anchors and links to notes that you renamed in
Evernote after you created the links.</li>
<li data-list-item-id="e83d3745ce3531ae99a9619274ad42440">Some note titles might not be well identified, even if they exist. This
<li>Some note titles might not be well identified, even if they exist. This
is especially the case if the note title contains some special characters.
Should this be problematic, consider&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/BgmBlOIl72jZ/_help_wy8So3yZZlH9">Reporting issues</a>.</li>
Should this be problematic, consider&nbsp;<a class="reference-link" href="#root/_help_wy8So3yZZlH9">Reporting issues</a>.</li>
</ul>

View File

@ -387,4 +387,58 @@ describe("Markdown export", () => {
expect(markdownExportService.toMarkdown(html)).toBe(expected);
});
it("maintains escaped HTML tags", () => {
const html = /*html*/`<p>&lt;div&gt;Hello World&lt;/div&gt;</p>`;
const expected = `\\<div\\>Hello World\\</div\\>`;
expect(markdownExportService.toMarkdown(html)).toBe(expected);
});
it("escapes HTML tags inside list", () => {
const html = trimIndentation/*html*/`\
<ul>
<li data-list-item-id="e07fda078f7dd7103a3b9017f49eb1589">
&lt;note&gt; is note.
</li>
</ul>
`;
const expected = trimIndentation`\
* \\<note\\> is note.`;
expect(markdownExportService.toMarkdown(html)).toBe(expected);
});
it("exports jQuery code in table properly", () => {
const html = trimIndentation`\
<figure class="table">
<table>
<thead>
<tr>
<th>
Code
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<pre>
<code class="language-text-x-trilium-auto">this.$widget = $("&lt;div&gt;");</code>
</pre>
</td>
</tr>
</tbody>
</table>
</figure>
`;
const expected = trimIndentation`\
<table><thead><tr><th>Code</th></tr></thead><tbody><tr><td><pre><code class="language-text-x-trilium-auto">this.$widget = $("&lt;div&gt;");</code>
</pre></td></tr></tbody></table>`;
expect(markdownExportService.toMarkdown(html)).toBe(expected);
});
it("renders emphasis with underscore", () => {
const html = /*html*/`<p>This is <em>underlined</em> text.</p>`;
const expected = `This is _underlined_ text.`;
expect(markdownExportService.toMarkdown(html)).toBe(expected);
});
});

View File

@ -1,9 +1,7 @@
"use strict";
import TurndownService, { type Rule } from "turndown";
import { gfm } from "@triliumnext/turndown-plugin-gfm";
import Turnish, { type Rule } from "turnish";
let instance: TurndownService | null = null;
let instance: Turnish | null = null;
// TODO: Move this to a dedicated file someday.
export const ADMONITION_TYPE_MAPPINGS: Record<string, string> = {
@ -16,12 +14,12 @@ export const ADMONITION_TYPE_MAPPINGS: Record<string, string> = {
export const DEFAULT_ADMONITION_TYPE = ADMONITION_TYPE_MAPPINGS.note;
const fencedCodeBlockFilter: TurndownService.Rule = {
filter: function (node, options) {
const fencedCodeBlockFilter: Rule = {
filter (node, options) {
return options.codeBlockStyle === "fenced" && node.nodeName === "PRE" && node.firstChild !== null && node.firstChild.nodeName === "CODE";
},
replacement: function (content, node, options) {
replacement (content, node, options) {
if (!node.firstChild || !("getAttribute" in node.firstChild) || typeof node.firstChild.getAttribute !== "function") {
return content;
}
@ -29,23 +27,25 @@ const fencedCodeBlockFilter: TurndownService.Rule = {
const className = node.firstChild.getAttribute("class") || "";
const language = rewriteLanguageTag((className.match(/language-(\S+)/) || [null, ""])[1]);
return "\n\n" + options.fence + language + "\n" + node.firstChild.textContent + "\n" + options.fence + "\n\n";
return `\n\n${options.fence}${language}\n${node.firstChild.textContent}\n${options.fence}\n\n`;
}
};
function toMarkdown(content: string) {
if (instance === null) {
instance = new TurndownService({
instance = new Turnish({
headingStyle: "atx",
bulletListMarker: "*",
emDelimiter: "_",
codeBlockStyle: "fenced",
blankReplacement(content, node, options) {
if (node.nodeName === "SECTION" && (node as HTMLElement).classList.contains("include-note")) {
return (node as HTMLElement).outerHTML;
blankReplacement(_content, node) {
if (node.nodeName === "SECTION" && node.classList.contains("include-note")) {
return node.outerHTML;
}
// Original implementation as per https://github.com/mixmark-io/turndown/blob/master/src/turndown.js.
return ("isBlock" in node && node.isBlock) ? '\n\n' : ''
}
return ("isBlock" in node && node.isBlock) ? '\n\n' : '';
},
});
// Filter is heavily based on: https://github.com/mixmark-io/turndown/issues/274#issuecomment-458730974
instance.addRule("fencedCodeBlock", fencedCodeBlockFilter);
@ -59,7 +59,7 @@ function toMarkdown(content: string) {
instance.keep([ "kbd", "sup", "sub" ]);
}
return instance.turndown(content);
return instance.render(content);
}
function rewriteLanguageTag(source: string) {
@ -85,14 +85,14 @@ function buildImageFilter() {
const ESCAPE_PATTERNS = {
before: /([\\*`[\]_]|(?:^[-+>])|(?:^~~~)|(?:^#{1-6}))/g,
after: /((?:^\d+(?=\.)))/
}
};
const escapePattern = new RegExp('(?:' + ESCAPE_PATTERNS.before.source + '|' + ESCAPE_PATTERNS.after.source + ')', 'g');
const escapePattern = new RegExp(`(?:${ESCAPE_PATTERNS.before.source}|${ESCAPE_PATTERNS.after.source})`, 'g');
function escapeMarkdown (content: string) {
return content.replace(escapePattern, function (match, before, after) {
return before ? '\\' + before : after + '\\'
})
return content.replace(escapePattern, (match, before, after) => {
return before ? `\\${before}` : `${after}\\`;
});
}
function escapeLinkDestination(destination: string) {
@ -102,10 +102,10 @@ function buildImageFilter() {
}
function escapeLinkTitle (title: string) {
return title.replace(/"/g, '\\"')
return title.replace(/"/g, '\\"');
}
const imageFilter: TurndownService.Rule = {
const imageFilter: Rule = {
filter: "img",
replacement(content, _node) {
const node = _node as HTMLElement;
@ -117,12 +117,12 @@ function buildImageFilter() {
// TODO: Deduplicate with upstream.
const untypedNode = (node as any);
const alt = escapeMarkdown(cleanAttribute(untypedNode.getAttribute('alt')))
const src = escapeLinkDestination(untypedNode.getAttribute('src') || '')
const title = cleanAttribute(untypedNode.getAttribute('title'))
const titlePart = title ? ' "' + escapeLinkTitle(title) + '"' : ''
const alt = escapeMarkdown(cleanAttribute(untypedNode.getAttribute('alt')));
const src = escapeLinkDestination(untypedNode.getAttribute('src') || '');
const title = cleanAttribute(untypedNode.getAttribute('title'));
const titlePart = title ? ` "${escapeLinkTitle(title)}"` : '';
return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : ''
return src ? `![${alt}](${src}${titlePart})` : '';
}
};
return imageFilter;
@ -151,7 +151,7 @@ function buildAdmonitionFilter() {
return DEFAULT_ADMONITION_TYPE;
}
const admonitionFilter: TurndownService.Rule = {
const admonitionFilter: Rule = {
filter(node, options) {
return node.nodeName === "ASIDE" && node.classList.contains("admonition");
},
@ -161,11 +161,11 @@ function buildAdmonitionFilter() {
content = content.replace(/^\n+|\n+$/g, '');
content = content.replace(/^/gm, '> ');
content = `> [!${admonitionType}]\n` + content;
content = `> [!${admonitionType}]\n${content}`;
return "\n\n" + content + "\n\n";
return `\n\n${content}\n\n`;
}
}
};
return admonitionFilter;
}
@ -178,15 +178,15 @@ function buildAdmonitionFilter() {
*/
function buildInlineLinkFilter(): Rule {
return {
filter: function (node, options) {
filter (node, options) {
return (
options.linkStyle === 'inlined' &&
node.nodeName === 'A' &&
!!node.getAttribute('href')
)
);
},
replacement: function (content, _node) {
replacement (content, _node) {
const node = _node as HTMLElement;
// Return reference links verbatim.
@ -196,13 +196,13 @@ function buildInlineLinkFilter(): Rule {
// Otherwise treat as normal.
// TODO: Call super() somehow instead of duplicating the implementation.
let href = node.getAttribute('href')
if (href) href = href.replace(/([()])/g, '\\$1')
let title = cleanAttribute(node.getAttribute('title'))
if (title) title = ' "' + title.replace(/"/g, '\\"') + '"'
return '[' + content + '](' + href + title + ')'
let href = node.getAttribute('href');
if (href) href = href.replace(/([()])/g, '\\$1');
let title = cleanAttribute(node.getAttribute('title'));
if (title) title = ` "${title.replace(/"/g, '\\"')}"`;
return `[${content}](${href}${title})`;
}
}
};
}
function buildFigureFilter(): Rule {
@ -214,7 +214,7 @@ function buildFigureFilter(): Rule {
replacement(content, node) {
return (node as HTMLElement).outerHTML;
}
}
};
}
// Keep in line with https://github.com/mixmark-io/turndown/blob/master/src/commonmark-rules.js.
@ -224,13 +224,13 @@ function buildListItemFilter(): Rule {
replacement(content, node, options) {
content = content
.trim()
.replace(/\n/gm, '\n ') // indent
let prefix = options.bulletListMarker + ' '
.replace(/\n/gm, '\n '); // indent
let prefix = `${options.bulletListMarker} `;
const parent = node.parentNode as HTMLElement;
if (parent.nodeName === 'OL') {
var start = parent.getAttribute('start')
var index = Array.prototype.indexOf.call(parent.children, node)
prefix = (start ? Number(start) + index : index + 1) + '. '
const start = parent.getAttribute('start');
const index = Array.prototype.indexOf.call(parent.children, node);
prefix = `${start ? Number(start) + index : index + 1}. `;
} else if (parent.classList.contains("todo-list")) {
const isChecked = node.querySelector("input[type=checkbox]:checked");
prefix = (isChecked ? "- [x] " : "- [ ] ");
@ -239,7 +239,7 @@ function buildListItemFilter(): Rule {
const result = prefix + content + (node.nextSibling && !/\n$/.test(content) ? '\n' : '');
return result;
}
}
};
}
function buildMathFilter(): Rule {
@ -270,13 +270,13 @@ function buildMathFilter(): Rule {
// Unknown.
return content;
}
}
};
}
// Taken from upstream since it's not exposed.
// https://github.com/mixmark-io/turndown/blob/master/src/commonmark-rules.js
function cleanAttribute(attribute: string | null | undefined) {
return attribute ? attribute.replace(/(\n+\s*)+/g, '\n') : ''
return attribute ? attribute.replace(/(\n+\s*)+/g, '\n') : '';
}
export default {

View File

@ -314,4 +314,9 @@ $$`;
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
});
it("preserves HTML entities in list", () => {
const input = `* &lt;note&gt; is note.`;
const expected = /*html*/`<ul><li>&lt;note&gt; is note.</li></ul>`;
expect(markdownService.renderToHtml(input, "Title")).toStrictEqual(expected);
});
});

View File

@ -1,5 +1,5 @@
# Documentation
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/fX56iuus8ySW/Documentation_image.png" width="205" height="162">
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/Fgny4aXwFFgV/Documentation_image.png" width="205" height="162">
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.

View File

@ -18,5 +18,5 @@
* [Max content width is not respected when switching between note types in the same tab](https://github.com/TriliumNext/Trilium/issues/8065)
* [Crash When a Note Includes Itself](https://github.com/TriliumNext/Trilium/issues/8294)
* [Severe Performance Degradation and Crash Issues Due to Recursive Inclusion in Included Notes](https://github.com/TriliumNext/Trilium/issues/8017)
* [<note> is not a launcher even though it's in the launcher subtree](https://github.com/TriliumNext/Trilium/issues/8218)
* [\<note\> is not a launcher even though it's in the launcher subtree](https://github.com/TriliumNext/Trilium/issues/8218)
* [Archived subnotes of direct children appear in grid view without #includeArchived](https://github.com/TriliumNext/Trilium/issues/8184)

View File

@ -6169,6 +6169,20 @@
"type": "text",
"mime": "text/markdown",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "dj3j8dG4th4l",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "wy8So3yZZlH9",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "shareAlias",
@ -6182,20 +6196,6 @@
"value": "bx bx-window-open",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "dj3j8dG4th4l",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "wy8So3yZZlH9",
"isInheritable": false,
"position": 50
}
],
"format": "markdown",

View File

@ -28,4 +28,4 @@ Sorting is done by comparing note properties or specific labels on child notes.
* **Label Sorting**: If `#sorted` has any other value, this value is treated as the name of a child note's label, and sorting is based on the values of this label. For example, setting `#sorted=myOrder` on the parent note and using `#myOrder=001`, `#myOrder=002`, etc., on child notes.
4. **Alphabetical Sorting**: Used as a last resort when other criteria result in equality.
All comparisons are made string-wise (e.g., "1" < "2" or "2020-10-10" < "2021-01-15", but also "2" > "10").
All comparisons are made string-wise (e.g., "1" \< "2" or "2020-10-10" < "2021-01-15", but also "2" \> "10").

75
pnpm-lock.yaml generated
View File

@ -792,9 +792,9 @@ importers:
tmp:
specifier: 0.2.5
version: 0.2.5
turndown:
specifier: 7.2.2
version: 7.2.2
turnish:
specifier: 1.7.1
version: 1.7.1
unescape:
specifier: 1.0.1
version: 1.0.1
@ -1461,6 +1461,9 @@ importers:
packages:
'@adobe/css-tools@4.4.4':
resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
@ -13466,6 +13469,9 @@ packages:
turndown@7.2.2:
resolution: {integrity: sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ==}
turnish@1.7.1:
resolution: {integrity: sha512-NgyY7pIDABjKyg2isRgZyFPav6tOyvmqpTx3HROsKrOaE3JccP4C1P2IhAtkAZ8DkQb/O1R7HOFAkxY8uaJmcQ==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -14407,6 +14413,8 @@ packages:
snapshots:
'@adobe/css-tools@4.4.4': {}
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@ -15078,6 +15086,8 @@ snapshots:
'@ckeditor/ckeditor5-core': 47.4.0
'@ckeditor/ckeditor5-upload': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-ai@47.4.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
dependencies:
@ -15218,12 +15228,16 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-cloud-services@47.4.0':
dependencies:
'@ckeditor/ckeditor5-core': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-code-block@47.4.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
dependencies:
@ -15416,6 +15430,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-editor-classic@47.4.0':
dependencies:
@ -15425,6 +15441,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-editor-decoupled@47.4.0':
dependencies:
@ -15434,6 +15452,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-editor-inline@47.4.0':
dependencies:
@ -15467,8 +15487,6 @@ snapshots:
'@ckeditor/ckeditor5-table': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-emoji@47.4.0':
dependencies:
@ -15525,8 +15543,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-export-word@47.4.0':
dependencies:
@ -15551,6 +15567,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-font@47.4.0':
dependencies:
@ -15625,6 +15643,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-html-embed@47.4.0':
dependencies:
@ -15670,8 +15690,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-import-word@47.4.0':
dependencies:
@ -15684,8 +15702,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-indent@47.4.0':
dependencies:
@ -15697,8 +15713,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-inspector@5.0.0': {}
@ -15708,8 +15722,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-line-height@47.4.0':
dependencies:
@ -15734,8 +15746,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-list-multi-level@47.4.0':
dependencies:
@ -15759,8 +15769,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-markdown-gfm@47.4.0':
dependencies:
@ -15798,8 +15806,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-mention@47.4.0(patch_hash=5981fb59ba35829e4dff1d39cf771000f8a8fdfa7a34b51d8af9549541f2d62d)':
dependencies:
@ -15809,8 +15815,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-merge-fields@47.4.0':
dependencies:
@ -15823,8 +15827,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-minimap@47.4.0':
dependencies:
@ -15833,8 +15835,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-operations-compressor@47.4.0':
dependencies:
@ -15889,8 +15889,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-pagination@47.4.0':
dependencies:
@ -15998,8 +15996,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-slash-command@47.4.0':
dependencies:
@ -16012,8 +16008,6 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-source-editing-enhanced@47.4.0':
dependencies:
@ -16061,8 +16055,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-table@47.4.0':
dependencies:
@ -16075,8 +16067,6 @@ snapshots:
'@ckeditor/ckeditor5-widget': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-template@47.4.0':
dependencies:
@ -16189,8 +16179,6 @@ snapshots:
'@ckeditor/ckeditor5-engine': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-widget@47.4.0':
dependencies:
@ -16210,8 +16198,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@codemirror/autocomplete@6.18.6':
dependencies:
@ -21678,8 +21664,6 @@ snapshots:
ckeditor5-collaboration@47.4.0:
dependencies:
'@ckeditor/ckeditor5-collaboration-core': 47.4.0
transitivePeerDependencies:
- supports-color
ckeditor5-premium-features@47.4.0(bufferutil@4.0.9)(ckeditor5@47.4.0)(utf-8-validate@6.0.5):
dependencies:
@ -30075,6 +30059,11 @@ snapshots:
dependencies:
'@mixmark-io/domino': 2.2.0
turnish@1.7.1:
dependencies:
'@adobe/css-tools': 4.4.4
'@mixmark-io/domino': 2.2.0
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1