diff --git a/docs/assets/v0.63.6/app-dist/share.js b/docs/assets/v0.63.6/app-dist/share.js
new file mode 100644
index 000000000..03ad92515
--- /dev/null
+++ b/docs/assets/v0.63.6/app-dist/share.js
@@ -0,0 +1,23 @@
+/**
+ * Fetch note with given ID from backend
+ *
+ * @param noteId of the given note to be fetched. If false, fetches current note.
+ */
+async function fetchNote(noteId = null) {
+ if (!noteId) {
+ noteId = document.body.getAttribute("data-note-id");
+ }
+
+ const resp = await fetch(`api/notes/${noteId}`);
+
+ return await resp.json();
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+ const toggleMenuButton = document.getElementById('toggleMenuButton');
+ const layout = document.getElementById('layout');
+
+ if (toggleMenuButton && layout) {
+ toggleMenuButton.addEventListener('click', () => layout.classList.toggle('showMenu'));
+ }
+}, false);
diff --git a/docs/assets/v0.63.6/libraries/ckeditor/ckeditor-content.css b/docs/assets/v0.63.6/libraries/ckeditor/ckeditor-content.css
new file mode 100644
index 000000000..47274e5f4
--- /dev/null
+++ b/docs/assets/v0.63.6/libraries/ckeditor/ckeditor-content.css
@@ -0,0 +1,551 @@
+/* !!!!!! 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;
+}
+
+/*
+ * 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;
+ color: hsl(0, 0%, 20.8%);
+ background: hsla(0, 0%, 78%, 0.3);
+ border: 1px solid hsl(0, 0%, 77%);
+ border-radius: 2px;
+ text-align: left;
+ direction: ltr;
+ tab-size: 4;
+ white-space: pre-wrap;
+ font-style: normal;
+ min-width: 200px;
+}
+/* @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;
+ }
+}
diff --git a/docs/assets/v0.63.6/libraries/normalize.min.css b/docs/assets/v0.63.6/libraries/normalize.min.css
new file mode 100644
index 000000000..87aa24dc5
--- /dev/null
+++ b/docs/assets/v0.63.6/libraries/normalize.min.css
@@ -0,0 +1,2 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
+/*# sourceMappingURL=normalize.min.css.map */
\ No newline at end of file
diff --git a/docs/assets/v0.63.6/stylesheets/share.css b/docs/assets/v0.63.6/stylesheets/share.css
new file mode 100644
index 000000000..718f8594c
--- /dev/null
+++ b/docs/assets/v0.63.6/stylesheets/share.css
@@ -0,0 +1,165 @@
+body {
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
+ line-height: 1.5;
+}
+
+#layout {
+ max-width: 1200px;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: row-reverse;
+}
+
+#menu {
+ padding: 25px;
+ flex-basis: 0;
+ flex-grow: 1;
+ overflow: auto;
+}
+
+#menu p {
+ margin: 0;
+}
+
+#menu > p {
+ font-weight: bold;
+ font-size: 110%;
+}
+
+#menu ul {
+ padding-left: 20px;
+}
+
+#main {
+ flex-basis: 0;
+ flex-grow: 3;
+ overflow: auto;
+ padding: 10px 20px 20px 20px;
+}
+
+#parentLink {
+ float: right;
+ margin-top: 20px;
+}
+
+#title {
+ margin: 0;
+ padding-top: 10px;
+}
+
+img {
+ max-width: 100%;
+}
+
+pre {
+ white-space: pre-wrap;
+ word-wrap: anywhere;
+}
+
+iframe.pdf-view {
+ width: 100%;
+ height: 800px;
+}
+
+#toggleMenuButton {
+ display: none;
+ position: fixed;
+ top: 8px;
+ left: 5px;
+ width: 1.4em;
+ border-radius: 5px;
+ border: 1px solid #aaa;
+ font-size: 2rem;
+ z-index: 10;
+ height: auto;
+ color: black;
+ cursor: pointer;
+}
+
+#childLinks.grid ul {
+ list-style-type: none;
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0;
+}
+
+#childLinks.grid ul li {
+ width: 180px;
+ height: 140px;
+ padding: 10px;
+}
+
+#childLinks.grid ul li a {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ width: 100%;
+ border: 1px solid #ddd;
+ border-radius: 5px;
+ justify-content: center;
+ align-content: center;
+ text-align: center;
+ font-size: large;
+}
+
+#childLinks.grid ul li a:hover {
+ background: #eee;
+}
+
+#childLinks.list ul {
+ list-style-type: none;
+ display: inline-flex;
+ flex-wrap: wrap;
+ padding: 0;
+ margin-top: 5px;
+}
+
+#childLinks.list ul li {
+ margin-right: 20px;
+}
+
+#noteClippedFrom {
+ padding: 10px 0 10px 0;
+ margin: 20px 0 20px 0;
+ color: #666;
+ border: 1px solid #ddd;
+ border-left: 0;
+ border-right: 0;
+}
+
+#toggleMenuButton::after {
+ position: relative;
+ top: -2px;
+ left: 1px;
+}
+
+@media (max-width: 48em) {
+ #layout.showMenu #menu {
+ display: block;
+ margin-top: 40px;
+ }
+
+ #toggleMenuButton {
+ display: block;
+ }
+
+ #layout.showMenu #main {
+ display: none;
+ }
+
+ #title {
+ padding-left: 60px;
+ }
+
+ #layout.showMenu #toggleMenuButton::after {
+ content: "«";
+ }
+
+ #toggleMenuButton::after {
+ content: "»";
+ }
+
+ #menu {
+ display: none;
+ }
+}
diff --git a/docs/favicon.ico b/docs/favicon.ico
new file mode 100644
index 000000000..46b97e62f
Binary files /dev/null and b/docs/favicon.ico differ
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 000000000..974b29b47
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/share/4yYHqKbLovVX b/docs/share/4yYHqKbLovVX
new file mode 100644
index 000000000..40fbd79e4
--- /dev/null
+++ b/docs/share/4yYHqKbLovVX
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Developer's Guide
+
+
+
+
+
+
+
Developer's Guide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/share/QXCi6Y1SYulw b/docs/share/QXCi6Y1SYulw
new file mode 100644
index 000000000..22caad28f
--- /dev/null
+++ b/docs/share/QXCi6Y1SYulw
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Adding a new client library
+
+
+
+
+
+
+ parent: Dependency Management
+
+
+
+
Adding a new client library
+
+
+
+
+
+
In the past some libraries have been copy-pasted (and adapted if needed) to the repository. However, new libraries must be obtained exclusively through npm.
The first step is to install the desired library. As an example we are going to install i18next
:
npm i i18next
Step 1. Understanding the structure of the import After installing the dependency, it's important to know how it's structured. You can do this by looking at the directory structure of the newly imported dependency:
$ tree node_modules/i18next
+node_modules/i18next
+├── dist
+│ ├── cjs
+│ │ └── i18next.js
+│ ├── esm
+│ │ ├── i18next.bundled.js
+│ │ ├── i18next.js
+│ │ └── package.json
+│ └── umd
+│ ├── i18next.js
+│ └── i18next.min.js
+├── i18next.js
+├── i18next.min.js
+├── index.d.mts
+├── index.d.ts
+├── index.js
+├── index.v4.d.ts
+├── LICENSE
+├── package.json
+├── README.md
+└── typescript
+ ├── helpers.d.ts
+ ├── options.d.ts
+ ├── t.d.ts
+ └── t.v4.d.ts
Generally you should be looking for a .min.js
file. Note that the esm
and cjs
variants generally don't work, we are looking for the classic, no module dependency.
Step 2. Exposing the library from the server The library must be delivered by the server and this is done via src/routes/assets.ts
. In the register
function, add a new entry near the bottom of the function:
app.use(`/${assetPath}/node_modules/i18next/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next/')));
Step 3. Adding it to the library loader The library loader is a client module which is in charge of downloading the library from the server and importing it. The loader is located in src/public/app/services/library_loader.js
.
To add a new library, start by creating a constant for it, with the value pointing to the minified JS identified at the first step:
const I18NEXT = {
+ js: [
+ "node_modules/i18next/i18next.min.js"
+ ]
+};
Then add it to the export default
section:
export default {
+ requireCss,
+ requireLibrary,
+ CKEDITOR,
+ CODE_MIRROR,
+ ESLINT,
+ RELATION_MAP,
+ PRINT_THIS,
+ CALENDAR_WIDGET,
+ KATEX,
+ WHEEL_ZOOM,
+ FORCE_GRAPH,
+ MERMAID,
+ EXCALIDRAW,
+- MARKJS
++ MARKJS,
++ I18NEXT
+ }
Step 4. Using the library To import the library, simply use the following mechanism:
import library_loader from "./library_loader.js";
+
+await library_loader.requireLibrary(library_loader.I18NEXT);
Make sure to replace I18NEXT
with the library that was created at the previous steps.
Note that because we are not using a module management mechanism such as ES Modules or Common.js modules, the requireLibrary
method does not actually return anything.
To benefit from the library, it must export on its own an object in window
.
In the case of i18next
, it sets window.i18next
and that can be used directly:
i18next.init({});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/share/VS22Hq5PBFNf b/docs/share/VS22Hq5PBFNf
new file mode 100644
index 000000000..cf656981e
--- /dev/null
+++ b/docs/share/VS22Hq5PBFNf
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dependency Management
+
+
+
+
+
+
+ parent: Developer's Guide
+
+
+
+
Dependency Management
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/share/hkrBX8KE1HQl b/docs/share/hkrBX8KE1HQl
new file mode 100644
index 000000000..3cd51c05f
--- /dev/null
+++ b/docs/share/hkrBX8KE1HQl
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Internationalisation
+
+
+
+
+
+
+ parent: Developer's Guide
+
+
+
+
Internationalisation
+
+
+
+
+
+
During the initial development of Trilium Notes, internationalisation was not considered as it was meant to be an English-only product.
As the application and the user base grows, it makes sense to be able to reach out as many people as possible by providing translations in their native language.
The library used is i18next .
What has been implemented so far Where are the translations? The translations are formatted as JSON files and they are located in src/public/translations
. For every supported locale, there is a subdirectory in which there is a translation.json
file (e.g. src/public/translations/en/translation.json
).
Message keys One important aspect is the fact that we are using a key-based approach. This means that each message is identified by an ID rather than a natural-language message (such as the default approach in gettext).
The key-based approach allows a hierarchical structure. For example, a key of about.title
would be added in translation.json
as follows:
{
+ "about": {
+ "title": "About TriliumNext Notes"
+ }
+}
Adding a new locale To add a new locale, go to src/public/translations
with your favorite text editor and copy the en
directory.
Rename the copy to the ISO code (e.g. fr
, ro
) of the language being translated.
Translations with a country-language combination, using their corresponding ISO code (e.g. fr_FR
, fr_BE
), has not been tested yet.
Changing the language Since the internationalisation process is in its early stages, there is no user-facing way to switch the language.
To change the language manually, edit src/public/app/services/i18n.js
and look for the line containing lng: "en"
. Replace en
with the desired language code (from the ones available in src/public/translations
).
Recommendations Use hierarchy whenever appropriate, try to group the messages by:Modals (e.g. about.foo
, jump_to_note.foo
) Don't duplicate messages that are very widely used.One such example is aria-label="Close"
which should go to a single message such as modal.close
instead of being duplicated in every modal. On the other hand, don't overly generalise messages. A close
message that is used whenever the “Close” word is encountered is not a good approach since it can potentially cause issues due to lack of context. Use variable interpolation whenever appropriate.If you see multiple messages joined together only to apply add a variable such as a user-inputted value, try to join those messages together into a single message containing a variable. So instead of “Number of updates: “ + numUpdates + “.”
use $(t("number_updates", { numUpdates }))
where the message translation would appear as Number of updates: {{numUpdates}}.
Client-side translations Component-level translations Most of the client translations are present in the various widgets and layouts.
Translation support has to be added manually for every file.
The first step is to add the translation import with a relative import. For example, if we are in the src/public/app/widgets/dialogs
directory, the import would look as follows:
import { t } from "../../services/i18n.js";
Afterwards, simply replace the hard-coded message with:
${t("msgid")}
where msgid
is the key of the message being translated.
Template-level translations Templates are .ejs
files present in src/views
, these are used to prepare the root layout for desktop, mobile applications as well as setup (onboarding) and the shared notes view.
Due to using a different approach, it is not possible yet to translate those files.
Server-side translations Currently the server-side messages are not translatable. They will be added as a separate step.
+
+
+
+
+
+
+
+
+
+
+
+
+
+