chore(ckeditor5): first integration into client (WIP)

This commit is contained in:
Elian Doran
2025-05-03 12:39:34 +03:00
parent ba67812101
commit e1af7eba93
14 changed files with 93 additions and 134 deletions
+1
View File
@@ -22,6 +22,7 @@
"@mind-elixir/node-menu": "1.0.5",
"@popperjs/core": "2.11.8",
"@triliumnext/commons": "workspace:*",
"@triliumnext/ckeditor5": "workspace:*",
"bootstrap": "5.3.5",
"dayjs": "1.11.13",
"dayjs-plugin-utc": "0.1.2",
-593
View File
@@ -1,593 +0,0 @@
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
.printed-content .ck-widget__selection-handle, .printed-content .ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
display: none;
}
.page-break {
page-break-after: always;
}
.printed-content .page-break:after,
.printed-content .page-break > * {
display: none !important;
}
.ck-content li p {
margin: 0 !important;
}
.admonition {
--accent-color: var(--card-border-color);
border: 1px solid var(--accent-color);
box-shadow: var(--card-box-shadow);
background: var(--card-background-color);
border-radius: 0.5em;
padding: 1em;
margin: 1.25em 0;
position: relative;
overflow: hidden;
}
.admonition p:last-child {
margin-bottom: 0;
}
.admonition p, h2 {
margin-top: 0;
}
.admonition.note { --accent-color: #69c7ff; }
.admonition.tip { --accent-color: #40c025; }
.admonition.important { --accent-color: #9839f7; }
.admonition.caution { --accent-color: #ff2e2e; }
.admonition.warning { --accent-color: #e2aa03; }
/*
* CKEditor 5 (v41.0.0) content styles.
* Generated on Fri, 26 Jan 2024 10:23:49 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html
*/
:root {
--ck-color-image-caption-background: hsl(0, 0%, 97%);
--ck-color-image-caption-text: hsl(0, 0%, 20%);
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
--ck-color-mention-text: hsl(341, 100%, 30%);
--ck-color-selector-caption-background: hsl(0, 0%, 97%);
--ck-color-selector-caption-text: hsl(0, 0%, 20%);
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
--ck-highlight-marker-green: hsl(120, 93%, 68%);
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
--ck-highlight-marker-yellow: hsl(60, 97%, 73%);
--ck-highlight-pen-green: hsl(112, 100%, 27%);
--ck-highlight-pen-red: hsl(0, 85%, 49%);
--ck-image-style-spacing: 1.5em;
--ck-inline-image-style-spacing: calc(var(--ck-image-style-spacing) / 2);
--ck-todo-list-checkmark-size: 16px;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table .ck-table-resized {
table-layout: fixed;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table table {
overflow: hidden;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table td,
.ck-content .table th {
overflow-wrap: break-word;
position: relative;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 0.9em auto;
display: table;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* @ckeditor/ckeditor5-table/theme/tablecaption.css */
.ck-content .table > figcaption {
display: table-caption;
caption-side: top;
word-break: break-word;
text-align: center;
color: var(--ck-color-selector-caption-text);
background-color: var(--ck-color-selector-caption-background);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 0.9em 0;
display: block;
min-width: 15em;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
list-style: none;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list li {
position: relative;
margin-bottom: 5px;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list li .todo-list {
margin-top: 5px;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input {
-webkit-appearance: none;
display: inline-block;
position: relative;
width: var(--ck-todo-list-checkmark-size);
height: var(--ck-todo-list-checkmark-size);
vertical-align: middle;
border: 0;
left: -25px;
margin-right: -15px;
right: 0;
margin-left: 0;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content[dir=rtl] .todo-list .todo-list__label > input {
left: 0;
margin-right: 0;
right: -25px;
margin-left: -15px;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input::before {
display: block;
position: absolute;
box-sizing: border-box;
content: '';
width: 100%;
height: 100%;
border: 1px solid hsl(0, 0%, 20%);
border-radius: 2px;
transition: 250ms ease-in-out box-shadow;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input::after {
display: block;
position: absolute;
box-sizing: content-box;
pointer-events: none;
content: '';
left: calc( var(--ck-todo-list-checkmark-size) / 3 );
top: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
width: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
height: calc( var(--ck-todo-list-checkmark-size) / 2.6 );
border-style: solid;
border-color: transparent;
border-width: 0 calc( var(--ck-todo-list-checkmark-size) / 8 ) calc( var(--ck-todo-list-checkmark-size) / 8 ) 0;
transform: rotate(45deg);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input[checked]::before {
background: hsl(126, 64%, 41%);
border-color: hsl(126, 64%, 41%);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label > input[checked]::after {
border-color: hsl(0, 0%, 100%);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list .todo-list__label.todo-list__label_without-description input[type=checkbox] {
position: absolute;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > input,
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input {
cursor: pointer;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > input:hover::before, .ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input:hover::before {
box-shadow: 0 0 0 5px hsla(0, 0%, 0%, 0.1);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input {
-webkit-appearance: none;
display: inline-block;
position: relative;
width: var(--ck-todo-list-checkmark-size);
height: var(--ck-todo-list-checkmark-size);
vertical-align: middle;
border: 0;
left: -25px;
margin-right: -15px;
right: 0;
margin-left: 0;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content[dir=rtl] .todo-list .todo-list__label > span[contenteditable=false] > input {
left: 0;
margin-right: 0;
right: -25px;
margin-left: -15px;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input::before {
display: block;
position: absolute;
box-sizing: border-box;
content: '';
width: 100%;
height: 100%;
border: 1px solid hsl(0, 0%, 20%);
border-radius: 2px;
transition: 250ms ease-in-out box-shadow;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input::after {
display: block;
position: absolute;
box-sizing: content-box;
pointer-events: none;
content: '';
left: calc( var(--ck-todo-list-checkmark-size) / 3 );
top: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
width: calc( var(--ck-todo-list-checkmark-size) / 5.3 );
height: calc( var(--ck-todo-list-checkmark-size) / 2.6 );
border-style: solid;
border-color: transparent;
border-width: 0 calc( var(--ck-todo-list-checkmark-size) / 8 ) calc( var(--ck-todo-list-checkmark-size) / 8 ) 0;
transform: rotate(45deg);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input[checked]::before {
background: hsl(126, 64%, 41%);
border-color: hsl(126, 64%, 41%);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label > span[contenteditable=false] > input[checked]::after {
border-color: hsl(0, 0%, 100%);
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-editor__editable.ck-content .todo-list .todo-list__label.todo-list__label_without-description input[type=checkbox] {
position: absolute;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol {
list-style-type: decimal;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol {
list-style-type: lower-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol {
list-style-type: lower-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol {
list-style-type: upper-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol ol {
list-style-type: upper-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul {
list-style-type: disc;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul {
list-style-type: circle;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
text-align: center;
margin: 0.9em auto;
min-width: 50px;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 100%;
height: auto;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline {
/*
* Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).;
* Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root.
* This strange behavior does not happen with inline-flex.
*/
display: inline-flex;
max-width: 100%;
align-items: flex-start;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture {
display: flex;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture,
.ck-content .image-inline img {
flex-grow: 1;
flex-shrink: 1;
max-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content img.image_resized {
height: auto;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
display: block;
}
/* @ckeditor/ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: var(--ck-color-image-caption-text);
background-color: var(--ck-color-image-caption-background);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left,
.ck-content .image-style-block-align-right {
max-width: calc(100% - var(--ck-image-style-spacing));
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left,
.ck-content .image-style-align-right {
clear: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-right {
margin-right: 0;
margin-left: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left {
margin-left: 0;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content p + .image-style-align-left,
.ck-content p + .image-style-align-right,
.ck-content p + .image-style-side {
margin-top: 0;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left,
.ck-content .image-inline.image-style-align-right {
margin-top: var(--ck-inline-image-style-spacing);
margin-bottom: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left {
margin-right: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-right {
margin-left: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
/* @ckeditor/ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
/* @ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* @ckeditor/ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
padding: 1em;
text-align: left;
direction: ltr;
tab-size: 4;
white-space: pre-wrap;
font-style: normal;
min-width: 200px;
border: 0px;
border-radius: 6px;
box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.2);
}
.ck-content pre:not(.hljs) {
color: hsl(0, 0%, 20.8%);
background: hsla(0, 0%, 78%, 0.3);
}
/* @ckeditor/ckeditor5-code-block/theme/codeblock.css */
.ck-content pre code {
background: unset;
padding: 0;
border-radius: 0;
}
@media print {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
padding: 0;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
display: none;
}
}
-49
View File
@@ -1,49 +0,0 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import { DecoupledEditor as DecoupledEditorBase } from '@ckeditor/ckeditor5-editor-decoupled';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Alignment } from '@ckeditor/ckeditor5-alignment';
import { FontSize, FontFamily, FontColor, FontBackgroundColor } from '@ckeditor/ckeditor5-font';
import { CKFinderUploadAdapter } from '@ckeditor/ckeditor5-adapter-ckfinder';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic, Strikethrough, Underline } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { CKBox } from '@ckeditor/ckeditor5-ckbox';
import { CKFinder } from '@ckeditor/ckeditor5-ckfinder';
import { EasyImage } from '@ckeditor/ckeditor5-easy-image';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Image, ImageCaption, ImageResize, ImageStyle, ImageToolbar, ImageUpload, PictureEditing } from '@ckeditor/ckeditor5-image';
import { Indent, IndentBlock } from '@ckeditor/ckeditor5-indent';
import { Link } from '@ckeditor/ckeditor5-link';
import { List, ListProperties } from '@ckeditor/ckeditor5-list';
import { MediaEmbed } from '@ckeditor/ckeditor5-media-embed';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { PasteFromOffice } from '@ckeditor/ckeditor5-paste-from-office';
import { Table, TableToolbar } from '@ckeditor/ckeditor5-table';
import { TextTransformation } from '@ckeditor/ckeditor5-typing';
import { CloudServices } from '@ckeditor/ckeditor5-cloud-services';
export default class DecoupledEditor extends DecoupledEditorBase {
static builtinPlugins: (typeof TextTransformation | typeof Essentials | typeof Alignment | typeof FontBackgroundColor | typeof FontColor | typeof FontFamily | typeof FontSize | typeof CKFinderUploadAdapter | typeof Paragraph | typeof Heading | typeof Autoformat | typeof Bold | typeof Italic | typeof Strikethrough | typeof Underline | typeof BlockQuote | typeof Image | typeof ImageCaption | typeof ImageResize | typeof ImageStyle | typeof ImageToolbar | typeof ImageUpload | typeof CloudServices | typeof CKBox | typeof CKFinder | typeof EasyImage | typeof List | typeof ListProperties | typeof Indent | typeof IndentBlock | typeof Link | typeof MediaEmbed | typeof PasteFromOffice | typeof Table | typeof TableToolbar | typeof PictureEditing)[];
static defaultConfig: {
toolbar: {
items: string[];
};
image: {
resizeUnit: "px";
toolbar: string[];
};
table: {
contentToolbar: string[];
};
list: {
properties: {
styles: boolean;
startIndex: boolean;
reversed: boolean;
};
};
language: string;
};
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,10 +7,6 @@ export interface Library {
css?: string[];
}
const CKEDITOR: Library = {
js: ["libraries/ckeditor/ckeditor.js"]
};
const CODE_MIRROR: Library = {
js: () => {
const scriptsToLoad = [
@@ -156,7 +152,6 @@ export default {
requireCss,
requireLibrary,
loadHighlightingTheme,
CKEDITOR,
CODE_MIRROR,
KATEX,
HIGHLIGHT_JS
@@ -4,7 +4,7 @@ import noteAutocompleteService from "../../services/note_autocomplete.js";
import server from "../../services/server.js";
import contextMenuService from "../../menus/context_menu.js";
import attributeParser, { type Attribute } from "../../services/attribute_parser.js";
import libraryLoader from "../../services/library_loader.js";
import { BalloonEditor } from "@triliumnext/ckeditor5";
import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js";
@@ -369,13 +369,11 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget implem
}
async initEditor() {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
this.$widget.show();
this.$editor.on("click", (e) => this.handleEditorClick(e));
this.textEditor = await CKEditor.BalloonEditor.create(this.$editor[0], editorConfig);
this.textEditor = BalloonEditor.create(this.$editor[0], editorConfig);
this.textEditor.model.document.on("change:data", () => this.dataChanged());
this.textEditor.editing.view.document.on(
"enter",
@@ -18,6 +18,7 @@ import { buildSelectedBackgroundColor } from "../../components/touch_bar.js";
import { buildConfig, buildToolbarConfig } from "./ckeditor/config.js";
import type FNote from "../../entities/fnote.js";
import { getMermaidConfig } from "../../services/mermaid.js";
import { BalloonEditor, DecoupledEditor, EditorWatchdog } from "@triliumnext/ckeditor5";
const ENABLE_INSPECTOR = false;
@@ -127,7 +128,7 @@ function buildListOfLanguages() {
export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
private contentLanguage?: string | null;
private watchdog!: CKWatchdog;
private watchdog!: EditorWatchdog<DecoupledEditor | BalloonEditor>;
private $editor!: JQuery<HTMLElement>;
@@ -149,16 +150,15 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
async initEditor() {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
const isClassicEditor = utils.isMobile() || options.get("textNoteEditorType") === "ckeditor-classic";
const editorClass = isClassicEditor ? CKEditor.DecoupledEditor : CKEditor.BalloonEditor;
const editorClass = isClassicEditor ? DecoupledEditor : BalloonEditor;
// 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
// display of $widget in both branches.
this.$widget.show();
this.watchdog = new CKEditor.EditorWatchdog(editorClass, {
this.watchdog = new EditorWatchdog<DecoupledEditor | BalloonEditor>(editorClass, {
// 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
// is also reached, the watchdog changes its state to crashedPermanently, and it stops
@@ -189,7 +189,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (currentState === "crashedPermanently") {
dialogService.info(`Editing component keeps crashing. Please try restarting Trilium. If problem persists, consider creating a bug report.`);
this.watchdog.editor.enableReadOnlyMode("crashed-editor");
this.watchdog.editor?.enableReadOnlyMode("crashed-editor");
}
});
@@ -210,6 +210,8 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const contentLanguage = this.note?.getLabelValue("language");
if (contentLanguage) {
// TODO: Wrong type?
//@ts-ignore
finalConfig.language = {
ui: (typeof finalConfig.language === "string" ? finalConfig.language : "en"),
content: contentLanguage
@@ -219,6 +221,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.contentLanguage = null;
}
//@ts-ignore
const editor = await editorClass.create(elementOrData, finalConfig);
const notificationsPlugin = editor.plugins.get("Notification");
@@ -235,6 +238,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
evt.stop();
});
//@ts-ignore
await initSyntaxHighlighting(editor);
if (isClassicEditor) {
@@ -256,17 +260,18 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
// 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.
const toolbarView = editor.ui.view.toolbar;
const toolbarView = (editor as DecoupledEditor).ui.view.toolbar;
for (const item of toolbarView.items) {
if (!("panelView" in item)) {
continue;
}
item.on("change:isOpen", () => {
if ( !item.isOpen ) {
if (!("isOpen" in item) || !item.isOpen ) {
return;
}
// @ts-ignore
item.panelView.position = item.panelView.position.replace("s", "n");
});
}
@@ -277,14 +282,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (glob.isDev && ENABLE_INSPECTOR) {
// TODO: Check if this still works.
await import(/* webpackIgnore: true */ "../../../libraries/ckeditor/inspector.js");
CKEditorInspector.attach(editor);
// await import(/* webpackIgnore: true */ "../../../libraries/ckeditor/inspector.js");
// CKEditorInspector.attach(editor);
}
// Touch bar integration
if (hasTouchBar) {
for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) {
editor.commands.get(event).on("change", () => this.triggerCommand("refreshTouchBar"));
editor.commands.get(event)?.on("change", () => this.triggerCommand("refreshTouchBar"));
}
}
@@ -297,6 +302,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async createEditor() {
await this.watchdog.create(this.$editor[0], {
placeholder: t("editable_text.placeholder"),
//@ts-ignore TODO: FIX TYPES
mention: mentionSetup,
codeBlock: {
languages: buildListOfLanguages()
@@ -324,13 +330,13 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (this.contentLanguage !== newContentLanguage) {
await this.reinitialize(data);
} else {
this.watchdog.editor.setData(data);
this.watchdog.editor?.setData(data);
}
});
}
getData() {
const content = this.watchdog.editor.getData();
const content = this.watchdog.editor?.getData() ?? "";
// if content is only tags/whitespace (typically <p>&nbsp;</p>), then just make it empty,
// this is important when setting a new note to code
@@ -344,11 +350,14 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
scrollToEnd() {
this.watchdog?.editor.model.change((writer) => {
writer.setSelection(writer.createPositionAt(this.watchdog?.editor.model.document.getRoot(), "end"));
this.watchdog?.editor?.model.change((writer) => {
const rootItem = this.watchdog?.editor?.model.document.getRoot();
if (rootItem) {
writer.setSelection(writer.createPositionAt(rootItem, "end"));
}
});
this.watchdog?.editor.editing.view.focus();
this.watchdog?.editor?.editing.view.focus();
}
show() { }
@@ -360,7 +369,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
cleanup() {
if (this.watchdog?.editor) {
this.spacedUpdate.allowUpdateWithoutChange(() => {
this.watchdog.editor.setData("");
this.watchdog.editor?.setData("");
});
}
}
@@ -375,18 +384,22 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async addLinkToEditor(linkHref: string, linkTitle: string) {
await this.initialized;
this.watchdog.editor.model.change((writer) => {
const insertPosition = this.watchdog.editor.model.document.selection.getFirstPosition();
writer.insertText(linkTitle, { linkHref: linkHref }, insertPosition);
this.watchdog.editor?.model.change((writer) => {
const insertPosition = this.watchdog.editor?.model.document.selection.getFirstPosition();
if (insertPosition) {
writer.insertText(linkTitle, { linkHref: linkHref }, insertPosition);
}
});
}
async addTextToEditor(text: string) {
await this.initialized;
this.watchdog.editor.model.change((writer) => {
const insertPosition = this.watchdog.editor.model.document.selection.getLastPosition();
writer.insertText(text, insertPosition);
this.watchdog.editor?.model.change((writer) => {
const insertPosition = this.watchdog.editor?.model.document.selection.getLastPosition();
if (insertPosition) {
writer.insertText(text, insertPosition);
}
});
}
@@ -403,23 +416,23 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
if (linkTitle) {
if (this.hasSelection()) {
this.watchdog.editor.execute("link", externalLink ? `${notePath}` : `#${notePath}`);
this.watchdog.editor?.execute("link", externalLink ? `${notePath}` : `#${notePath}`);
} else {
await this.addLinkToEditor(externalLink ? `${notePath}` : `#${notePath}`, linkTitle);
}
} else {
this.watchdog.editor.execute("referenceLink", { href: "#" + notePath });
this.watchdog.editor?.execute("referenceLink", { href: "#" + notePath });
}
this.watchdog.editor.editing.view.focus();
this.watchdog.editor?.editing.view.focus();
}
// returns true if user selected some text, false if there's no selection
hasSelection() {
const model = this.watchdog.editor.model;
const selection = model.document.selection;
const model = this.watchdog.editor?.model;
const selection = model?.document.selection;
return !selection.isCollapsed;
return !selection?.isCollapsed;
}
async executeWithTextEditorEvent({ callback, resolve, ntxId }: EventData<"executeWithTextEditor">) {
@@ -443,11 +456,15 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
getSelectedText() {
const range = this.watchdog.editor.model.document.selection.getFirstRange();
const range = this.watchdog.editor?.model.document.selection.getFirstRange();
let text = "";
if (!range) {
return text;
}
for (const item of range.getItems()) {
if (item.data) {
if ("data" in item && item.data) {
text += item.data;
}
}
@@ -458,12 +475,12 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async followLinkUnderCursorCommand() {
await this.initialized;
const selection = this.watchdog.editor.model.document.selection;
const selectedElement = selection.getSelectedElement();
const selection = this.watchdog.editor?.model.document.selection;
const selectedElement = selection?.getSelectedElement();
if (selectedElement?.name === "reference") {
// reference link
const notePath = selectedElement.getAttribute("notePath");
const notePath = selectedElement.getAttribute("notePath") as string | undefined;
if (notePath) {
await appContext.tabManager.getActiveContext()?.setNote(notePath);
@@ -475,7 +492,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return;
}
const selectedLinkUrl = selection.getAttribute("linkHref");
const selectedLinkUrl = selection.getAttribute("linkHref") as string;
const notePath = link.getNotePathFromUrl(selectedLinkUrl);
if (notePath) {
@@ -490,10 +507,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
}
addIncludeNote(noteId: string, boxSize?: string) {
this.watchdog.editor.model.change((writer) => {
this.watchdog.editor?.model.change((writer) => {
// Insert <includeNote>*</includeNote> at the current selection position
// in a way that will result in creating a valid model structure
this.watchdog.editor.model.insertContent(
this.watchdog.editor?.model.insertContent(
writer.createElement("includeNote", {
noteId: noteId,
boxSize: boxSize
@@ -504,7 +521,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
async addImage(noteId: string) {
const note = await froca.getNote(noteId);
if (!note) {
if (!note || !this.watchdog.editor) {
return;
}
@@ -512,7 +529,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const encodedTitle = encodeURIComponent(note.title);
const src = `api/images/${note.noteId}/${encodedTitle}`;
this.watchdog.editor.execute("insertImage", { source: src });
this.watchdog.editor?.execute("insertImage", { source: src });
});
}
@@ -544,12 +561,12 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.watchdog.destroy();
await this.createEditor();
this.watchdog.editor.setData(data);
this.watchdog.editor?.setData(data);
}
async onLanguageChanged() {
const data = this.watchdog.editor.getData();
await this.reinitialize(data);
const data = this.watchdog.editor?.getData();
await this.reinitialize(data ?? "");
}
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
@@ -557,20 +574,24 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
const { TouchBarSegmentedControl, TouchBarGroup, TouchBarButton } = TouchBar;
const { editor } = this.watchdog;
if (!editor) {
return;
}
const commandButton = (icon: string, command: string) => new TouchBarButton({
icon: buildIcon(icon),
click: () => editor.execute(command),
backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command).value as boolean)
backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command)?.value as boolean)
});
let headingSelectedIndex = undefined;
const headingCommand = editor.commands.get("heading");
const paragraphCommand = editor.commands.get("paragraph");
if (paragraphCommand.value) {
if (paragraphCommand?.value) {
headingSelectedIndex = 0;
} else if (headingCommand.value === "heading2") {
} else if (headingCommand?.value === "heading2") {
headingSelectedIndex = 1;
} else if (headingCommand.value === "heading3") {
} else if (headingCommand?.value === "heading3") {
headingSelectedIndex = 2;
}
@@ -581,7 +602,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
{ label: "H2" },
{ label: "H3" }
],
change(selectedIndex, isSelected) {
change(selectedIndex: number, isSelected: boolean) {
switch (selectedIndex) {
case 0:
editor.execute("paragraph")
@@ -100,7 +100,7 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
// we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes
// we could load just ckeditor-content.css but that causes CSS conflicts when both build CSS and this content CSS is loaded at the same time
// (see https://github.com/zadam/trilium/issues/1590 for example of such conflict)
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
await import("@triliumnext/ckeditor5");
this.onLanguageChanged();
+3
View File
@@ -34,6 +34,9 @@
"src/**/*.ts"
],
"references": [
{
"path": "../../packages/ckeditor5/tsconfig.lib.json"
},
{
"path": "../../packages/commons/tsconfig.lib.json"
}
+3
View File
@@ -3,6 +3,9 @@
"files": [],
"include": [],
"references": [
{
"path": "../../packages/ckeditor5"
},
{
"path": "../../packages/commons"
},