Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit 0325bee

Browse files
committed
feat(ckeditor): fallback to GPL if license key fails
1 parent e280968 commit 0325bee

File tree

5 files changed

+253
-234
lines changed

5 files changed

+253
-234
lines changed

apps/client/src/widgets/type_widgets/ckeditor/config.ts

Lines changed: 75 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,64 @@ import { buildExtraCommands, type EditorConfig } from "@triliumnext/ckeditor5";
44
import { getHighlightJsNameForMime } from "../../../services/mime_types.js";
55
import options from "../../../services/options.js";
66
import { ensureMimeTypesForHighlighting, isSyntaxHighlightEnabled } from "../../../services/syntax_highlight.js";
7-
import utils from "../../../services/utils.js";
87
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
98
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
109
import getTemplates from "./snippets.js";
1110
import { PREMIUM_PLUGINS } from "../../../../../../packages/ckeditor5/src/plugins.js";
11+
import { t } from "../../../services/i18n.js";
12+
import { getMermaidConfig } from "../../../services/mermaid.js";
13+
import noteAutocompleteService, { type Suggestion } from "../../../services/note_autocomplete.js";
14+
import mimeTypesService from "../../../services/mime_types.js";
15+
import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons";
16+
import { buildToolbarConfig } from "./toolbar.js";
1217

13-
const OPEN_SOURCE_LICENSE_KEY = "GPL";
18+
export const OPEN_SOURCE_LICENSE_KEY = "GPL";
1419

15-
const TEXT_FORMATTING_GROUP = {
16-
label: "Text formatting",
17-
icon: "text"
18-
};
20+
export interface BuildEditorOptions {
21+
forceGplLicense: boolean;
22+
isClassicEditor: boolean;
23+
contentLanguage: string | null;
24+
}
1925

20-
export async function buildConfig(): Promise<EditorConfig> {
21-
const licenseKey = getLicenseKey();
26+
export async function buildConfig(opts: BuildEditorOptions): Promise<EditorConfig> {
27+
const licenseKey = (opts.forceGplLicense ? OPEN_SOURCE_LICENSE_KEY : getLicenseKey());
2228
const hasPremiumLicense = (licenseKey !== OPEN_SOURCE_LICENSE_KEY);
2329

2430
const config: EditorConfig = {
2531
licenseKey,
32+
placeholder: t("editable_text.placeholder"),
33+
mention: {
34+
feeds: [
35+
{
36+
marker: "@",
37+
feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
38+
itemRenderer: (item) => {
39+
const itemElement = document.createElement("button");
40+
41+
itemElement.innerHTML = `${(item as Suggestion).highlightedNotePathTitle} `;
42+
43+
return itemElement;
44+
},
45+
minimumCharacters: 0
46+
}
47+
],
48+
},
49+
codeBlock: {
50+
languages: buildListOfLanguages()
51+
},
52+
math: {
53+
engine: "katex",
54+
outputType: "span", // or script
55+
lazyLoad: async () => {
56+
(window as any).katex = (await import("../../../services/math.js")).default
57+
},
58+
forceOutputType: false, // forces output to use outputType
59+
enablePreview: true // Enable preview view
60+
},
61+
mermaid: {
62+
lazyLoad: async () => (await import("mermaid")).default, // FIXME
63+
config: getMermaidConfig()
64+
},
2665
image: {
2766
styles: {
2867
options: [
@@ -137,160 +176,51 @@ export async function buildConfig(): Promise<EditorConfig> {
137176
template: {
138177
definitions: await getTemplates()
139178
},
179+
htmlSupport: {
180+
allow: JSON.parse(options.get("allowedHtmlTags"))
181+
},
140182
// This value must be kept in sync with the language defined in webpack.config.js.
141183
language: "en"
142184
};
143185

186+
// Set up content language.
187+
const { contentLanguage } = opts;
188+
if (contentLanguage) {
189+
config.language = {
190+
ui: (typeof config.language === "string" ? config.language : "en"),
191+
content: contentLanguage
192+
}
193+
}
194+
144195
// Enable premium plugins.
145196
if (hasPremiumLicense) {
146197
config.extraPlugins = [
147198
...PREMIUM_PLUGINS
148199
];
149200
}
150201

151-
return config;
152-
}
153-
154-
export function buildToolbarConfig(isClassicToolbar: boolean) {
155-
if (utils.isMobile()) {
156-
return buildMobileToolbar();
157-
} else if (isClassicToolbar) {
158-
const multilineToolbar = utils.isDesktop() && options.get("textNoteEditorMultilineToolbar") === "true";
159-
return buildClassicToolbar(multilineToolbar);
160-
} else {
161-
return buildFloatingToolbar();
162-
}
163-
}
164-
165-
export function buildMobileToolbar() {
166-
const classicConfig = buildClassicToolbar(false);
167-
const items: string[] = [];
168-
169-
for (const item of classicConfig.toolbar.items) {
170-
if (typeof item === "object" && "items" in item) {
171-
for (const subitem of item.items) {
172-
items.push(subitem);
173-
}
174-
} else {
175-
items.push(item);
176-
}
177-
}
178-
179202
return {
180-
...classicConfig,
181-
toolbar: {
182-
...classicConfig.toolbar,
183-
items
184-
}
203+
...config,
204+
...buildToolbarConfig(opts.isClassicEditor)
185205
};
186206
}
187207

188-
export function buildClassicToolbar(multilineToolbar: boolean) {
189-
// For nested toolbars, refer to https://ckeditor.com/docs/ckeditor5/latest/getting-started/setup/toolbar.html#grouping-toolbar-items-in-dropdowns-nested-toolbars.
190-
return {
191-
toolbar: {
192-
items: [
193-
"heading",
194-
"fontSize",
195-
"|",
196-
"bold",
197-
"italic",
198-
{
199-
...TEXT_FORMATTING_GROUP,
200-
items: ["underline", "strikethrough", "|", "superscript", "subscript", "|", "kbd"]
201-
},
202-
"|",
203-
"fontColor",
204-
"fontBackgroundColor",
205-
"removeFormat",
206-
"|",
207-
"bulletedList",
208-
"numberedList",
209-
"todoList",
210-
"|",
211-
"blockQuote",
212-
"admonition",
213-
"insertTable",
214-
"|",
215-
"code",
216-
"codeBlock",
217-
"|",
218-
"footnote",
219-
{
220-
label: "Insert",
221-
icon: "plus",
222-
items: ["imageUpload", "|", "link", "bookmark", "internallink", "includeNote", "|", "specialCharacters", "emoji", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
223-
},
224-
"|",
225-
"alignment",
226-
"outdent",
227-
"indent",
228-
"|",
229-
"insertTemplate",
230-
"markdownImport",
231-
"cuttonote",
232-
"findAndReplace"
233-
],
234-
shouldNotGroupWhenFull: multilineToolbar
235-
}
236-
};
237-
}
208+
function buildListOfLanguages() {
209+
const userLanguages = mimeTypesService
210+
.getMimeTypes()
211+
.filter((mt) => mt.enabled)
212+
.map((mt) => ({
213+
language: normalizeMimeTypeForCKEditor(mt.mime),
214+
label: mt.title
215+
}));
238216

239-
export function buildFloatingToolbar() {
240-
return {
241-
toolbar: {
242-
items: [
243-
"fontSize",
244-
"bold",
245-
"italic",
246-
"underline",
247-
{
248-
...TEXT_FORMATTING_GROUP,
249-
items: [ "strikethrough", "|", "superscript", "subscript", "|", "kbd" ]
250-
},
251-
"|",
252-
"fontColor",
253-
"fontBackgroundColor",
254-
"|",
255-
"code",
256-
"link",
257-
"bookmark",
258-
"removeFormat",
259-
"internallink",
260-
"cuttonote"
261-
]
217+
return [
218+
{
219+
language: mimeTypesService.MIME_TYPE_AUTO,
220+
label: t("editable-text.auto-detect-language")
262221
},
263-
264-
blockToolbar: [
265-
"heading",
266-
"|",
267-
"bulletedList",
268-
"numberedList",
269-
"todoList",
270-
"|",
271-
"blockQuote",
272-
"admonition",
273-
"codeBlock",
274-
"insertTable",
275-
"footnote",
276-
{
277-
label: "Insert",
278-
icon: "plus",
279-
items: ["link", "bookmark", "internallink", "includeNote", "|", "math", "mermaid", "horizontalLine", "pageBreak", "dateTime"]
280-
},
281-
"|",
282-
"alignment",
283-
"outdent",
284-
"indent",
285-
"|",
286-
"insertTemplate",
287-
"imageUpload",
288-
"markdownImport",
289-
"specialCharacters",
290-
"emoji",
291-
"findAndReplace"
292-
]
293-
};
222+
...userLanguages
223+
];
294224
}
295225

296226
function getLicenseKey() {

apps/client/src/widgets/type_widgets/ckeditor/config.spec.ts renamed to apps/client/src/widgets/type_widgets/ckeditor/toolbar.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { buildClassicToolbar, buildFloatingToolbar } from "./config.js";
2+
import { buildClassicToolbar, buildFloatingToolbar } from "./toolbar.js";
33

44
type ToolbarConfig = string | "|" | { items: ToolbarConfig[] };
55

0 commit comments

Comments
 (0)