From 5b82b750dc08b805f817cf288ccaebeb4725d95a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 20 Mar 2025 23:42:32 +0200 Subject: [PATCH] chore(client/ts): port editable_text --- src/public/app/types.d.ts | 64 ++++++++++++++++--- .../{editable_text.js => editable_text.ts} | 19 ++++-- 2 files changed, 67 insertions(+), 16 deletions(-) rename src/public/app/widgets/type_widgets/{editable_text.js => editable_text.ts} (97%) diff --git a/src/public/app/types.d.ts b/src/public/app/types.d.ts index 6e9e514cf..fa06ec614 100644 --- a/src/public/app/types.d.ts +++ b/src/public/app/types.d.ts @@ -176,17 +176,50 @@ declare global { }> }; + interface CKCodeBlockLanguage { + language: string; + label: string; + } + + interface CKWatchdog { + constructor(editorClass: CKEditorInstance, opts: { + minimumNonErrorTimePeriod: number; + crashNumberLimit: number, + saveInterval: number + }); + on(event: string, callback: () => void); + state: string; + crashes: unknown[]; + editor: TextEditor; + setCreator(callback: (elementOrData, editorConfig) => void); + create(el: HTMLElement, opts: { + placeholder: string, + mention: MentionConfig, + codeBlock: { + languages: CKCodeBlockLanguage[] + }, + math: { + engine: string, + outputType: string, + lazyLoad: () => Promise, + forceOutputType: boolean, + enablePreview: boolean + }, + mermaid: { + lazyLoad: () => Promise, + config: MermaidConfig + } + }); + } + var CKEditor: { - BalloonEditor: { - create(el: HTMLElement, config: { - removePlugins?: string[]; - toolbar: { - items: any[]; - }, - placeholder: string; - mention: MentionConfig - }) - } + BalloonEditor: CKEditorInstance; + DecoupledEditor: CKEditorInstance; + EditorWatchdog: typeof CKWatchdog; + }; + + var CKEditorInspector: { + attach(editor: TextEditor); }; var CodeMirror: { @@ -257,6 +290,7 @@ declare global { setAttribute(name: string, value: string, el: TextEditorElement); createPositionAt(el: TextEditorElement, opt?: "end"); setSelection(pos: number, pos?: number); + insertText(text: string, opts: Record | undefined, position?: TextPosition); } interface TextNode { previousSibling?: TextNode; @@ -275,6 +309,15 @@ declare global { compareWith(pos: TextPosition): string; } interface TextEditor { + create(el: HTMLElement, config: { + removePlugins?: string[]; + toolbar: { + items: any[]; + }, + placeholder: string; + mention: MentionConfig + }); + enableReadOnlyMode(reason: string); model: { document: { on(event: string, cb: () => void); @@ -308,6 +351,7 @@ declare global { } change(cb: (writer: Writer) => void); scrollToTheSelection(): void; + focus(): void; } }, plugins: { diff --git a/src/public/app/widgets/type_widgets/editable_text.js b/src/public/app/widgets/type_widgets/editable_text.ts similarity index 97% rename from src/public/app/widgets/type_widgets/editable_text.js rename to src/public/app/widgets/type_widgets/editable_text.ts index 435d3a127..2863b025a 100644 --- a/src/public/app/widgets/type_widgets/editable_text.js +++ b/src/public/app/widgets/type_widgets/editable_text.ts @@ -16,14 +16,15 @@ import toast from "../../services/toast.js"; import { getMermaidConfig } from "../mermaid.js"; import { normalizeMimeTypeForCKEditor } from "../../services/mime_type_definitions.js"; import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js"; +import type FNote from "../../entities/fnote.js"; const ENABLE_INSPECTOR = false; -const mentionSetup = { +const mentionSetup: MentionConfig = { feeds: [ { marker: "@", - feed: (queryText) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText), + feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText), itemRenderer: (item) => { const itemElement = document.createElement("button"); @@ -118,6 +119,12 @@ function buildListOfLanguages() { * - Decoupled mode, in which the editing toolbar is actually added on the client side (in {@link ClassicEditorToolbar}), see https://ckeditor.com/docs/ckeditor5/latest/examples/framework/bottom-toolbar-editor.html for an example on how the decoupled editor works. */ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { + + private contentLanguage?: string | null; + private watchdog!: CKWatchdog; + + private $editor!: JQuery; + static getType() { return "editableText"; } @@ -195,7 +202,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { } }; - const contentLanguage = this.note.getLabelValue("language"); + const contentLanguage = this.note?.getLabelValue("language"); if (contentLanguage) { finalConfig.language = { ui: (typeof finalConfig.language === "string" ? finalConfig.language : "en"), @@ -277,7 +284,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { }); } - async doRefresh(note) { + async doRefresh(note: FNote) { const blob = await note.getBlob(); await this.spacedUpdate.allowUpdateWithoutChange(async () => { @@ -334,7 +341,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { this.addTextToEditor(dateString); } - async addLinkToEditor(linkHref, linkTitle) { + async addLinkToEditor(linkHref: string, linkTitle: string) { await this.initialized; this.watchdog.editor.model.change((writer) => { @@ -343,7 +350,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget { }); } - async addTextToEditor(text) { + async addTextToEditor(text: string) { await this.initialized; this.watchdog.editor.model.change((writer) => {