mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 10:02:59 +08:00
Text snippets (#2344)
This commit is contained in:
commit
60dbf9dd67
@ -281,6 +281,7 @@ export type CommandMappings = {
|
|||||||
buildIcon(name: string): NativeImage;
|
buildIcon(name: string): NativeImage;
|
||||||
};
|
};
|
||||||
refreshTouchBar: CommandData;
|
refreshTouchBar: CommandData;
|
||||||
|
reloadTextEditor: CommandData;
|
||||||
};
|
};
|
||||||
|
|
||||||
type EventMappings = {
|
type EventMappings = {
|
||||||
|
@ -245,6 +245,10 @@ class FrocaImpl implements Froca {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getNotes(noteIds: string[] | JQuery<string>, silentNotFoundError = false): Promise<FNote[]> {
|
async getNotes(noteIds: string[] | JQuery<string>, silentNotFoundError = false): Promise<FNote[]> {
|
||||||
|
if (noteIds.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
noteIds = Array.from(new Set(noteIds)); // make unique
|
noteIds = Array.from(new Set(noteIds)); // make unique
|
||||||
const missingNoteIds = noteIds.filter((noteId) => !this.notes[noteId]);
|
const missingNoteIds = noteIds.filter((noteId) => !this.notes[noteId]);
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import { t } from "./i18n.js";
|
|||||||
import type { MenuItem } from "../menus/context_menu.js";
|
import type { MenuItem } from "../menus/context_menu.js";
|
||||||
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
|
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
|
||||||
|
|
||||||
|
const SEPARATOR = { title: "----" };
|
||||||
|
|
||||||
async function getNoteTypeItems(command?: TreeCommandNames) {
|
async function getNoteTypeItems(command?: TreeCommandNames) {
|
||||||
const items: MenuItem<TreeCommandNames>[] = [
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
||||||
@ -18,14 +20,23 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
|
|||||||
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
|
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
|
||||||
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
|
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
|
||||||
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
|
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
|
||||||
|
...await getBuiltInTemplates(command),
|
||||||
|
...await getUserTemplates(command)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUserTemplates(command?: TreeCommandNames) {
|
||||||
const templateNoteIds = await server.get<string[]>("search-templates");
|
const templateNoteIds = await server.get<string[]>("search-templates");
|
||||||
const templateNotes = await froca.getNotes(templateNoteIds);
|
const templateNotes = await froca.getNotes(templateNoteIds);
|
||||||
|
if (templateNotes.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
if (templateNotes.length > 0) {
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
items.push({ title: "----" });
|
SEPARATOR
|
||||||
|
];
|
||||||
for (const templateNote of templateNotes) {
|
for (const templateNote of templateNotes) {
|
||||||
items.push({
|
items.push({
|
||||||
title: templateNote.title,
|
title: templateNote.title,
|
||||||
@ -35,8 +46,33 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
|
|||||||
templateNoteId: templateNote.noteId
|
templateNoteId: templateNote.noteId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getBuiltInTemplates(command?: TreeCommandNames) {
|
||||||
|
const templatesRoot = await froca.getNote("_templates");
|
||||||
|
if (!templatesRoot) {
|
||||||
|
console.warn("Unable to find template root.");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const childNotes = await templatesRoot.getChildNotes();
|
||||||
|
if (childNotes.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
|
SEPARATOR
|
||||||
|
];
|
||||||
|
for (const templateNote of childNotes) {
|
||||||
|
items.push({
|
||||||
|
title: templateNote.title,
|
||||||
|
uiIcon: templateNote.getIcon(),
|
||||||
|
command: command,
|
||||||
|
type: templateNote.type,
|
||||||
|
templateNoteId: templateNote.noteId
|
||||||
|
});
|
||||||
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1280,16 +1280,19 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
|
|||||||
padding: 0.5em 1em !important;
|
padding: 0.5em 1em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck.ck-slash-command-button__text-part {
|
.ck.ck-slash-command-button__text-part,
|
||||||
|
.ck.ck-template-form__text-part {
|
||||||
margin-left: 0.5em;
|
margin-left: 0.5em;
|
||||||
line-height: 1.2em !important;
|
line-height: 1.2em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck.ck-slash-command-button__text-part > span {
|
.ck.ck-slash-command-button__text-part > span,
|
||||||
|
.ck.ck-template-form__text-part > span {
|
||||||
line-height: inherit !important;
|
line-height: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck.ck-slash-command-button__text-part .ck.ck-slash-command-button__description {
|
.ck.ck-slash-command-button__text-part .ck.ck-slash-command-button__description,
|
||||||
|
.ck.ck-template-form__text-part .ck-template-form__description {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
5
apps/client/src/types-assets.d.ts
vendored
5
apps/client/src/types-assets.d.ts
vendored
@ -8,4 +8,9 @@ declare module "*?url" {
|
|||||||
export default path;
|
export default path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "*?raw" {
|
||||||
|
var content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
declare module "boxicons/css/boxicons.min.css" { }
|
declare module "boxicons/css/boxicons.min.css" { }
|
||||||
|
@ -7,13 +7,14 @@ import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../
|
|||||||
import utils from "../../../services/utils.js";
|
import utils from "../../../services/utils.js";
|
||||||
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
|
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
|
||||||
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
||||||
|
import getTemplates from "./snippets.js";
|
||||||
|
|
||||||
const TEXT_FORMATTING_GROUP = {
|
const TEXT_FORMATTING_GROUP = {
|
||||||
label: "Text formatting",
|
label: "Text formatting",
|
||||||
icon: "text"
|
icon: "text"
|
||||||
};
|
};
|
||||||
|
|
||||||
export function buildConfig(): EditorConfig {
|
export async function buildConfig(): Promise<EditorConfig> {
|
||||||
return {
|
return {
|
||||||
image: {
|
image: {
|
||||||
styles: {
|
styles: {
|
||||||
@ -126,6 +127,9 @@ export function buildConfig(): EditorConfig {
|
|||||||
dropdownLimit: Number.MAX_SAFE_INTEGER,
|
dropdownLimit: Number.MAX_SAFE_INTEGER,
|
||||||
extraCommands: buildExtraCommands()
|
extraCommands: buildExtraCommands()
|
||||||
},
|
},
|
||||||
|
template: {
|
||||||
|
definitions: await getTemplates()
|
||||||
|
},
|
||||||
// This value must be kept in sync with the language defined in webpack.config.js.
|
// This value must be kept in sync with the language defined in webpack.config.js.
|
||||||
language: "en"
|
language: "en"
|
||||||
};
|
};
|
||||||
@ -206,6 +210,7 @@ export function buildClassicToolbar(multilineToolbar: boolean) {
|
|||||||
"outdent",
|
"outdent",
|
||||||
"indent",
|
"indent",
|
||||||
"|",
|
"|",
|
||||||
|
"insertTemplate",
|
||||||
"markdownImport",
|
"markdownImport",
|
||||||
"cuttonote",
|
"cuttonote",
|
||||||
"findAndReplace"
|
"findAndReplace"
|
||||||
@ -262,6 +267,7 @@ export function buildFloatingToolbar() {
|
|||||||
"outdent",
|
"outdent",
|
||||||
"indent",
|
"indent",
|
||||||
"|",
|
"|",
|
||||||
|
"insertTemplate",
|
||||||
"imageUpload",
|
"imageUpload",
|
||||||
"markdownImport",
|
"markdownImport",
|
||||||
"specialCharacters",
|
"specialCharacters",
|
||||||
|
105
apps/client/src/widgets/type_widgets/ckeditor/snippets.ts
Normal file
105
apps/client/src/widgets/type_widgets/ckeditor/snippets.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import debounce from "debounce";
|
||||||
|
import froca from "../../../services/froca.js";
|
||||||
|
import type LoadResults from "../../../services/load_results.js";
|
||||||
|
import search from "../../../services/search.js";
|
||||||
|
import type { TemplateDefinition } from "@triliumnext/ckeditor5";
|
||||||
|
import appContext from "../../../components/app_context.js";
|
||||||
|
import TemplateIcon from "@ckeditor/ckeditor5-icons/theme/icons/template.svg?raw";
|
||||||
|
import type FNote from "../../../entities/fnote.js";
|
||||||
|
|
||||||
|
interface TemplateData {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let templateCache: Map<string, TemplateData> = new Map();
|
||||||
|
const debouncedHandleContentUpdate = debounce(handleContentUpdate, 1000);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the list of snippets based on the user's notes to be passed down to the CKEditor configuration.
|
||||||
|
*
|
||||||
|
* @returns the list of templates.
|
||||||
|
*/
|
||||||
|
export default async function getTemplates() {
|
||||||
|
// Build the definitions and populate the cache.
|
||||||
|
const snippets = await search.searchForNotes("#textSnippet");
|
||||||
|
const definitions: TemplateDefinition[] = [];
|
||||||
|
for (const snippet of snippets) {
|
||||||
|
const { description } = await invalidateCacheFor(snippet);
|
||||||
|
|
||||||
|
definitions.push({
|
||||||
|
title: snippet.title,
|
||||||
|
data: () => templateCache.get(snippet.noteId)?.content ?? "",
|
||||||
|
icon: TemplateIcon,
|
||||||
|
description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function invalidateCacheFor(snippet: FNote) {
|
||||||
|
const description = snippet.getLabelValue("textSnippetDescription");
|
||||||
|
const data: TemplateData = {
|
||||||
|
title: snippet.title,
|
||||||
|
description: description ?? undefined,
|
||||||
|
content: await snippet.getContent()
|
||||||
|
};
|
||||||
|
templateCache.set(snippet.noteId, data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFullReload() {
|
||||||
|
console.warn("Full text editor reload needed");
|
||||||
|
appContext.triggerCommand("reloadTextEditor");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleContentUpdate(affectedNoteIds: string[]) {
|
||||||
|
const updatedNoteIds = new Set(affectedNoteIds);
|
||||||
|
const templateNoteIds = new Set(templateCache.keys());
|
||||||
|
const affectedTemplateNoteIds = templateNoteIds.intersection(updatedNoteIds);
|
||||||
|
|
||||||
|
await froca.getNotes(affectedNoteIds);
|
||||||
|
|
||||||
|
let fullReloadNeeded = false;
|
||||||
|
for (const affectedTemplateNoteId of affectedTemplateNoteIds) {
|
||||||
|
try {
|
||||||
|
const template = await froca.getNote(affectedTemplateNoteId);
|
||||||
|
if (!template) {
|
||||||
|
console.warn("Unable to obtain template with ID ", affectedTemplateNoteId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTitle = template.title;
|
||||||
|
if (templateCache.get(affectedTemplateNoteId)?.title !== newTitle) {
|
||||||
|
fullReloadNeeded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await invalidateCacheFor(template);
|
||||||
|
} catch (e) {
|
||||||
|
// If a note was not found while updating the cache, it means we need to do a full reload.
|
||||||
|
fullReloadNeeded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullReloadNeeded) {
|
||||||
|
handleFullReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateTemplateCache(loadResults: LoadResults): boolean {
|
||||||
|
const affectedNoteIds = loadResults.getNoteIds();
|
||||||
|
|
||||||
|
// React to creation or deletion of text snippets.
|
||||||
|
if (loadResults.getAttributeRows().find((attr) =>
|
||||||
|
attr.type === "label" &&
|
||||||
|
(attr.name === "textSnippet" || attr.name === "textSnippetDescription"))) {
|
||||||
|
handleFullReload();
|
||||||
|
} else if (affectedNoteIds.length > 0) {
|
||||||
|
// Update content and titles if one of the template notes were updated.
|
||||||
|
debouncedHandleContentUpdate(affectedNoteIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -18,6 +18,7 @@ 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 } from "@triliumnext/ckeditor5";
|
||||||
import "@triliumnext/ckeditor5/index.css";
|
import "@triliumnext/ckeditor5/index.css";
|
||||||
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
|
||||||
|
import { updateTemplateCache } from "./ckeditor/snippets.js";
|
||||||
|
|
||||||
const mentionSetup: MentionFeed[] = [
|
const mentionSetup: MentionFeed[] = [
|
||||||
{
|
{
|
||||||
@ -193,7 +194,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
|
|
||||||
const finalConfig = {
|
const finalConfig = {
|
||||||
...editorConfig,
|
...editorConfig,
|
||||||
...buildConfig(),
|
...(await buildConfig()),
|
||||||
...buildToolbarConfig(isClassicEditor),
|
...buildToolbarConfig(isClassicEditor),
|
||||||
htmlSupport: {
|
htmlSupport: {
|
||||||
allow: JSON.parse(options.get("allowedHtmlTags")),
|
allow: JSON.parse(options.get("allowedHtmlTags")),
|
||||||
@ -326,7 +327,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
const data = blob?.content || "";
|
const data = blob?.content || "";
|
||||||
const newContentLanguage = this.note?.getLabelValue("language");
|
const newContentLanguage = this.note?.getLabelValue("language");
|
||||||
if (this.contentLanguage !== newContentLanguage) {
|
if (this.contentLanguage !== newContentLanguage) {
|
||||||
await this.reinitialize(data);
|
await this.reinitializeWithData(data);
|
||||||
} else {
|
} else {
|
||||||
this.watchdog.editor?.setData(data);
|
this.watchdog.editor?.setData(data);
|
||||||
}
|
}
|
||||||
@ -562,7 +563,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.refreshIncludedNote(this.$editor, noteId);
|
this.refreshIncludedNote(this.$editor, noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async reinitialize(data: string) {
|
async reinitializeWithData(data: string) {
|
||||||
if (!this.watchdog) {
|
if (!this.watchdog) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -572,9 +573,25 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
this.watchdog.editor?.setData(data);
|
this.watchdog.editor?.setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onLanguageChanged() {
|
async reinitialize() {
|
||||||
const data = this.watchdog.editor?.getData();
|
const data = this.watchdog.editor?.getData();
|
||||||
await this.reinitialize(data ?? "");
|
await this.reinitializeWithData(data ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
async reloadTextEditorEvent() {
|
||||||
|
await this.reinitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLanguageChanged() {
|
||||||
|
await this.reinitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
async entitiesReloadedEvent(e: EventData<"entitiesReloaded">) {
|
||||||
|
await super.entitiesReloadedEvent(e);
|
||||||
|
|
||||||
|
if (updateTemplateCache(e.loadResults)) {
|
||||||
|
await this.reinitialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
|
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
|
||||||
|
@ -8,6 +8,7 @@ import migrationService from "./migration.js";
|
|||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { cleanUpHelp, getHelpHiddenSubtreeData } from "./in_app_help.js";
|
import { cleanUpHelp, getHelpHiddenSubtreeData } from "./in_app_help.js";
|
||||||
import buildLaunchBarConfig from "./hidden_subtree_launcherbar.js";
|
import buildLaunchBarConfig from "./hidden_subtree_launcherbar.js";
|
||||||
|
import buildHiddenSubtreeTemplates from "./hidden_subtree_templates.js";
|
||||||
|
|
||||||
const LBTPL_ROOT = "_lbTplRoot";
|
const LBTPL_ROOT = "_lbTplRoot";
|
||||||
const LBTPL_BASE = "_lbTplBase";
|
const LBTPL_BASE = "_lbTplBase";
|
||||||
@ -257,7 +258,8 @@ function buildHiddenSubtreeDefinition(helpSubtree: HiddenSubtreeItem[]): HiddenS
|
|||||||
icon: "bx-help-circle",
|
icon: "bx-help-circle",
|
||||||
children: helpSubtree,
|
children: helpSubtree,
|
||||||
isExpanded: true
|
isExpanded: true
|
||||||
}
|
},
|
||||||
|
buildHiddenSubtreeTemplates()
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
34
apps/server/src/services/hidden_subtree_templates.ts
Normal file
34
apps/server/src/services/hidden_subtree_templates.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { HiddenSubtreeItem } from "@triliumnext/commons";
|
||||||
|
|
||||||
|
export default function buildHiddenSubtreeTemplates() {
|
||||||
|
const templates: HiddenSubtreeItem = {
|
||||||
|
id: "_templates",
|
||||||
|
title: "Built-in templates",
|
||||||
|
type: "book",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: "_template_text_snippet",
|
||||||
|
type: "text",
|
||||||
|
title: "Text Snippet",
|
||||||
|
icon: "bx bx-align-left",
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
name: "template",
|
||||||
|
type: "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "textSnippet",
|
||||||
|
type: "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "label:textSnippetDescription",
|
||||||
|
type: "label",
|
||||||
|
value: "promoted,alias=Description,single,text"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
return templates;
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { COMMON_PLUGINS, CORE_PLUGINS, POPUP_EDITOR_PLUGINS } from "./plugins";
|
|||||||
import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } from "ckeditor5";
|
import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } from "ckeditor5";
|
||||||
export { EditorWatchdog } from "ckeditor5";
|
export { EditorWatchdog } from "ckeditor5";
|
||||||
export type { EditorConfig, MentionFeed, MentionFeedObjectItem, Node, Position, Element, WatchdogConfig } from "ckeditor5";
|
export type { EditorConfig, MentionFeed, MentionFeedObjectItem, Node, Position, Element, WatchdogConfig } from "ckeditor5";
|
||||||
|
export type { TemplateDefinition } from "ckeditor5-premium-features";
|
||||||
export { default as buildExtraCommands } from "./extra_slash_commands.js";
|
export { default as buildExtraCommands } from "./extra_slash_commands.js";
|
||||||
|
|
||||||
// Import with sideffects to ensure that type augmentations are present.
|
// Import with sideffects to ensure that type augmentations are present.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
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, Alignment, 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, Bookmark, Emoji } 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, Alignment, 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, Bookmark, Emoji } from "ckeditor5";
|
||||||
import { SlashCommand } from "ckeditor5-premium-features";
|
import { SlashCommand, Template } from "ckeditor5-premium-features";
|
||||||
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";
|
||||||
@ -82,7 +82,8 @@ export const CORE_PLUGINS: typeof Plugin[] = [
|
|||||||
* Plugins that require a premium CKEditor license key to work.
|
* Plugins that require a premium CKEditor license key to work.
|
||||||
*/
|
*/
|
||||||
export const PREMIUM_PLUGINS: typeof Plugin[] = [
|
export const PREMIUM_PLUGINS: typeof Plugin[] = [
|
||||||
SlashCommand
|
SlashCommand,
|
||||||
|
Template
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,7 @@ enum Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface HiddenSubtreeAttribute {
|
export interface HiddenSubtreeAttribute {
|
||||||
type: AttributeType;
|
type: "label" | "relation";
|
||||||
name: string;
|
name: string;
|
||||||
isInheritable?: boolean;
|
isInheritable?: boolean;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user