mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-03 14:41:38 +08:00 
			
		
		
		
	feat(ckeditor): fallback to GPL if license key fails
This commit is contained in:
		
							parent
							
								
									e280968271
								
							
						
					
					
						commit
						0325bee425
					
				@ -4,25 +4,64 @@ import { buildExtraCommands, type EditorConfig } from "@triliumnext/ckeditor5";
 | 
			
		||||
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
 | 
			
		||||
import options from "../../../services/options.js";
 | 
			
		||||
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
 | 
			
		||||
import utils from "../../../services/utils.js";
 | 
			
		||||
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
 | 
			
		||||
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
 | 
			
		||||
import getTemplates from "./snippets.js";
 | 
			
		||||
import { PREMIUM_PLUGINS } from "../../../../../../packages/ckeditor5/src/plugins.js";
 | 
			
		||||
import { t } from "../../../services/i18n.js";
 | 
			
		||||
import { getMermaidConfig } from "../../../services/mermaid.js";
 | 
			
		||||
import noteAutocompleteService, { type Suggestion } from "../../../services/note_autocomplete.js";
 | 
			
		||||
import mimeTypesService from "../../../services/mime_types.js";
 | 
			
		||||
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
 | 
			
		||||
import { buildToolbarConfig } from "./toolbar.js";
 | 
			
		||||
 | 
			
		||||
const OPEN_SOURCE_LICENSE_KEY = "GPL";
 | 
			
		||||
export const OPEN_SOURCE_LICENSE_KEY = "GPL";
 | 
			
		||||
 | 
			
		||||
const TEXT_FORMATTING_GROUP = {
 | 
			
		||||
    label: "Text formatting",
 | 
			
		||||
    icon: "text"
 | 
			
		||||
};
 | 
			
		||||
export interface BuildEditorOptions {
 | 
			
		||||
    forceGplLicense: boolean;
 | 
			
		||||
    isClassicEditor: boolean;
 | 
			
		||||
    contentLanguage: string | null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function buildConfig(): Promise<EditorConfig> {
 | 
			
		||||
    const licenseKey = getLicenseKey();
 | 
			
		||||
export async function buildConfig(opts: BuildEditorOptions): Promise<EditorConfig> {
 | 
			
		||||
    const licenseKey = (opts.forceGplLicense ? OPEN_SOURCE_LICENSE_KEY : getLicenseKey());
 | 
			
		||||
    const hasPremiumLicense = (licenseKey !== OPEN_SOURCE_LICENSE_KEY);
 | 
			
		||||
 | 
			
		||||
    const config: EditorConfig = {
 | 
			
		||||
        licenseKey,
 | 
			
		||||
        placeholder: t("editable_text.placeholder"),
 | 
			
		||||
        mention: {
 | 
			
		||||
            feeds: [
 | 
			
		||||
                {
 | 
			
		||||
                    marker: "@",
 | 
			
		||||
                    feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
 | 
			
		||||
                    itemRenderer: (item) => {
 | 
			
		||||
                        const itemElement = document.createElement("button");
 | 
			
		||||
 | 
			
		||||
                        itemElement.innerHTML = `${(item as Suggestion).highlightedNotePathTitle} `;
 | 
			
		||||
 | 
			
		||||
                        return itemElement;
 | 
			
		||||
                    },
 | 
			
		||||
                    minimumCharacters: 0
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        codeBlock: {
 | 
			
		||||
            languages: buildListOfLanguages()
 | 
			
		||||
        },
 | 
			
		||||
        math: {
 | 
			
		||||
            engine: "katex",
 | 
			
		||||
            outputType: "span", // or script
 | 
			
		||||
            lazyLoad: async () => {
 | 
			
		||||
                (window as any).katex = (await import("../../../services/math.js")).default
 | 
			
		||||
            },
 | 
			
		||||
            forceOutputType: false, // forces output to use outputType
 | 
			
		||||
            enablePreview: true // Enable preview view
 | 
			
		||||
        },
 | 
			
		||||
        mermaid: {
 | 
			
		||||
            lazyLoad: async () => (await import("mermaid")).default, // FIXME
 | 
			
		||||
            config: getMermaidConfig()
 | 
			
		||||
        },
 | 
			
		||||
        image: {
 | 
			
		||||
            styles: {
 | 
			
		||||
                options: [
 | 
			
		||||
@ -137,10 +176,22 @@ export async function buildConfig(): Promise<EditorConfig> {
 | 
			
		||||
        template: {
 | 
			
		||||
            definitions: await getTemplates()
 | 
			
		||||
        },
 | 
			
		||||
        htmlSupport: {
 | 
			
		||||
            allow: JSON.parse(options.get("allowedHtmlTags"))
 | 
			
		||||
        },
 | 
			
		||||
        // This value must be kept in sync with the language defined in webpack.config.js.
 | 
			
		||||
        language: "en"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Set up content language.
 | 
			
		||||
    const { contentLanguage } = opts;
 | 
			
		||||
    if (contentLanguage) {
 | 
			
		||||
        config.language = {
 | 
			
		||||
            ui: (typeof config.language === "string" ? config.language : "en"),
 | 
			
		||||
            content: contentLanguage
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Enable premium plugins.
 | 
			
		||||
    if (hasPremiumLicense) {
 | 
			
		||||
        config.extraPlugins = [
 | 
			
		||||
@ -148,149 +199,28 @@ export async function buildConfig(): Promise<EditorConfig> {
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildToolbarConfig(isClassicToolbar: boolean) {
 | 
			
		||||
    if (utils.isMobile()) {
 | 
			
		||||
        return buildMobileToolbar();
 | 
			
		||||
    } else if (isClassicToolbar) {
 | 
			
		||||
        const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
 | 
			
		||||
        return buildClassicToolbar(multilineToolbar);
 | 
			
		||||
    } else {
 | 
			
		||||
        return buildFloatingToolbar();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildMobileToolbar() {
 | 
			
		||||
    const classicConfig = buildClassicToolbar(false);
 | 
			
		||||
    const items: string[] = [];
 | 
			
		||||
 | 
			
		||||
    for (const item of classicConfig.toolbar.items) {
 | 
			
		||||
        if (typeof item === "object" && "items" in item) {
 | 
			
		||||
            for (const subitem of item.items) {
 | 
			
		||||
                items.push(subitem);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            items.push(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        ...classicConfig,
 | 
			
		||||
        toolbar: {
 | 
			
		||||
            ...classicConfig.toolbar,
 | 
			
		||||
            items
 | 
			
		||||
        }
 | 
			
		||||
        ...config,
 | 
			
		||||
        ...buildToolbarConfig(opts.isClassicEditor)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildClassicToolbar(multilineToolbar: boolean) {
 | 
			
		||||
    // For nested toolbars, refer to https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/toolbar.html#grouping-toolbar-items-in-dropdowns-nested-toolbars.
 | 
			
		||||
    return {
 | 
			
		||||
        toolbar: {
 | 
			
		||||
            items: [
 | 
			
		||||
                "heading",
 | 
			
		||||
                "fontSize",
 | 
			
		||||
                "|",
 | 
			
		||||
                "bold",
 | 
			
		||||
                "italic",
 | 
			
		||||
                {
 | 
			
		||||
                    ...TEXT_FORMATTING_GROUP,
 | 
			
		||||
                    items: ["underline", "strikethrough", "|", "superscript", "subscript", "|", "kbd"]
 | 
			
		||||
                },
 | 
			
		||||
                "|",
 | 
			
		||||
                "fontColor",
 | 
			
		||||
                "fontBackgroundColor",
 | 
			
		||||
                "removeFormat",
 | 
			
		||||
                "|",
 | 
			
		||||
                "bulletedList",
 | 
			
		||||
                "numberedList",
 | 
			
		||||
                "todoList",
 | 
			
		||||
                "|",
 | 
			
		||||
                "blockQuote",
 | 
			
		||||
                "admonition",
 | 
			
		||||
                "insertTable",
 | 
			
		||||
                "|",
 | 
			
		||||
                "code",
 | 
			
		||||
                "codeBlock",
 | 
			
		||||
                "|",
 | 
			
		||||
                "footnote",
 | 
			
		||||
                {
 | 
			
		||||
                    label: "Insert",
 | 
			
		||||
                    icon: "plus",
 | 
			
		||||
                    items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
 | 
			
		||||
                },
 | 
			
		||||
                "|",
 | 
			
		||||
                "alignment",
 | 
			
		||||
                "outdent",
 | 
			
		||||
                "indent",
 | 
			
		||||
                "|",
 | 
			
		||||
                "insertTemplate",
 | 
			
		||||
                "markdownImport",
 | 
			
		||||
                "cuttonote",
 | 
			
		||||
                "findAndReplace"
 | 
			
		||||
            ],
 | 
			
		||||
            shouldNotGroupWhenFull: multilineToolbar
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
function buildListOfLanguages() {
 | 
			
		||||
    const userLanguages = mimeTypesService
 | 
			
		||||
        .getMimeTypes()
 | 
			
		||||
        .filter((mt) => mt.enabled)
 | 
			
		||||
        .map((mt) => ({
 | 
			
		||||
            language: normalizeMimeTypeForCKEditor(mt.mime),
 | 
			
		||||
            label: mt.title
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
export function buildFloatingToolbar() {
 | 
			
		||||
    return {
 | 
			
		||||
        toolbar: {
 | 
			
		||||
            items: [
 | 
			
		||||
                "fontSize",
 | 
			
		||||
                "bold",
 | 
			
		||||
                "italic",
 | 
			
		||||
                "underline",
 | 
			
		||||
    return [
 | 
			
		||||
        {
 | 
			
		||||
                    ...TEXT_FORMATTING_GROUP,
 | 
			
		||||
                    items: [ "strikethrough", "|", "superscript", "subscript", "|", "kbd" ]
 | 
			
		||||
            language: mimeTypesService.MIME_TYPE_AUTO,
 | 
			
		||||
            label: t("editable-text.auto-detect-language")
 | 
			
		||||
        },
 | 
			
		||||
                "|",
 | 
			
		||||
                "fontColor",
 | 
			
		||||
                "fontBackgroundColor",
 | 
			
		||||
                "|",
 | 
			
		||||
                "code",
 | 
			
		||||
                "link",
 | 
			
		||||
                "bookmark",
 | 
			
		||||
                "removeFormat",
 | 
			
		||||
                "internallink",
 | 
			
		||||
                "cuttonote"
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        blockToolbar: [
 | 
			
		||||
            "heading",
 | 
			
		||||
            "|",
 | 
			
		||||
            "bulletedList",
 | 
			
		||||
            "numberedList",
 | 
			
		||||
            "todoList",
 | 
			
		||||
            "|",
 | 
			
		||||
            "blockQuote",
 | 
			
		||||
            "admonition",
 | 
			
		||||
            "codeBlock",
 | 
			
		||||
            "insertTable",
 | 
			
		||||
            "footnote",
 | 
			
		||||
            {
 | 
			
		||||
                label: "Insert",
 | 
			
		||||
                icon: "plus",
 | 
			
		||||
                items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
 | 
			
		||||
            },
 | 
			
		||||
            "|",
 | 
			
		||||
            "alignment",
 | 
			
		||||
            "outdent",
 | 
			
		||||
            "indent",
 | 
			
		||||
            "|",
 | 
			
		||||
            "insertTemplate",
 | 
			
		||||
            "imageUpload",
 | 
			
		||||
            "markdownImport",
 | 
			
		||||
            "specialCharacters",
 | 
			
		||||
            "emoji",
 | 
			
		||||
            "findAndReplace"
 | 
			
		||||
        ]
 | 
			
		||||
    };
 | 
			
		||||
        ...userLanguages
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getLicenseKey() {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { describe, expect, it } from "vitest";
 | 
			
		||||
import { buildClassicToolbar, buildFloatingToolbar } from "./config.js";
 | 
			
		||||
import { buildClassicToolbar, buildFloatingToolbar } from "./toolbar.js";
 | 
			
		||||
 | 
			
		||||
type ToolbarConfig = string | "|" | { items: ToolbarConfig[] };
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										149
									
								
								apps/client/src/widgets/type_widgets/ckeditor/toolbar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								apps/client/src/widgets/type_widgets/ckeditor/toolbar.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,149 @@
 | 
			
		||||
import utils from "../../../services/utils.js";
 | 
			
		||||
import options from "../../../services/options.js";
 | 
			
		||||
 | 
			
		||||
const TEXT_FORMATTING_GROUP = {
 | 
			
		||||
    label: "Text formatting",
 | 
			
		||||
    icon: "text"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function buildToolbarConfig(isClassicToolbar: boolean) {
 | 
			
		||||
    if (utils.isMobile()) {
 | 
			
		||||
        return buildMobileToolbar();
 | 
			
		||||
    } else if (isClassicToolbar) {
 | 
			
		||||
        const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
 | 
			
		||||
        return buildClassicToolbar(multilineToolbar);
 | 
			
		||||
    } else {
 | 
			
		||||
        return buildFloatingToolbar();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildMobileToolbar() {
 | 
			
		||||
    const classicConfig = buildClassicToolbar(false);
 | 
			
		||||
    const items: string[] = [];
 | 
			
		||||
 | 
			
		||||
    for (const item of classicConfig.toolbar.items) {
 | 
			
		||||
        if (typeof item === "object" && "items" in item) {
 | 
			
		||||
            for (const subitem of item.items) {
 | 
			
		||||
                items.push(subitem);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            items.push(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        ...classicConfig,
 | 
			
		||||
        toolbar: {
 | 
			
		||||
            ...classicConfig.toolbar,
 | 
			
		||||
            items
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildClassicToolbar(multilineToolbar: boolean) {
 | 
			
		||||
    // For nested toolbars, refer to https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/toolbar.html#grouping-toolbar-items-in-dropdowns-nested-toolbars.
 | 
			
		||||
    return {
 | 
			
		||||
        toolbar: {
 | 
			
		||||
            items: [
 | 
			
		||||
                "heading",
 | 
			
		||||
                "fontSize",
 | 
			
		||||
                "|",
 | 
			
		||||
                "bold",
 | 
			
		||||
                "italic",
 | 
			
		||||
                {
 | 
			
		||||
                    ...TEXT_FORMATTING_GROUP,
 | 
			
		||||
                    items: ["underline", "strikethrough", "|", "superscript", "subscript", "|", "kbd"]
 | 
			
		||||
                },
 | 
			
		||||
                "|",
 | 
			
		||||
                "fontColor",
 | 
			
		||||
                "fontBackgroundColor",
 | 
			
		||||
                "removeFormat",
 | 
			
		||||
                "|",
 | 
			
		||||
                "bulletedList",
 | 
			
		||||
                "numberedList",
 | 
			
		||||
                "todoList",
 | 
			
		||||
                "|",
 | 
			
		||||
                "blockQuote",
 | 
			
		||||
                "admonition",
 | 
			
		||||
                "insertTable",
 | 
			
		||||
                "|",
 | 
			
		||||
                "code",
 | 
			
		||||
                "codeBlock",
 | 
			
		||||
                "|",
 | 
			
		||||
                "footnote",
 | 
			
		||||
                {
 | 
			
		||||
                    label: "Insert",
 | 
			
		||||
                    icon: "plus",
 | 
			
		||||
                    items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
 | 
			
		||||
                },
 | 
			
		||||
                "|",
 | 
			
		||||
                "alignment",
 | 
			
		||||
                "outdent",
 | 
			
		||||
                "indent",
 | 
			
		||||
                "|",
 | 
			
		||||
                "insertTemplate",
 | 
			
		||||
                "markdownImport",
 | 
			
		||||
                "cuttonote",
 | 
			
		||||
                "findAndReplace"
 | 
			
		||||
            ],
 | 
			
		||||
            shouldNotGroupWhenFull: multilineToolbar
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function buildFloatingToolbar() {
 | 
			
		||||
    return {
 | 
			
		||||
        toolbar: {
 | 
			
		||||
            items: [
 | 
			
		||||
                "fontSize",
 | 
			
		||||
                "bold",
 | 
			
		||||
                "italic",
 | 
			
		||||
                "underline",
 | 
			
		||||
                {
 | 
			
		||||
                    ...TEXT_FORMATTING_GROUP,
 | 
			
		||||
                    items: [ "strikethrough", "|", "superscript", "subscript", "|", "kbd" ]
 | 
			
		||||
                },
 | 
			
		||||
                "|",
 | 
			
		||||
                "fontColor",
 | 
			
		||||
                "fontBackgroundColor",
 | 
			
		||||
                "|",
 | 
			
		||||
                "code",
 | 
			
		||||
                "link",
 | 
			
		||||
                "bookmark",
 | 
			
		||||
                "removeFormat",
 | 
			
		||||
                "internallink",
 | 
			
		||||
                "cuttonote"
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        blockToolbar: [
 | 
			
		||||
            "heading",
 | 
			
		||||
            "|",
 | 
			
		||||
            "bulletedList",
 | 
			
		||||
            "numberedList",
 | 
			
		||||
            "todoList",
 | 
			
		||||
            "|",
 | 
			
		||||
            "blockQuote",
 | 
			
		||||
            "admonition",
 | 
			
		||||
            "codeBlock",
 | 
			
		||||
            "insertTable",
 | 
			
		||||
            "footnote",
 | 
			
		||||
            {
 | 
			
		||||
                label: "Insert",
 | 
			
		||||
                icon: "plus",
 | 
			
		||||
                items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
 | 
			
		||||
            },
 | 
			
		||||
            "|",
 | 
			
		||||
            "alignment",
 | 
			
		||||
            "outdent",
 | 
			
		||||
            "indent",
 | 
			
		||||
            "|",
 | 
			
		||||
            "insertTemplate",
 | 
			
		||||
            "imageUpload",
 | 
			
		||||
            "markdownImport",
 | 
			
		||||
            "specialCharacters",
 | 
			
		||||
            "emoji",
 | 
			
		||||
            "findAndReplace"
 | 
			
		||||
        ]
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,3 @@
 | 
			
		||||
import { t } from "../../services/i18n.js";
 | 
			
		||||
import noteAutocompleteService, { type Suggestion } from "../../services/note_autocomplete.js";
 | 
			
		||||
import mimeTypesService from "../../services/mime_types.js";
 | 
			
		||||
import utils, { hasTouchBar } from "../../services/utils.js";
 | 
			
		||||
import keyboardActionService from "../../services/keyboard_actions.js";
 | 
			
		||||
import froca from "../../services/froca.js";
 | 
			
		||||
@ -12,29 +9,12 @@ import dialogService from "../../services/dialog.js";
 | 
			
		||||
import options from "../../services/options.js";
 | 
			
		||||
import toast from "../../services/toast.js";
 | 
			
		||||
import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
 | 
			
		||||
import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js";
 | 
			
		||||
import { buildConfig, BuildEditorOptions, OPEN_SOURCE_LICENSE_KEY } from "./ckeditor/config.js";
 | 
			
		||||
import type FNote from "../../entities/fnote.js";
 | 
			
		||||
import { getMermaidConfig } from "../../services/mermaid.js";
 | 
			
		||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig } from "@triliumnext/ckeditor5";
 | 
			
		||||
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
 | 
			
		||||
import "@triliumnext/ckeditor5/index.css";
 | 
			
		||||
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
 | 
			
		||||
import { updateTemplateCache } from "./ckeditor/snippets.js";
 | 
			
		||||
 | 
			
		||||
const mentionSetup: MentionFeed[] = [
 | 
			
		||||
    {
 | 
			
		||||
        marker: "@",
 | 
			
		||||
        feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
 | 
			
		||||
        itemRenderer: (item) => {
 | 
			
		||||
            const itemElement = document.createElement("button");
 | 
			
		||||
 | 
			
		||||
            itemElement.innerHTML = `${(item as Suggestion).highlightedNotePathTitle} `;
 | 
			
		||||
 | 
			
		||||
            return itemElement;
 | 
			
		||||
        },
 | 
			
		||||
        minimumCharacters: 0
 | 
			
		||||
    }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const TPL = /*html*/`
 | 
			
		||||
<div class="note-detail-editable-text note-detail-printable">
 | 
			
		||||
    <style>
 | 
			
		||||
@ -97,24 +77,6 @@ const TPL = /*html*/`
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
function buildListOfLanguages() {
 | 
			
		||||
    const userLanguages = mimeTypesService
 | 
			
		||||
        .getMimeTypes()
 | 
			
		||||
        .filter((mt) => mt.enabled)
 | 
			
		||||
        .map((mt) => ({
 | 
			
		||||
            language: normalizeMimeTypeForCKEditor(mt.mime),
 | 
			
		||||
            label: mt.title
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
    return [
 | 
			
		||||
        {
 | 
			
		||||
            language: mimeTypesService.MIME_TYPE_AUTO,
 | 
			
		||||
            label: t("editable-text.auto-detect-language")
 | 
			
		||||
        },
 | 
			
		||||
        ...userLanguages
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The editor can operate into two distinct modes:
 | 
			
		||||
 *
 | 
			
		||||
@ -147,7 +109,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
 | 
			
		||||
 | 
			
		||||
    async initEditor() {
 | 
			
		||||
        const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic";
 | 
			
		||||
        const editorClass = isClassicEditor ? ClassicEditor : PopupEditor;
 | 
			
		||||
 | 
			
		||||
        // CKEditor since version 12 needs the element to be visible before initialization. At the same time,
 | 
			
		||||
        // we want to avoid flicker - i.e., show editor only once everything is ready. That's why we have separate
 | 
			
		||||
@ -192,33 +153,15 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
 | 
			
		||||
        this.watchdog.setCreator(async (_, editorConfig) => {
 | 
			
		||||
            logInfo("Creating new CKEditor");
 | 
			
		||||
 | 
			
		||||
            const finalConfig = {
 | 
			
		||||
                ...editorConfig,
 | 
			
		||||
                ...(await buildConfig()),
 | 
			
		||||
                ...buildToolbarConfig(isClassicEditor),
 | 
			
		||||
                htmlSupport: {
 | 
			
		||||
                    allow: JSON.parse(options.get("allowedHtmlTags")),
 | 
			
		||||
                    styles: true,
 | 
			
		||||
                    classes: true,
 | 
			
		||||
                    attributes: true
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const contentLanguage = this.note?.getLabelValue("language");
 | 
			
		||||
            if (contentLanguage) {
 | 
			
		||||
                // TODO: Wrong type?
 | 
			
		||||
                //@ts-ignore
 | 
			
		||||
                finalConfig.language = {
 | 
			
		||||
                    ui: (typeof finalConfig.language === "string" ? finalConfig.language : "en"),
 | 
			
		||||
                    content: contentLanguage
 | 
			
		||||
                }
 | 
			
		||||
                this.contentLanguage = contentLanguage;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.contentLanguage = null;
 | 
			
		||||
            }
 | 
			
		||||
            this.contentLanguage = contentLanguage ?? null;
 | 
			
		||||
 | 
			
		||||
            //@ts-ignore
 | 
			
		||||
            const editor = await editorClass.create(this.$editor[0], finalConfig);
 | 
			
		||||
            const opts: BuildEditorOptions = {
 | 
			
		||||
                contentLanguage: this.contentLanguage,
 | 
			
		||||
                forceGplLicense: false,
 | 
			
		||||
                isClassicEditor
 | 
			
		||||
            };
 | 
			
		||||
            const editor = await buildEditor(this.$editor[0], isClassicEditor, opts);
 | 
			
		||||
 | 
			
		||||
            const notificationsPlugin = editor.plugins.get("Notification");
 | 
			
		||||
            notificationsPlugin.on("show:warning", (evt, data) => {
 | 
			
		||||
@ -295,28 +238,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async createEditor() {
 | 
			
		||||
        await this.watchdog.create(this.$editor[0], {
 | 
			
		||||
            placeholder: t("editable_text.placeholder"),
 | 
			
		||||
            mention: {
 | 
			
		||||
                feeds: mentionSetup,
 | 
			
		||||
            },
 | 
			
		||||
            codeBlock: {
 | 
			
		||||
                languages: buildListOfLanguages()
 | 
			
		||||
            },
 | 
			
		||||
            math: {
 | 
			
		||||
                engine: "katex",
 | 
			
		||||
                outputType: "span", // or script
 | 
			
		||||
                lazyLoad: async () => {
 | 
			
		||||
                    (window as any).katex = (await import("../../services/math.js")).default
 | 
			
		||||
                },
 | 
			
		||||
                forceOutputType: false, // forces output to use outputType
 | 
			
		||||
                enablePreview: true // Enable preview view
 | 
			
		||||
            },
 | 
			
		||||
            mermaid: {
 | 
			
		||||
                lazyLoad: async () => (await import("mermaid")).default, // FIXME
 | 
			
		||||
                config: getMermaidConfig()
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        await this.watchdog.create(this.$editor[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async doRefresh(note: FNote) {
 | 
			
		||||
@ -655,3 +577,18 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function buildEditor(element: HTMLElement, isClassicEditor: boolean, opts: BuildEditorOptions) {
 | 
			
		||||
    const editorClass = isClassicEditor ? ClassicEditor : PopupEditor;
 | 
			
		||||
    let config = await buildConfig(opts);
 | 
			
		||||
    let editor = await editorClass.create(element, config);
 | 
			
		||||
 | 
			
		||||
    if (editor.isReadOnly) {
 | 
			
		||||
        editor.destroy();
 | 
			
		||||
 | 
			
		||||
        opts.forceGplLicense = true;
 | 
			
		||||
        config = await buildConfig(opts);
 | 
			
		||||
        editor = await editorClass.create(element, config);
 | 
			
		||||
    }
 | 
			
		||||
    return editor;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -97,6 +97,9 @@ export default defineConfig(() => ({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    test: {
 | 
			
		||||
        environment: "happy-dom"
 | 
			
		||||
    },
 | 
			
		||||
    optimizeDeps: {
 | 
			
		||||
        exclude: [
 | 
			
		||||
            "@triliumnext/highlightjs"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user