diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index 57811da7e..a0ebd6a6a 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -26,6 +26,7 @@ import type TypeWidget from "../widgets/type_widgets/type_widget.js"; import type EditableTextTypeWidget from "../widgets/type_widgets/editable_text.js"; import type { NativeImage, TouchBar } from "electron"; import TouchBarComponent from "./touch_bar.js"; +import type { ClassicEditor, PopupEditor } from "@triliumnext/ckeditor5"; interface Layout { getRootWidget: (appContext: AppContext) => RootWidget; @@ -187,7 +188,7 @@ export type CommandMappings = { callback: (value: NoteDetailWidget | PromiseLike) => void; }; executeWithTextEditor: CommandData & - ExecuteCommandData & { + ExecuteCommandData & { callback?: GetTextEditorCallback; }; executeWithCodeEditor: CommandData & ExecuteCommandData; diff --git a/apps/client/src/components/note_context.ts b/apps/client/src/components/note_context.ts index 6c93fcc5b..f1500ee42 100644 --- a/apps/client/src/components/note_context.ts +++ b/apps/client/src/components/note_context.ts @@ -10,13 +10,14 @@ import options from "../services/options.js"; import type { ViewScope } from "../services/link.js"; import type FNote from "../entities/fnote.js"; import type TypeWidget from "../widgets/type_widgets/type_widget.js"; +import type { ClassicEditor, PopupEditor } from "@triliumnext/ckeditor5"; export interface SetNoteOpts { triggerSwitchEvent?: unknown; viewScope?: ViewScope; } -export type GetTextEditorCallback = (editor: TextEditor) => void; +export type GetTextEditorCallback = (editor: ClassicEditor | PopupEditor) => void; class NoteContext extends Component implements EventListener<"entitiesReloaded"> { ntxId: string | null; @@ -298,7 +299,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded"> } async getTextEditor(callback?: GetTextEditorCallback) { - return this.timeout( + return this.timeout( new Promise((resolve) => appContext.triggerCommand("executeWithTextEditor", { callback, diff --git a/apps/client/src/types.d.ts b/apps/client/src/types.d.ts index b67be8f42..8266db557 100644 --- a/apps/client/src/types.d.ts +++ b/apps/client/src/types.d.ts @@ -332,109 +332,6 @@ declare global { }; } - interface TextEditor { - create(el: HTMLElement, config: { - removePlugins?: string[]; - toolbar: { - items: any[]; - }, - placeholder: string; - mention: MentionConfig - }); - enableReadOnlyMode(reason: string); - commands: { - get(name: string): { - value: unknown; - on(event: string, callback: () => void): void; - }; - } - model: { - document: { - on(event: string, cb: () => void); - getRoot(): CKNode; - registerPostFixer(callback: (writer: Writer) => boolean); - selection: { - getFirstPosition(): undefined | TextPosition; - getLastPosition(): undefined | TextPosition; - getSelectedElement(): CKNode; - hasAttribute(attribute: string): boolean; - getAttribute(attribute: string): string; - getFirstRange(): Range; - isCollapsed: boolean; - }; - differ: { - getChanges(): { - type: string; - name: string; - position?: { - nodeAfter?: CKNode; - parent: CKNode; - toJSON(): Object; - } - }[]; - } - }, - insertContent(modelFragment: any, selection?: any); - change(cb: (writer: Writer) => void) - }, - editing: { - view: { - document: { - on(event: string, cb: (event: CKEvent, data: { - preventDefault(); - }) => void, opts?: { - priority: "high" - }); - getRoot(): CKNode - }, - domRoots: { - values: () => { - next: () => { - value: string; - } - }; - } - change(cb: (writer: Writer) => void); - scrollToTheSelection(): void; - focus(): void; - } - }, - plugins: { - get(command: string) - }, - data: { - processor: { - toView(html: string); - }; - toModel(viewFeragment: any); - }, - ui: { - view: { - toolbar: { - items: any[]; - element: HTMLElement; - } - } - } - conversion: { - for(filter: string): { - markerToHighlight(data: { - model: string; - view: (data: { - markerName: string; - }) => void; - }) - } - } - getData(): string; - setData(data: string): void; - getSelectedHtml(): string; - removeSelection(): void; - execute(action: string, ...args: unknown[]): T; - focus(): void; - sourceElement: HTMLElement; - } - interface EditingState { highlightedResult: string; results: unknown[]; diff --git a/apps/client/src/widgets/attribute_widgets/attribute_editor.ts b/apps/client/src/widgets/attribute_widgets/attribute_editor.ts index 919b53c3f..572f5d12b 100644 --- a/apps/client/src/widgets/attribute_widgets/attribute_editor.ts +++ b/apps/client/src/widgets/attribute_widgets/attribute_editor.ts @@ -4,7 +4,7 @@ import noteAutocompleteService from "../../services/note_autocomplete.js"; import server from "../../services/server.js"; import contextMenuService from "../../menus/context_menu.js"; import attributeParser, { type Attribute } from "../../services/attribute_parser.js"; -import { BalloonEditor } from "@triliumnext/ckeditor5"; +import { AttributeEditor } from "@triliumnext/ckeditor5"; import froca from "../../services/froca.js"; import attributeRenderer from "../../services/attribute_renderer.js"; import noteCreateService from "../../services/note_create.js"; @@ -199,7 +199,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem private $saveAttributesButton!: JQuery; private $errors!: JQuery; - private textEditor!: BalloonEditor; + private textEditor!: AttributeEditor; private lastUpdatedNoteId!: string | undefined; private lastSavedContent!: string; @@ -373,7 +373,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem this.$editor.on("click", (e) => this.handleEditorClick(e)); - this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig); + this.textEditor = await AttributeEditor.create(this.$editor[0], editorConfig); this.textEditor.model.document.on("change:data", () => this.dataChanged()); this.textEditor.editing.view.document.on( "enter", diff --git a/apps/client/src/widgets/type_widgets/editable_text.ts b/apps/client/src/widgets/type_widgets/editable_text.ts index 3afda133a..3e8163f98 100644 --- a/apps/client/src/widgets/type_widgets/editable_text.ts +++ b/apps/client/src/widgets/type_widgets/editable_text.ts @@ -18,7 +18,7 @@ import { buildSelectedBackgroundColor } from "../../components/touch_bar.js"; import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js"; import type FNote from "../../entities/fnote.js"; import { getMermaidConfig } from "../../services/mermaid.js"; -import { BalloonEditor, COMMON_PLUGINS, DecoupledEditor, EditorWatchdog } from "@triliumnext/ckeditor5"; +import { PopupEditor, ClassicEditor, EditorWatchdog } from "@triliumnext/ckeditor5"; const ENABLE_INSPECTOR = false; @@ -128,7 +128,7 @@ function buildListOfLanguages() { export default class EditableTextTypeWidget extends AbstractTextTypeWidget { private contentLanguage?: string | null; - private watchdog!: EditorWatchdog; + private watchdog!: EditorWatchdog; private $editor!: JQuery; @@ -151,14 +151,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { async initEditor() { const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic"; - const editorClass = isClassicEditor ? DecoupledEditor : BalloonEditor; + 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 // display of $widget in both branches. this.$widget.show(); - this.watchdog = new EditorWatchdog(editorClass, { + this.watchdog = new EditorWatchdog(editorClass, { // 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 // is also reached, the watchdog changes its state to crashedPermanently, and it stops @@ -252,7 +252,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { $classicToolbarWidget.empty(); if ($classicToolbarWidget.length) { - $classicToolbarWidget[0].appendChild(editor.ui.view.toolbar.element); + const toolbarView = (editor as ClassicEditor).ui.view.toolbar; + if (toolbarView.element) { + $classicToolbarWidget[0].appendChild(toolbarView.element); + } } if (utils.isMobile()) { @@ -260,7 +263,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { // 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. - const toolbarView = (editor as DecoupledEditor).ui.view.toolbar; + const toolbarView = (editor as ClassicEditor).ui.view.toolbar; for (const item of toolbarView.items) { if (!("panelView" in item)) { continue; @@ -317,8 +320,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { mermaid: { lazyLoad: async () => (await import("mermaid")).default, // FIXME config: getMermaidConfig() - }, - plugins: COMMON_PLUGINS + } }); } @@ -443,6 +445,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { await this.initialized; + if (!this.watchdog.editor) { + return; + } + if (callback) { callback(this.watchdog.editor); } @@ -489,7 +495,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { } } - if (!selection.hasAttribute("linkHref")) { + if (!selection?.hasAttribute("linkHref")) { return; } diff --git a/packages/ckeditor5/src/index.ts b/packages/ckeditor5/src/index.ts index ce45a6876..7ce7f501b 100644 --- a/packages/ckeditor5/src/index.ts +++ b/packages/ckeditor5/src/index.ts @@ -1,3 +1,22 @@ import "ckeditor5/ckeditor5.css"; -export { EditorWatchdog, BalloonEditor, DecoupledEditor } from "ckeditor5"; -export * from "./plugins.js"; +import { COMMON_PLUGINS, POPUP_EDITOR_PLUGINS } from "./plugins"; +import { BalloonEditor, DecoupledEditor } from "ckeditor5"; +export { EditorWatchdog } from "ckeditor5"; + +export class AttributeEditor extends BalloonEditor { + static override get builtinPlugins() { + return []; + } +} + +export class ClassicEditor extends DecoupledEditor { + static override get builtinPlugins() { + return COMMON_PLUGINS; + } +} + +export class PopupEditor extends BalloonEditor { + static override get builtinPlugins() { + return POPUP_EDITOR_PLUGINS; + } +} diff --git a/packages/ckeditor5/src/plugins.ts b/packages/ckeditor5/src/plugins.ts index 9b49c13cc..3e0b09f8b 100644 --- a/packages/ckeditor5/src/plugins.ts +++ b/packages/ckeditor5/src/plugins.ts @@ -1,4 +1,4 @@ -import { Autoformat, AutoLink, BlockQuote, Bold, CKFinderUploadAdapter, Clipboard, Code, CodeBlock, Enter, FindAndReplace, Font, FontBackgroundColor, FontColor, GeneralHtmlSupport, Heading, HeadingButtonsUI, HorizontalLine, Image, ImageCaption, ImageInline, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Indent, IndentBlock, Italic, Link, List, ListProperties, Mention, PageBreak, Paragraph, ParagraphButtonUI, PasteFromOffice, PictureEditing, RemoveFormat, SelectAll, ShiftEnter, SpecialCharacters, SpecialCharactersEssentials, Strikethrough, Style, Subscript, Superscript, Table, TableCaption, TableCellProperties, TableColumnResize, TableProperties, TableSelection, TableToolbar, TextPartLanguage, TextTransformation, TodoList, Typing, Underline, Undo } from "ckeditor5"; +import { Autoformat, AutoLink, BlockQuote, BlockToolbar, Bold, CKFinderUploadAdapter, Clipboard, Code, CodeBlock, Enter, FindAndReplace, Font, FontBackgroundColor, FontColor, GeneralHtmlSupport, Heading, HeadingButtonsUI, HorizontalLine, Image, ImageCaption, ImageInline, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Indent, IndentBlock, Italic, Link, List, ListProperties, Mention, PageBreak, Paragraph, ParagraphButtonUI, PasteFromOffice, PictureEditing, RemoveFormat, SelectAll, ShiftEnter, SpecialCharacters, SpecialCharactersEssentials, Strikethrough, Style, Subscript, Superscript, Table, TableCaption, TableCellProperties, TableColumnResize, TableProperties, TableSelection, TableToolbar, TextPartLanguage, TextTransformation, TodoList, Typing, Underline, Undo } from "ckeditor5"; import type { Plugin } from "ckeditor5"; import CutToNotePlugin from "./plugins/cuttonote.js"; import UploadimagePlugin from "./plugins/uploadimage.js"; @@ -110,4 +110,9 @@ export const COMMON_PLUGINS: typeof Plugin[] = [ Style ]; +export const POPUP_EDITOR_PLUGINS: typeof Plugin[] = [ + ...COMMON_PLUGINS, + BlockToolbar +]; + export const COMMON_SETTINGS = { }; diff --git a/packages/ckeditor5/tsconfig.json b/packages/ckeditor5/tsconfig.json index 992ddddc0..443bd1e47 100644 --- a/packages/ckeditor5/tsconfig.json +++ b/packages/ckeditor5/tsconfig.json @@ -4,10 +4,10 @@ "include": [], "references": [ { - "path": "../ckeditor5-math" + "path": "../ckeditor5-footnotes" }, { - "path": "../ckeditor5-footnotes" + "path": "../ckeditor5-math" }, { "path": "../ckeditor5-admonition" diff --git a/packages/ckeditor5/tsconfig.lib.json b/packages/ckeditor5/tsconfig.lib.json index 56b8c10dd..00ee5d2e1 100644 --- a/packages/ckeditor5/tsconfig.lib.json +++ b/packages/ckeditor5/tsconfig.lib.json @@ -20,10 +20,10 @@ ], "references": [ { - "path": "../ckeditor5-math" + "path": "../ckeditor5-footnotes" }, { - "path": "../ckeditor5-footnotes" + "path": "../ckeditor5-math" }, { "path": "../ckeditor5-admonition"