mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
fix(ckeditor5): reintroduce block handle for floating editor
This commit is contained in:
parent
10e5852a67
commit
910b0d280d
@ -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 EditableTextTypeWidget from "../widgets/type_widgets/editable_text.js";
|
||||||
import type { NativeImage, TouchBar } from "electron";
|
import type { NativeImage, TouchBar } from "electron";
|
||||||
import TouchBarComponent from "./touch_bar.js";
|
import TouchBarComponent from "./touch_bar.js";
|
||||||
|
import type { ClassicEditor, PopupEditor } from "@triliumnext/ckeditor5";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
getRootWidget: (appContext: AppContext) => RootWidget;
|
getRootWidget: (appContext: AppContext) => RootWidget;
|
||||||
@ -187,7 +188,7 @@ export type CommandMappings = {
|
|||||||
callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void;
|
callback: (value: NoteDetailWidget | PromiseLike<NoteDetailWidget>) => void;
|
||||||
};
|
};
|
||||||
executeWithTextEditor: CommandData &
|
executeWithTextEditor: CommandData &
|
||||||
ExecuteCommandData<TextEditor> & {
|
ExecuteCommandData<ClassicEditor | PopupEditor> & {
|
||||||
callback?: GetTextEditorCallback;
|
callback?: GetTextEditorCallback;
|
||||||
};
|
};
|
||||||
executeWithCodeEditor: CommandData & ExecuteCommandData<CodeMirrorInstance>;
|
executeWithCodeEditor: CommandData & ExecuteCommandData<CodeMirrorInstance>;
|
||||||
|
@ -10,13 +10,14 @@ import options from "../services/options.js";
|
|||||||
import type { ViewScope } from "../services/link.js";
|
import type { ViewScope } from "../services/link.js";
|
||||||
import type FNote from "../entities/fnote.js";
|
import type FNote from "../entities/fnote.js";
|
||||||
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
||||||
|
import type { ClassicEditor, PopupEditor } from "@triliumnext/ckeditor5";
|
||||||
|
|
||||||
export interface SetNoteOpts {
|
export interface SetNoteOpts {
|
||||||
triggerSwitchEvent?: unknown;
|
triggerSwitchEvent?: unknown;
|
||||||
viewScope?: ViewScope;
|
viewScope?: ViewScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetTextEditorCallback = (editor: TextEditor) => void;
|
export type GetTextEditorCallback = (editor: ClassicEditor | PopupEditor) => void;
|
||||||
|
|
||||||
class NoteContext extends Component implements EventListener<"entitiesReloaded"> {
|
class NoteContext extends Component implements EventListener<"entitiesReloaded"> {
|
||||||
ntxId: string | null;
|
ntxId: string | null;
|
||||||
@ -298,7 +299,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getTextEditor(callback?: GetTextEditorCallback) {
|
async getTextEditor(callback?: GetTextEditorCallback) {
|
||||||
return this.timeout<TextEditor>(
|
return this.timeout<ClassicEditor | PopupEditor>(
|
||||||
new Promise((resolve) =>
|
new Promise((resolve) =>
|
||||||
appContext.triggerCommand("executeWithTextEditor", {
|
appContext.triggerCommand("executeWithTextEditor", {
|
||||||
callback,
|
callback,
|
||||||
|
103
apps/client/src/types.d.ts
vendored
103
apps/client/src/types.d.ts
vendored
@ -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<T>(action: string, ...args: unknown[]): T;
|
|
||||||
focus(): void;
|
|
||||||
sourceElement: HTMLElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditingState {
|
interface EditingState {
|
||||||
highlightedResult: string;
|
highlightedResult: string;
|
||||||
results: unknown[];
|
results: unknown[];
|
||||||
|
@ -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 { BalloonEditor } from "@triliumnext/ckeditor5";
|
import { AttributeEditor } 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";
|
||||||
@ -199,7 +199,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
|
|||||||
private $saveAttributesButton!: JQuery<HTMLElement>;
|
private $saveAttributesButton!: JQuery<HTMLElement>;
|
||||||
private $errors!: JQuery<HTMLElement>;
|
private $errors!: JQuery<HTMLElement>;
|
||||||
|
|
||||||
private textEditor!: BalloonEditor;
|
private textEditor!: AttributeEditor;
|
||||||
private lastUpdatedNoteId!: string | undefined;
|
private lastUpdatedNoteId!: string | undefined;
|
||||||
private lastSavedContent!: string;
|
private lastSavedContent!: string;
|
||||||
|
|
||||||
@ -373,7 +373,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
|
|||||||
|
|
||||||
this.$editor.on("click", (e) => this.handleEditorClick(e));
|
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.model.document.on("change:data", () => this.dataChanged());
|
||||||
this.textEditor.editing.view.document.on(
|
this.textEditor.editing.view.document.on(
|
||||||
"enter",
|
"enter",
|
||||||
|
@ -18,7 +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, COMMON_PLUGINS, DecoupledEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
|
import { PopupEditor, ClassicEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
|
||||||
|
|
||||||
const ENABLE_INSPECTOR = false;
|
const ENABLE_INSPECTOR = false;
|
||||||
|
|
||||||
@ -128,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!: EditorWatchdog<DecoupledEditor | BalloonEditor>;
|
private watchdog!: EditorWatchdog<ClassicEditor | PopupEditor>;
|
||||||
|
|
||||||
private $editor!: JQuery<HTMLElement>;
|
private $editor!: JQuery<HTMLElement>;
|
||||||
|
|
||||||
@ -151,14 +151,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
|
|
||||||
async initEditor() {
|
async initEditor() {
|
||||||
const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic";
|
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,
|
// 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 EditorWatchdog<DecoupledEditor | BalloonEditor>(editorClass, {
|
this.watchdog = new EditorWatchdog<ClassicEditor | PopupEditor>(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
|
||||||
@ -252,7 +252,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
|
|
||||||
$classicToolbarWidget.empty();
|
$classicToolbarWidget.empty();
|
||||||
if ($classicToolbarWidget.length) {
|
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()) {
|
if (utils.isMobile()) {
|
||||||
@ -260,7 +263,7 @@ 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 as DecoupledEditor).ui.view.toolbar;
|
const toolbarView = (editor as ClassicEditor).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;
|
||||||
@ -317,8 +320,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
mermaid: {
|
mermaid: {
|
||||||
lazyLoad: async () => (await import("mermaid")).default, // FIXME
|
lazyLoad: async () => (await import("mermaid")).default, // FIXME
|
||||||
config: getMermaidConfig()
|
config: getMermaidConfig()
|
||||||
},
|
}
|
||||||
plugins: COMMON_PLUGINS
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,6 +445,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
|
|
||||||
await this.initialized;
|
await this.initialized;
|
||||||
|
|
||||||
|
if (!this.watchdog.editor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(this.watchdog.editor);
|
callback(this.watchdog.editor);
|
||||||
}
|
}
|
||||||
@ -489,7 +495,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selection.hasAttribute("linkHref")) {
|
if (!selection?.hasAttribute("linkHref")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
import "ckeditor5/ckeditor5.css";
|
import "ckeditor5/ckeditor5.css";
|
||||||
export { EditorWatchdog, BalloonEditor, DecoupledEditor } from "ckeditor5";
|
import { COMMON_PLUGINS, POPUP_EDITOR_PLUGINS } from "./plugins";
|
||||||
export * from "./plugins.js";
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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 type { Plugin } from "ckeditor5";
|
||||||
import CutToNotePlugin from "./plugins/cuttonote.js";
|
import CutToNotePlugin from "./plugins/cuttonote.js";
|
||||||
import UploadimagePlugin from "./plugins/uploadimage.js";
|
import UploadimagePlugin from "./plugins/uploadimage.js";
|
||||||
@ -110,4 +110,9 @@ export const COMMON_PLUGINS: typeof Plugin[] = [
|
|||||||
Style
|
Style
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const POPUP_EDITOR_PLUGINS: typeof Plugin[] = [
|
||||||
|
...COMMON_PLUGINS,
|
||||||
|
BlockToolbar
|
||||||
|
];
|
||||||
|
|
||||||
export const COMMON_SETTINGS = { };
|
export const COMMON_SETTINGS = { };
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
"include": [],
|
"include": [],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "../ckeditor5-math"
|
"path": "../ckeditor5-footnotes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../ckeditor5-footnotes"
|
"path": "../ckeditor5-math"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../ckeditor5-admonition"
|
"path": "../ckeditor5-admonition"
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
],
|
],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "../ckeditor5-math"
|
"path": "../ckeditor5-footnotes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../ckeditor5-footnotes"
|
"path": "../ckeditor5-math"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "../ckeditor5-admonition"
|
"path": "../ckeditor5-admonition"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user