chore(ckeditor5): first integration into client (WIP)

This commit is contained in:
Elian Doran 2025-05-03 12:39:34 +03:00
parent ba67812101
commit e1af7eba93
No known key found for this signature in database
14 changed files with 93 additions and 134 deletions

View File

@ -22,6 +22,7 @@
"@mind-elixir/node-menu": "1.0.5", "@mind-elixir/node-menu": "1.0.5",
"@popperjs/core": "2.11.8", "@popperjs/core": "2.11.8",
"@triliumnext/commons": "workspace:*", "@triliumnext/commons": "workspace:*",
"@triliumnext/ckeditor5": "workspace:*",
"bootstrap": "5.3.5", "bootstrap": "5.3.5",
"dayjs": "1.11.13", "dayjs": "1.11.13",
"dayjs-plugin-utc": "0.1.2", "dayjs-plugin-utc": "0.1.2",

View File

@ -1,49 +0,0 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import { DecoupledEditor as DecoupledEditorBase } from '@ckeditor/ckeditor5-editor-decoupled';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Alignment } from '@ckeditor/ckeditor5-alignment';
import { FontSize, FontFamily, FontColor, FontBackgroundColor } from '@ckeditor/ckeditor5-font';
import { CKFinderUploadAdapter } from '@ckeditor/ckeditor5-adapter-ckfinder';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic, Strikethrough, Underline } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { CKBox } from '@ckeditor/ckeditor5-ckbox';
import { CKFinder } from '@ckeditor/ckeditor5-ckfinder';
import { EasyImage } from '@ckeditor/ckeditor5-easy-image';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Image, ImageCaption, ImageResize, ImageStyle, ImageToolbar, ImageUpload, PictureEditing } from '@ckeditor/ckeditor5-image';
import { Indent, IndentBlock } from '@ckeditor/ckeditor5-indent';
import { Link } from '@ckeditor/ckeditor5-link';
import { List, ListProperties } from '@ckeditor/ckeditor5-list';
import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office';
import { Table, TableToolbar } from '@ckeditor/ckeditor5-table';
import { TextTransformation } from '@ckeditor/ckeditor5-typing';
import { CloudServices } from '@ckeditor/ckeditor5-cloud-services';
export default class DecoupledEditor extends DecoupledEditorBase {
static builtinPlugins: (typeof TextTransformation | typeof Essentials | typeof Alignment | typeof FontBackgroundColor | typeof FontColor | typeof FontFamily | typeof FontSize | typeof CKFinderUploadAdapter | typeof Paragraph | typeof Heading | typeof Autoformat | typeof Bold | typeof Italic | typeof Strikethrough | typeof Underline | typeof BlockQuote | typeof Image | typeof ImageCaption | typeof ImageResize | typeof ImageStyle | typeof ImageToolbar | typeof ImageUpload | typeof CloudServices | typeof CKBox | typeof CKFinder | typeof EasyImage | typeof List | typeof ListProperties | typeof Indent | typeof IndentBlock | typeof Link | typeof MediaEmbed | typeof PasteFromOffice | typeof Table | typeof TableToolbar | typeof PictureEditing)[];
static defaultConfig: {
toolbar: {
items: string[];
};
image: {
resizeUnit: "px";
toolbar: string[];
};
table: {
contentToolbar: string[];
};
list: {
properties: {
styles: boolean;
startIndex: boolean;
reversed: boolean;
};
};
language: string;
};
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,10 +7,6 @@ export interface Library {
css?: string[]; css?: string[];
} }
const CKEDITOR: Library = {
js: ["libraries/ckeditor/ckeditor.js"]
};
const CODE_MIRROR: Library = { const CODE_MIRROR: Library = {
js: () => { js: () => {
const scriptsToLoad = [ const scriptsToLoad = [
@ -156,7 +152,6 @@ export default {
requireCss, requireCss,
requireLibrary, requireLibrary,
loadHighlightingTheme, loadHighlightingTheme,
CKEDITOR,
CODE_MIRROR, CODE_MIRROR,
KATEX, KATEX,
HIGHLIGHT_JS HIGHLIGHT_JS

View File

@ -4,7 +4,7 @@ import noteAutocompleteService from "../../services/note_autocomplete.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import contextMenuService from "../../menus/context_menu.js"; import contextMenuService from "../../menus/context_menu.js";
import attributeParser, { type Attribute } from "../../services/attribute_parser.js"; import attributeParser, { type Attribute } from "../../services/attribute_parser.js";
import libraryLoader from "../../services/library_loader.js"; import { BalloonEditor } from "@triliumnext/ckeditor5";
import froca from "../../services/froca.js"; import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js"; import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js"; import noteCreateService from "../../services/note_create.js";
@ -369,13 +369,11 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
} }
async initEditor() { async initEditor() {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
this.$widget.show(); this.$widget.show();
this.$editor.on("click", (e) => this.handleEditorClick(e)); this.$editor.on("click", (e) => this.handleEditorClick(e));
this.textEditor = await CKEditor.BalloonEditor.create(this.$editor[0], editorConfig); this.textEditor = BalloonEditor.create(this.$editor[0], editorConfig);
this.textEditor.model.document.on("change:data", () => this.dataChanged()); this.textEditor.model.document.on("change:data", () => this.dataChanged());
this.textEditor.editing.view.document.on( this.textEditor.editing.view.document.on(
"enter", "enter",

View File

@ -18,6 +18,7 @@ import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js"; import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js";
import type FNote from "../../entities/fnote.js"; import type FNote from "../../entities/fnote.js";
import { getMermaidConfig } from "../../services/mermaid.js"; import { getMermaidConfig } from "../../services/mermaid.js";
import { BalloonEditor, DecoupledEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
const ENABLE_INSPECTOR = false; const ENABLE_INSPECTOR = false;
@ -127,7 +128,7 @@ function buildListOfLanguages() {
export default class EditableTextTypeWidget extends AbstractTextTypeWidget { export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
private contentLanguage?: string | null; private contentLanguage?: string | null;
private watchdog!: CKWatchdog; private watchdog!: EditorWatchdog<DecoupledEditor | BalloonEditor>;
private $editor!: JQuery<HTMLElement>; private $editor!: JQuery<HTMLElement>;
@ -149,16 +150,15 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
} }
async initEditor() { async initEditor() {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic"; const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic";
const editorClass = isClassicEditor ? CKEditor.DecoupledEditor : CKEditor.BalloonEditor; const editorClass = isClassicEditor ? DecoupledEditor : BalloonEditor;
// CKEditor since version 12 needs the element to be visible before initialization. At the same time, // 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 // we want to avoid flicker - i.e., show editor only once everything is ready. That's why we have separate
// display of $widget in both branches. // display of $widget in both branches.
this.$widget.show(); this.$widget.show();
this.watchdog = new CKEditor.EditorWatchdog(editorClass, { this.watchdog = new EditorWatchdog<DecoupledEditor | BalloonEditor>(editorClass, {
// An average number of milliseconds between the last editor errors (defaults to 5000). // An average number of milliseconds between the last editor errors (defaults to 5000).
// When the period of time between errors is lower than that and the crashNumberLimit // When the period of time between errors is lower than that and the crashNumberLimit
// is also reached, the watchdog changes its state to crashedPermanently, and it stops // is also reached, the watchdog changes its state to crashedPermanently, and it stops
@ -189,7 +189,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (currentState === "crashedPermanently") { if (currentState === "crashedPermanently") {
dialogService.info(`Editing component keeps crashing. Please try restarting Trilium. If problem persists, consider creating a bug report.`); dialogService.info(`Editing component keeps crashing. Please try restarting Trilium. If problem persists, consider creating a bug report.`);
this.watchdog.editor.enableReadOnlyMode("crashed-editor"); this.watchdog.editor?.enableReadOnlyMode("crashed-editor");
} }
}); });
@ -210,6 +210,8 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const contentLanguage = this.note?.getLabelValue("language"); const contentLanguage = this.note?.getLabelValue("language");
if (contentLanguage) { if (contentLanguage) {
// TODO: Wrong type?
//@ts-ignore
finalConfig.language = { finalConfig.language = {
ui: (typeof finalConfig.language === "string" ? finalConfig.language : "en"), ui: (typeof finalConfig.language === "string" ? finalConfig.language : "en"),
content: contentLanguage content: contentLanguage
@ -219,6 +221,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.contentLanguage = null; this.contentLanguage = null;
} }
//@ts-ignore
const editor = await editorClass.create(elementOrData, finalConfig); const editor = await editorClass.create(elementOrData, finalConfig);
const notificationsPlugin = editor.plugins.get("Notification"); const notificationsPlugin = editor.plugins.get("Notification");
@ -235,6 +238,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
evt.stop(); evt.stop();
}); });
//@ts-ignore
await initSyntaxHighlighting(editor); await initSyntaxHighlighting(editor);
if (isClassicEditor) { if (isClassicEditor) {
@ -256,17 +260,18 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
// Reposition all dropdowns to point upwards instead of downwards. // Reposition all dropdowns to point upwards instead of downwards.
// See https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for more info. // See https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for more info.
const toolbarView = editor.ui.view.toolbar; const toolbarView = (editor as DecoupledEditor).ui.view.toolbar;
for (const item of toolbarView.items) { for (const item of toolbarView.items) {
if (!("panelView" in item)) { if (!("panelView" in item)) {
continue; continue;
} }
item.on("change:isOpen", () => { item.on("change:isOpen", () => {
if ( !item.isOpen ) { if (!("isOpen" in item) || !item.isOpen ) {
return; return;
} }
// @ts-ignore
item.panelView.position = item.panelView.position.replace("s", "n"); item.panelView.position = item.panelView.position.replace("s", "n");
}); });
} }
@ -277,14 +282,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (glob.isDev && ENABLE_INSPECTOR) { if (glob.isDev && ENABLE_INSPECTOR) {
// TODO: Check if this still works. // TODO: Check if this still works.
await import(/* webpackIgnore: true */ "../../../libraries/ckeditor/inspector.js"); // await import(/* webpackIgnore: true */ "../../../libraries/ckeditor/inspector.js");
CKEditorInspector.attach(editor); // CKEditorInspector.attach(editor);
} }
// Touch bar integration // Touch bar integration
if (hasTouchBar) { if (hasTouchBar) {
for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) { for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) {
editor.commands.get(event).on("change", () => this.triggerCommand("refreshTouchBar")); editor.commands.get(event)?.on("change", () => this.triggerCommand("refreshTouchBar"));
} }
} }
@ -297,6 +302,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async createEditor() { async createEditor() {
await this.watchdog.create(this.$editor[0], { await this.watchdog.create(this.$editor[0], {
placeholder: t("editable_text.placeholder"), placeholder: t("editable_text.placeholder"),
//@ts-ignore TODO: FIX TYPES
mention: mentionSetup, mention: mentionSetup,
codeBlock: { codeBlock: {
languages: buildListOfLanguages() languages: buildListOfLanguages()
@ -324,13 +330,13 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (this.contentLanguage !== newContentLanguage) { if (this.contentLanguage !== newContentLanguage) {
await this.reinitialize(data); await this.reinitialize(data);
} else { } else {
this.watchdog.editor.setData(data); this.watchdog.editor?.setData(data);
} }
}); });
} }
getData() { getData() {
const content = this.watchdog.editor.getData(); const content = this.watchdog.editor?.getData() ?? "";
// if content is only tags/whitespace (typically <p>&nbsp;</p>), then just make it empty, // if content is only tags/whitespace (typically <p>&nbsp;</p>), then just make it empty,
// this is important when setting a new note to code // this is important when setting a new note to code
@ -344,11 +350,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
} }
scrollToEnd() { scrollToEnd() {
this.watchdog?.editor.model.change((writer) => { this.watchdog?.editor?.model.change((writer) => {
writer.setSelection(writer.createPositionAt(this.watchdog?.editor.model.document.getRoot(), "end")); const rootItem = this.watchdog?.editor?.model.document.getRoot();
if (rootItem) {
writer.setSelection(writer.createPositionAt(rootItem, "end"));
}
}); });
this.watchdog?.editor.editing.view.focus(); this.watchdog?.editor?.editing.view.focus();
} }
show() { } show() { }
@ -360,7 +369,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
cleanup() { cleanup() {
if (this.watchdog?.editor) { if (this.watchdog?.editor) {
this.spacedUpdate.allowUpdateWithoutChange(() => { this.spacedUpdate.allowUpdateWithoutChange(() => {
this.watchdog.editor.setData(""); this.watchdog.editor?.setData("");
}); });
} }
} }
@ -375,18 +384,22 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async addLinkToEditor(linkHref: string, linkTitle: string) { async addLinkToEditor(linkHref: string, linkTitle: string) {
await this.initialized; await this.initialized;
this.watchdog.editor.model.change((writer) => { this.watchdog.editor?.model.change((writer) => {
const insertPosition = this.watchdog.editor.model.document.selection.getFirstPosition(); const insertPosition = this.watchdog.editor?.model.document.selection.getFirstPosition();
if (insertPosition) {
writer.insertText(linkTitle, { linkHref: linkHref }, insertPosition); writer.insertText(linkTitle, { linkHref: linkHref }, insertPosition);
}
}); });
} }
async addTextToEditor(text: string) { async addTextToEditor(text: string) {
await this.initialized; await this.initialized;
this.watchdog.editor.model.change((writer) => { this.watchdog.editor?.model.change((writer) => {
const insertPosition = this.watchdog.editor.model.document.selection.getLastPosition(); const insertPosition = this.watchdog.editor?.model.document.selection.getLastPosition();
if (insertPosition) {
writer.insertText(text, insertPosition); writer.insertText(text, insertPosition);
}
}); });
} }
@ -403,23 +416,23 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (linkTitle) { if (linkTitle) {
if (this.hasSelection()) { if (this.hasSelection()) {
this.watchdog.editor.execute("link", externalLink ? `${notePath}` : `#${notePath}`); this.watchdog.editor?.execute("link", externalLink ? `${notePath}` : `#${notePath}`);
} else { } else {
await this.addLinkToEditor(externalLink ? `${notePath}` : `#${notePath}`, linkTitle); await this.addLinkToEditor(externalLink ? `${notePath}` : `#${notePath}`, linkTitle);
} }
} else { } else {
this.watchdog.editor.execute("referenceLink", { href: "#" + notePath }); this.watchdog.editor?.execute("referenceLink", { href: "#" + notePath });
} }
this.watchdog.editor.editing.view.focus(); this.watchdog.editor?.editing.view.focus();
} }
// returns true if user selected some text, false if there's no selection // returns true if user selected some text, false if there's no selection
hasSelection() { hasSelection() {
const model = this.watchdog.editor.model; const model = this.watchdog.editor?.model;
const selection = model.document.selection; const selection = model?.document.selection;
return !selection.isCollapsed; return !selection?.isCollapsed;
} }
async executeWithTextEditorEvent({ callback, resolve, ntxId }: EventData<"executeWithTextEditor">) { async executeWithTextEditorEvent({ callback, resolve, ntxId }: EventData<"executeWithTextEditor">) {
@ -443,11 +456,15 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
} }
getSelectedText() { getSelectedText() {
const range = this.watchdog.editor.model.document.selection.getFirstRange(); const range = this.watchdog.editor?.model.document.selection.getFirstRange();
let text = ""; let text = "";
if (!range) {
return text;
}
for (const item of range.getItems()) { for (const item of range.getItems()) {
if (item.data) { if ("data" in item && item.data) {
text += item.data; text += item.data;
} }
} }
@ -458,12 +475,12 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async followLinkUnderCursorCommand() { async followLinkUnderCursorCommand() {
await this.initialized; await this.initialized;
const selection = this.watchdog.editor.model.document.selection; const selection = this.watchdog.editor?.model.document.selection;
const selectedElement = selection.getSelectedElement(); const selectedElement = selection?.getSelectedElement();
if (selectedElement?.name === "reference") { if (selectedElement?.name === "reference") {
// reference link // reference link
const notePath = selectedElement.getAttribute("notePath"); const notePath = selectedElement.getAttribute("notePath") as string | undefined;
if (notePath) { if (notePath) {
await appContext.tabManager.getActiveContext()?.setNote(notePath); await appContext.tabManager.getActiveContext()?.setNote(notePath);
@ -475,7 +492,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return; return;
} }
const selectedLinkUrl = selection.getAttribute("linkHref"); const selectedLinkUrl = selection.getAttribute("linkHref") as string;
const notePath = link.getNotePathFromUrl(selectedLinkUrl); const notePath = link.getNotePathFromUrl(selectedLinkUrl);
if (notePath) { if (notePath) {
@ -490,10 +507,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
} }
addIncludeNote(noteId: string, boxSize?: string) { addIncludeNote(noteId: string, boxSize?: string) {
this.watchdog.editor.model.change((writer) => { this.watchdog.editor?.model.change((writer) => {
// Insert <includeNote>*</includeNote> at the current selection position // Insert <includeNote>*</includeNote> at the current selection position
// in a way that will result in creating a valid model structure // in a way that will result in creating a valid model structure
this.watchdog.editor.model.insertContent( this.watchdog.editor?.model.insertContent(
writer.createElement("includeNote", { writer.createElement("includeNote", {
noteId: noteId, noteId: noteId,
boxSize: boxSize boxSize: boxSize
@ -504,7 +521,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async addImage(noteId: string) { async addImage(noteId: string) {
const note = await froca.getNote(noteId); const note = await froca.getNote(noteId);
if (!note) { if (!note || !this.watchdog.editor) {
return; return;
} }
@ -512,7 +529,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const encodedTitle = encodeURIComponent(note.title); const encodedTitle = encodeURIComponent(note.title);
const src = `api/images/${note.noteId}/${encodedTitle}`; const src = `api/images/${note.noteId}/${encodedTitle}`;
this.watchdog.editor.execute("insertImage", { source: src }); this.watchdog.editor?.execute("insertImage", { source: src });
}); });
} }
@ -544,12 +561,12 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.watchdog.destroy(); this.watchdog.destroy();
await this.createEditor(); await this.createEditor();
this.watchdog.editor.setData(data); this.watchdog.editor?.setData(data);
} }
async onLanguageChanged() { async onLanguageChanged() {
const data = this.watchdog.editor.getData(); const data = this.watchdog.editor?.getData();
await this.reinitialize(data); await this.reinitialize(data ?? "");
} }
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) { buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
@ -557,20 +574,24 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const { TouchBarSegmentedControl, TouchBarGroup, TouchBarButton } = TouchBar; const { TouchBarSegmentedControl, TouchBarGroup, TouchBarButton } = TouchBar;
const { editor } = this.watchdog; const { editor } = this.watchdog;
if (!editor) {
return;
}
const commandButton = (icon: string, command: string) => new TouchBarButton({ const commandButton = (icon: string, command: string) => new TouchBarButton({
icon: buildIcon(icon), icon: buildIcon(icon),
click: () => editor.execute(command), click: () => editor.execute(command),
backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command).value as boolean) backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command)?.value as boolean)
}); });
let headingSelectedIndex = undefined; let headingSelectedIndex = undefined;
const headingCommand = editor.commands.get("heading"); const headingCommand = editor.commands.get("heading");
const paragraphCommand = editor.commands.get("paragraph"); const paragraphCommand = editor.commands.get("paragraph");
if (paragraphCommand.value) { if (paragraphCommand?.value) {
headingSelectedIndex = 0; headingSelectedIndex = 0;
} else if (headingCommand.value === "heading2") { } else if (headingCommand?.value === "heading2") {
headingSelectedIndex = 1; headingSelectedIndex = 1;
} else if (headingCommand.value === "heading3") { } else if (headingCommand?.value === "heading3") {
headingSelectedIndex = 2; headingSelectedIndex = 2;
} }
@ -581,7 +602,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
{ label: "H2" }, { label: "H2" },
{ label: "H3" } { label: "H3" }
], ],
change(selectedIndex, isSelected) { change(selectedIndex: number, isSelected: boolean) {
switch (selectedIndex) { switch (selectedIndex) {
case 0: case 0:
editor.execute("paragraph") editor.execute("paragraph")

View File

@ -100,7 +100,7 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
// we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes // we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes
// we could load just ckeditor-content.css but that causes CSS conflicts when both build CSS and this content CSS is loaded at the same time // we could load just ckeditor-content.css but that causes CSS conflicts when both build CSS and this content CSS is loaded at the same time
// (see https://github.com/zadam/trilium/issues/1590 for example of such conflict) // (see https://github.com/zadam/trilium/issues/1590 for example of such conflict)
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR); await import("@triliumnext/ckeditor5");
this.onLanguageChanged(); this.onLanguageChanged();

View File

@ -34,6 +34,9 @@
"src/**/*.ts" "src/**/*.ts"
], ],
"references": [ "references": [
{
"path": "../../packages/ckeditor5/tsconfig.lib.json"
},
{ {
"path": "../../packages/commons/tsconfig.lib.json" "path": "../../packages/commons/tsconfig.lib.json"
} }

View File

@ -3,6 +3,9 @@
"files": [], "files": [],
"include": [], "include": [],
"references": [ "references": [
{
"path": "../../packages/ckeditor5"
},
{ {
"path": "../../packages/commons" "path": "../../packages/commons"
}, },

View File

@ -1,9 +1,3 @@
import { EditorWatchdog } from "ckeditor5"; export { EditorWatchdog } from "ckeditor5";
import BalloonEditor from "./ckeditor_balloon.js"; export { default as BalloonEditor } from "./ckeditor_balloon.js";
import DecoupledEditor from "./ckeditor_decoupled.js"; export { default as DecoupledEditor } from "./ckeditor_decoupled.js";
export default {
BalloonEditor,
DecoupledEditor,
EditorWatchdog
}

23
pnpm-lock.yaml generated
View File

@ -177,6 +177,9 @@ importers:
'@popperjs/core': '@popperjs/core':
specifier: 2.11.8 specifier: 2.11.8
version: 2.11.8 version: 2.11.8
'@triliumnext/ckeditor5':
specifier: workspace:*
version: link:../../packages/ckeditor5
'@triliumnext/commons': '@triliumnext/commons':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/commons version: link:../../packages/commons
@ -371,7 +374,7 @@ importers:
version: 1.0.2 version: 1.0.2
copy-webpack-plugin: copy-webpack-plugin:
specifier: 13.0.0 specifier: 13.0.0
version: 13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0))) version: 13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1))
electron: electron:
specifier: 35.2.2 specifier: 35.2.2
version: 35.2.2 version: 35.2.2
@ -427,7 +430,7 @@ importers:
version: 11.0.4 version: 11.0.4
copy-webpack-plugin: copy-webpack-plugin:
specifier: 13.0.0 specifier: 13.0.0
version: 13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0))) version: 13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1))
electron: electron:
specifier: 35.2.2 specifier: 35.2.2
version: 35.2.2 version: 35.2.2
@ -605,7 +608,7 @@ importers:
version: 1.4.7 version: 1.4.7
copy-webpack-plugin: copy-webpack-plugin:
specifier: 13.0.0 specifier: 13.0.0
version: 13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0))) version: 13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1))
csrf-csrf: csrf-csrf:
specifier: 3.2.2 specifier: 3.2.2
version: 3.2.2 version: 3.2.2
@ -776,7 +779,7 @@ importers:
version: 1.0.1 version: 1.0.1
webpack: webpack:
specifier: 5.99.7 specifier: 5.99.7
version: 5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0)) version: 5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1)
ws: ws:
specifier: 8.18.1 specifier: 8.18.1
version: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) version: 8.18.1(bufferutil@4.0.9)(utf-8-validate@6.0.5)
@ -16950,14 +16953,14 @@ snapshots:
serialize-javascript: 6.0.2 serialize-javascript: 6.0.2
webpack: 5.98.0(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1) webpack: 5.98.0(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1)
copy-webpack-plugin@13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0))): copy-webpack-plugin@13.0.0(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1)):
dependencies: dependencies:
glob-parent: 6.0.2 glob-parent: 6.0.2
normalize-path: 3.0.0 normalize-path: 3.0.0
schema-utils: 4.3.2 schema-utils: 4.3.2
serialize-javascript: 6.0.2 serialize-javascript: 6.0.2
tinyglobby: 0.2.13 tinyglobby: 0.2.13
webpack: 5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0)) webpack: 5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1)
core-js-compat@3.41.0: core-js-compat@3.41.0:
dependencies: dependencies:
@ -22876,14 +22879,14 @@ snapshots:
'@swc/core': 1.5.29(@swc/helpers@0.5.17) '@swc/core': 1.5.29(@swc/helpers@0.5.17)
esbuild: 0.25.3 esbuild: 0.25.3
terser-webpack-plugin@5.3.14(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0))): terser-webpack-plugin@5.3.14(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1)):
dependencies: dependencies:
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
jest-worker: 27.5.1 jest-worker: 27.5.1
schema-utils: 4.3.2 schema-utils: 4.3.2
serialize-javascript: 6.0.2 serialize-javascript: 6.0.2
terser: 5.39.0 terser: 5.39.0
webpack: 5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0)) webpack: 5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1)
optionalDependencies: optionalDependencies:
'@swc/core': 1.5.29(@swc/helpers@0.5.17) '@swc/core': 1.5.29(@swc/helpers@0.5.17)
esbuild: 0.25.3 esbuild: 0.25.3
@ -23665,7 +23668,7 @@ snapshots:
- esbuild - esbuild
- uglify-js - uglify-js
webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0)): webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1):
dependencies: dependencies:
'@types/eslint-scope': 3.7.7 '@types/eslint-scope': 3.7.7
'@types/estree': 1.0.7 '@types/estree': 1.0.7
@ -23688,7 +23691,7 @@ snapshots:
neo-async: 2.6.2 neo-async: 2.6.2
schema-utils: 4.3.2 schema-utils: 4.3.2
tapable: 2.2.1 tapable: 2.2.1
terser-webpack-plugin: 5.3.14(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1(webpack@5.98.0))) terser-webpack-plugin: 5.3.14(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack@5.99.7(@swc/core@1.5.29(@swc/helpers@0.5.17))(esbuild@0.25.3)(webpack-cli@6.0.1))
watchpack: 2.4.2 watchpack: 2.4.2
webpack-sources: 3.2.3 webpack-sources: 3.2.3
optionalDependencies: optionalDependencies: