Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/client/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
VITE_CKEDITOR_ENABLE_INSPECTOR=false

# The development license key for premium CKEditor features.
# Note: This key is for development purposes only and should not be used in production.
# Expires on: 2025-09-13
VITE_CKEDITOR_KEY=eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE3NTc3MjE1OTksImp0aSI6IjRmMjdkYmYxLTcwOTEtNDYwZi04ZDZmLTc0NzBiZjQwNjg2MCIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsic2giLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwibGljZW5zZVR5cGUiOiJkZXZlbG9wbWVudCIsImZlYXR1cmVzIjpbIkRSVVAiLCJDTVQiLCJETyIsIkZQIiwiU0MiLCJUT0MiLCJUUEwiLCJQT0UiLCJDQyIsIk1GIiwiU0VFIiwiRUNIIiwiRUlTIl0sInZjIjoiMjMxYzMwNTEifQ.9Ct5lIKbioC3dM8EFatDTmimEIVOdItE3Uh_ICHlS_A_8ueqIfkZpsN3L4_EqprvteNki9yqbuZVGpZTaQ51xg
1 change: 1 addition & 0 deletions apps/client/.env.production
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_CKEDITOR_ENABLE_INSPECTOR=false
2 changes: 0 additions & 2 deletions apps/client/src/services/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,6 @@ async function openDialog($dialog: JQuery<HTMLElement>, closeActDialog = true) {
}
});

// TODO: Fix once keyboard_actions is ported.
// @ts-ignore
const keyboardActionsService = (await import("./keyboard_actions.js")).default;
keyboardActionsService.updateDisplayedShortcuts($dialog);

Expand Down
21 changes: 21 additions & 0 deletions apps/client/src/stylesheets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
--bs-body-font-weight: var(--main-font-weight) !important;
--bs-body-color: var(--main-text-color) !important;
--bs-body-bg: var(--main-background-color) !important;
--ck-mention-list-max-height: 500px;
}

.table {
Expand Down Expand Up @@ -1273,6 +1274,26 @@ body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
white-space: normal !important;
}

/* Slash commands */

.ck.ck-slash-command-button {
padding: 0.5em 1em !important;
}

.ck.ck-slash-command-button__text-part {
margin-left: 0.5em;
line-height: 1.2em !important;
}

.ck.ck-slash-command-button__text-part > span {
line-height: inherit !important;
}

.ck.ck-slash-command-button__text-part .ck.ck-slash-command-button__description {
display: block;
opacity: 0.8;
}

.area-expander {
display: flex;
flex-direction: row;
Expand Down
5 changes: 5 additions & 0 deletions apps/client/src/stylesheets/theme-next/notes/text.css
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@
color: var(--menu-item-icon-color);
}

/* Slash commands */
.ck.ck-slash-command-button__text-part .ck.ck-button__label {
font-weight: bold;
}

/* Separator */
:root .ck .ck-list__separator {
margin: .5em 0;
Expand Down
16 changes: 16 additions & 0 deletions apps/client/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// <reference types="vite/client" />

interface ViteTypeOptions {
strictImportMetaEnv: unknown
}

interface ImportMetaEnv {
/** The license key for CKEditor premium features. */
readonly VITE_CKEDITOR_KEY?: string;
/** Whether to enable the CKEditor inspector (see https://ckeditor.com/docs/ckeditor5/latest/framework/develpment-tools/inspector.html). */
readonly VITE_CKEDITOR_ENABLE_INSPECTOR?: "true" | "false";
}

interface ImportMeta {
readonly env: ImportMetaEnv
}
2 changes: 1 addition & 1 deletion apps/client/src/widgets/dialogs/add_link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default class AddLinkDialog extends BasicWidget {

this.updateTitleSettingsVisibility();

utils.openDialog(this.$widget);
await utils.openDialog(this.$widget);

this.$autoComplete.val("");
this.$linkTitle.val("");
Expand Down
7 changes: 6 additions & 1 deletion apps/client/src/widgets/type_widgets/ckeditor/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ALLOWED_PROTOCOLS } from "../../../services/link.js";
import { MIME_TYPE_AUTO } from "@triliumnext/commons";
import type { EditorConfig } from "@triliumnext/ckeditor5";
import { buildExtraCommands, type EditorConfig } from "@triliumnext/ckeditor5";
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
import options from "../../../services/options.js";
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
Expand Down Expand Up @@ -121,6 +121,11 @@ export function buildConfig(): EditorConfig {
clipboard: {
copy: copyTextWithToast
},
slashCommand: {
removeCommands: [],
dropdownLimit: Number.MAX_SAFE_INTEGER,
extraCommands: buildExtraCommands()
},
// This value must be kept in sync with the language defined in webpack.config.js.
language: "en"
};
Expand Down
16 changes: 12 additions & 4 deletions apps/client/src/widgets/type_widgets/editable_text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type Men
import "@triliumnext/ckeditor5/index.css";
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";

const ENABLE_INSPECTOR = false;

const mentionSetup: MentionFeed[] = [
{
marker: "@",
Expand Down Expand Up @@ -203,7 +201,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
classes: true,
attributes: true
},
licenseKey: "GPL"
licenseKey: getLicenseKey()
};

const contentLanguage = this.note?.getLabelValue("language");
Expand Down Expand Up @@ -278,7 +276,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {

editor.model.document.on("change:data", () => this.spacedUpdate.scheduleUpdate());

if (glob.isDev && ENABLE_INSPECTOR) {
if (import.meta.env.VITE_CKEDITOR_ENABLE_INSPECTOR === "true") {
const CKEditorInspector = (await import("@ckeditor/ckeditor5-inspector")).default;
CKEditorInspector.attach(editor);
}
Expand Down Expand Up @@ -640,3 +638,13 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}

}

function getLicenseKey() {
const premiumLicenseKey = import.meta.env.VITE_CKEDITOR_KEY;
if (!premiumLicenseKey) {
logError("CKEditor license key is not set, premium features will not be available.");
return "GPL";
}

return premiumLicenseKey;
}
4 changes: 2 additions & 2 deletions packages/ckeditor5-admonition/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import "../theme/blockquote.css";

export { default as Admonition } from './admonition.js';
export { default as AdmonitionEditing } from './admonitionediting.js';
export { default as AdmonitionUI } from './admonitionui.js';
export { default as AdmonitionUI, ADMONITION_TYPES } from './admonitionui.js';
export { default as AdmonitionAutoformat } from './admonitionautoformat.js';
export type { default as AdmonitionCommand } from './admonitioncommand.js';
export type { default as AdmonitionCommand, AdmonitionType } from './admonitioncommand.js';

export const icons = {
admonitionIcon
Expand Down
1 change: 1 addition & 0 deletions packages/ckeditor5-math/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import './augmentation.js';
import "../theme/mathform.css";

export { default as Math } from './math.js';
export { default as MathUI } from './mathui.js';
export { default as AutoformatMath } from './autoformatmath.js';

export const icons = {
Expand Down
13 changes: 8 additions & 5 deletions packages/ckeditor5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,20 @@
"name": "ckeditor5",
"targets": {
"typecheck": {
"dependsOn": [ "^build" ]
"dependsOn": [
"^build"
]
}
}
},
"dependencies": {
"ckeditor5": "45.2.0",
"@triliumnext/ckeditor5-keyboard-marker": "workspace:*",
"@triliumnext/ckeditor5-mermaid": "workspace:*",
"@triliumnext/ckeditor5-admonition": "workspace:*",
"@triliumnext/ckeditor5-footnotes": "workspace:*",
"@triliumnext/ckeditor5-math": "workspace:*"
"@triliumnext/ckeditor5-keyboard-marker": "workspace:*",
"@triliumnext/ckeditor5-math": "workspace:*",
"@triliumnext/ckeditor5-mermaid": "workspace:*",
"ckeditor5": "45.2.0",
"ckeditor5-premium-features": "45.2.0"
},
"devDependencies": {
"@types/jquery": "3.5.32"
Expand Down
139 changes: 139 additions & 0 deletions packages/ckeditor5/src/extra_slash_commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import type { Editor } from 'ckeditor5';
import type { SlashCommandEditorConfig } from 'ckeditor5-premium-features';
import { icons as footnoteIcons } from '@triliumnext/ckeditor5-footnotes';
import IconPageBreak from "@ckeditor/ckeditor5-icons/theme/icons/page-break.svg?raw";
import IconAlignLeft from "@ckeditor/ckeditor5-icons/theme/icons/align-left.svg?raw";
import IconAlignCenter from "@ckeditor/ckeditor5-icons/theme/icons/align-center.svg?raw";
import IconAlignRight from "@ckeditor/ckeditor5-icons/theme/icons/align-right.svg?raw";
import IconAlignJustify from "@ckeditor/ckeditor5-icons/theme/icons/align-justify.svg?raw";
import bxInfoCircle from "boxicons/svg/regular/bx-info-circle.svg?raw";
import bxBulb from "boxicons/svg/regular/bx-bulb.svg?raw";
import bxCommentError from "boxicons/svg/regular/bx-comment-error.svg?raw";
import bxErrorCircle from "boxicons/svg/regular/bx-error-circle.svg?raw";
import bxError from "boxicons/svg/regular/bx-error.svg?raw";
import { COMMAND_NAME as INSERT_DATE_TIME_COMMAND } from './plugins/insert_date_time.js';
import { COMMAND_NAME as INTERNAL_LINK_COMMAND } from './plugins/internallink.js';
import { COMMAND_NAME as INCLUDE_NOTE_COMMAND } from './plugins/includenote.js';
import { COMMAND_NAME as MARKDOWN_IMPORT_COMMAND } from './plugins/markdownimport.js';
import { ADMONITION_TYPES, type AdmonitionType } from '@triliumnext/ckeditor5-admonition';
import dateTimeIcon from './icons/date-time.svg?raw';
import internalLinkIcon from './icons/trilium.svg?raw';
import noteIcon from './icons/note.svg?raw';
import importMarkdownIcon from './icons/markdown-mark.svg?raw';
import { icons as mathIcons, MathUI } from '@triliumnext/ckeditor5-math';

type SlashCommandDefinition = SlashCommandEditorConfig["extraCommands"][number];

export default function buildExtraCommands(): SlashCommandDefinition[] {
return [
...buildAlignmentExtraCommands(),
...buildAdmonitionExtraCommands(),
{
id: 'footnote',
title: 'Footnote',
description: 'Create a new footnote and reference it here',
icon: footnoteIcons.insertFootnoteIcon,
commandName: "InsertFootnote"
},
{
id: "datetime",
title: "Insert Date/Time",
description: "Insert the current date and time",
icon: dateTimeIcon,
commandName: INSERT_DATE_TIME_COMMAND
},
{
id: "internal-link",
title: "Internal Trilium link",
description: "Insert a link to another Trilium note",
aliases: [ "internal link", "trilium link", "reference link" ],
icon: internalLinkIcon,
commandName: INTERNAL_LINK_COMMAND
},
{
id: "math",
title: "Math equation",
description: "Insert a math equation",
icon: mathIcons.ckeditor,
execute: (editor: Editor) => editor.plugins.get(MathUI)._showUI()
},
{
id: "include-note",
title: "Include note",
description: "Display the content of another note in this note",
icon: noteIcon,
commandName: INCLUDE_NOTE_COMMAND
},
{
id: "page-break",
title: "Page break",
description: "Insert a page break (for printing)",
icon: IconPageBreak,
commandName: "pageBreak"
},
{
id: "markdown-import",
title: "Markdown import",
description: "Import a markdown file into this note",
icon: importMarkdownIcon,
commandName: MARKDOWN_IMPORT_COMMAND
}
];
}

function buildAlignmentExtraCommands(): SlashCommandDefinition[] {
return [
{
id: "align-left",
title: "Align Left",
description: "Align text to the left",
icon: IconAlignLeft,
execute: (editor: Editor) => editor.execute("alignment", { value: "left" }),
},
{
id: "align-center",
title: "Align Center",
description: "Align text to the center",
icon: IconAlignCenter,
execute: (editor: Editor) => editor.execute("alignment", { value: "center" }),
},
{
id: "align-right",
title: "Align Right",
description: "Align text to the right",
icon: IconAlignRight,
execute: (editor: Editor) => editor.execute("alignment", { value: "right" }),
},
{
id: "align-justify",
title: "Justify",
description: "Justify text alignment",
icon: IconAlignJustify,
execute: (editor: Editor) => editor.execute("alignment", { value: "justify" }),
}
];
}

function buildAdmonitionExtraCommands(): SlashCommandDefinition[] {
const commands: SlashCommandDefinition[] = [];
const admonitionIcons: Record<AdmonitionType, string> = {
note: bxInfoCircle,
tip: bxBulb,
important: bxCommentError,
caution: bxErrorCircle,
warning: bxError,
};

for (const [ keyword, definition ] of Object.entries(ADMONITION_TYPES)) {
commands.push({
id: keyword,
title: definition.title,
description: "Inserts a new admonition",
icon: admonitionIcons[keyword as AdmonitionType],
execute: (editor: Editor) => editor.execute("admonition", { forceValue: keyword as AdmonitionType }),
aliases: [ "box" ]
});
}
return commands;
}

2 changes: 2 additions & 0 deletions packages/ckeditor5/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { COMMON_PLUGINS, CORE_PLUGINS, POPUP_EDITOR_PLUGINS } from "./plugins";
import { BalloonEditor, DecoupledEditor, FindAndReplaceEditing, FindCommand } from "ckeditor5";
export { EditorWatchdog } from "ckeditor5";
export type { EditorConfig, MentionFeed, MentionFeedObjectItem, Node, Position, Element, WatchdogConfig } from "ckeditor5";
export { default as buildExtraCommands } from "./extra_slash_commands.js";

// Import with sideffects to ensure that type augmentations are present.
import "@triliumnext/ckeditor5-math";
Expand All @@ -25,6 +26,7 @@ export type FindCommandResult = ReturnType<FindCommand["execute"]>;
* The text editor that can be used for editing attributes and relations.
*/
export class AttributeEditor extends BalloonEditor {

static override get builtinPlugins() {
return CORE_PLUGINS;
}
Expand Down
11 changes: 10 additions & 1 deletion packages/ckeditor5/src/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,4 +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 { SlashCommand } from "ckeditor5-premium-features";
import type { Plugin } from "ckeditor5";
import CutToNotePlugin from "./plugins/cuttonote.js";
import UploadimagePlugin from "./plugins/uploadimage.js";
Expand Down Expand Up @@ -77,6 +78,13 @@ export const CORE_PLUGINS: typeof Plugin[] = [
ReferenceLink
];

/**
* Plugins that require a premium CKEditor license key to work.
*/
export const PREMIUM_PLUGINS: typeof Plugin[] = [
SlashCommand
];

/**
* The set of plugins that are required for the editor to work. This is used in normal text editors (floating or fixed toolbar) but not in the attribute editor.
*/
Expand Down Expand Up @@ -139,7 +147,8 @@ export const COMMON_PLUGINS: typeof Plugin[] = [
Emoji,

...TRILIUM_PLUGINS,
...EXTERNAL_PLUGINS
...EXTERNAL_PLUGINS,
...PREMIUM_PLUGINS
];

/**
Expand Down
Loading
Loading