Merge branch 'develop' into feat/add-rootless-dockerfiles

pull/1923/head
Elian Doran 2025-05-27 19:34:49 +07:00 committed by GitHub
commit a1dda3c01a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
237 changed files with 2640 additions and 2297 deletions

@ -84,7 +84,7 @@ Download the binary release for your platform from the [latest release page](htt
If your distribution is listed in the table below, use your distribution's package.
[![Packaging status](https://repology.org/badge/vertical-allrepos/trilium-next-desktop.svg)](https://repology.org/project/trilium-next-desktop/versions)
[![Packaging status](https://repology.org/badge/vertical-allrepos/triliumnext.svg)](https://repology.org/project/triliumnext/versions)
You may also download the binary release for your platform from the [latest release page](https://github.com/TriliumNext/Notes/releases/latest), unzip the package and run the `trilium` executable.

@ -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;
}
}

@ -0,0 +1,43 @@
### Test regular API metrics endpoint (requires session authentication)
### Get metrics from regular API (default Prometheus format)
GET {{triliumHost}}/api/metrics
> {%
client.test("API metrics endpoint returns Prometheus format by default", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.headers["content-type"].includes("text/plain"), "Content-Type should be text/plain");
client.assert(response.body.includes("trilium_info"), "Should contain trilium_info metric");
client.assert(response.body.includes("trilium_notes_total"), "Should contain trilium_notes_total metric");
client.assert(response.body.includes("# HELP"), "Should contain HELP comments");
client.assert(response.body.includes("# TYPE"), "Should contain TYPE comments");
});
%}
### Get metrics in JSON format
GET {{triliumHost}}/api/metrics?format=json
> {%
client.test("API metrics endpoint returns JSON when requested", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.headers["content-type"].includes("application/json"), "Content-Type should be application/json");
client.assert(response.body.version, "Version info not present");
client.assert(response.body.database, "Database info not present");
client.assert(response.body.timestamp, "Timestamp not present");
client.assert(typeof response.body.database.totalNotes === 'number', "Total notes should be a number");
client.assert(typeof response.body.database.activeNotes === 'number', "Active notes should be a number");
client.assert(response.body.noteTypes, "Note types breakdown not present");
client.assert(response.body.attachmentTypes, "Attachment types breakdown not present");
client.assert(response.body.statistics, "Statistics not present");
});
%}
### Test invalid format parameter
GET {{triliumHost}}/api/metrics?format=xml
> {%
client.test("Invalid format parameter returns error", function() {
client.assert(response.status === 500, "Response status should be 500");
client.assert(response.body.message.includes("prometheus"), "Error message should mention supported formats");
});
%}

@ -0,0 +1,82 @@
### Test ETAPI metrics endpoint
# First login to get a token
POST {{triliumHost}}/etapi/auth/login
Content-Type: application/json
{
"password": "{{password}}"
}
> {%
client.test("Login successful", function() {
client.assert(response.status === 201, "Response status is not 201");
client.assert(response.body.authToken, "Auth token not present");
client.global.set("authToken", response.body.authToken);
});
%}
### Get metrics with authentication (default Prometheus format)
GET {{triliumHost}}/etapi/metrics
Authorization: {{authToken}}
> {%
client.test("Metrics endpoint returns Prometheus format by default", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.headers["content-type"].includes("text/plain"), "Content-Type should be text/plain");
client.assert(response.body.includes("trilium_info"), "Should contain trilium_info metric");
client.assert(response.body.includes("trilium_notes_total"), "Should contain trilium_notes_total metric");
client.assert(response.body.includes("# HELP"), "Should contain HELP comments");
client.assert(response.body.includes("# TYPE"), "Should contain TYPE comments");
});
%}
### Get metrics in JSON format
GET {{triliumHost}}/etapi/metrics?format=json
Authorization: {{authToken}}
> {%
client.test("Metrics endpoint returns JSON when requested", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.headers["content-type"].includes("application/json"), "Content-Type should be application/json");
client.assert(response.body.version, "Version info not present");
client.assert(response.body.database, "Database info not present");
client.assert(response.body.timestamp, "Timestamp not present");
client.assert(typeof response.body.database.totalNotes === 'number', "Total notes should be a number");
client.assert(typeof response.body.database.activeNotes === 'number', "Active notes should be a number");
});
%}
### Get metrics in Prometheus format explicitly
GET {{triliumHost}}/etapi/metrics?format=prometheus
Authorization: {{authToken}}
> {%
client.test("Metrics endpoint returns Prometheus format when requested", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.headers["content-type"].includes("text/plain"), "Content-Type should be text/plain");
client.assert(response.body.includes("trilium_info"), "Should contain trilium_info metric");
client.assert(response.body.includes("trilium_notes_total"), "Should contain trilium_notes_total metric");
});
%}
### Test invalid format parameter
GET {{triliumHost}}/etapi/metrics?format=xml
Authorization: {{authToken}}
> {%
client.test("Invalid format parameter returns error", function() {
client.assert(response.status === 400, "Response status should be 400");
client.assert(response.body.code === "INVALID_FORMAT", "Error code should be INVALID_FORMAT");
client.assert(response.body.message.includes("prometheus"), "Error message should mention supported formats");
});
%}
### Test without authentication (should fail)
GET {{triliumHost}}/etapi/metrics
> {%
client.test("Metrics endpoint requires authentication", function() {
client.assert(response.status === 401, "Response status should be 401");
});
%}

@ -159,6 +159,9 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
}
saveToRecentNotes(resolvedNotePath: string) {
if (options.is("databaseReadonly")) {
return;
}
setTimeout(async () => {
// we include the note in the recent list only if the user stayed on the note at least 5 seconds
if (resolvedNotePath && resolvedNotePath === this.notePath) {
@ -254,6 +257,10 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded">
return false;
}
if (options.is("databaseReadonly")) {
return true;
}
if (this.note.isLabelTruthy("readOnly")) {
return true;
}

@ -44,6 +44,9 @@ export default class TabManager extends Component {
if (!appContext.isMainWindow) {
return;
}
if (options.is("databaseReadonly")) {
return;
}
const openNoteContexts = this.noteContexts
.map((nc) => nc.getPojoState())

@ -4,6 +4,7 @@ import froca from "./froca.js";
import linkService from "./link.js";
import utils from "./utils.js";
import { t } from "./i18n.js";
import toast from "./toast.js";
let clipboardBranchIds: string[] = [];
let clipboardMode: string | null = null;
@ -108,6 +109,39 @@ function isClipboardEmpty() {
return clipboardBranchIds.length === 0;
}
export function copyText(text: string) {
if (!text) {
return;
}
let succeeded = false;
try {
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
succeeded = true;
} else {
// Fallback method: https://stackoverflow.com/a/72239825
const textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
succeeded = document.execCommand('copy');
document.body.removeChild(textArea);
}
} catch (e) {
console.warn(e);
succeeded = false;
}
if (succeeded) {
toast.showMessage(t("code_block.copy_success"));
} else {
toast.showError(t("code_block.copy_failed"));
}
}
export default {
pasteAfter,
pasteInto,

@ -9,7 +9,7 @@ import treeService from "./tree.js";
import FNote from "../entities/fnote.js";
import FAttachment from "../entities/fattachment.js";
import imageContextMenuService from "../menus/image_context_menu.js";
import { applySingleBlockSyntaxHighlight, applySyntaxHighlight } from "./syntax_highlight.js";
import { applySingleBlockSyntaxHighlight, formatCodeBlocks } from "./syntax_highlight.js";
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
import renderDoc from "./doc_renderer.js";
import { t } from "../services/i18n.js";
@ -106,7 +106,7 @@ async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HT
await linkService.loadReferenceLinkTitle($(el));
}
await applySyntaxHighlight($renderedContent);
await formatCodeBlocks($renderedContent);
} else if (note instanceof FNote) {
await renderChildrenList($renderedContent, note);
}

@ -1,6 +1,6 @@
import type FNote from "../entities/fnote.js";
import { getCurrentLanguage } from "./i18n.js";
import { applySyntaxHighlight } from "./syntax_highlight.js";
import { formatCodeBlocks } from "./syntax_highlight.js";
export default function renderDoc(note: FNote) {
return new Promise<JQuery<HTMLElement>>((resolve) => {
@ -41,7 +41,7 @@ function processContent(url: string, $content: JQuery<HTMLElement>) {
$img.attr("src", dir + "/" + $img.attr("src"));
});
applySyntaxHighlight($content);
formatCodeBlocks($content);
}
function getUrl(docNameValue: string, language: string) {

@ -1,21 +1,23 @@
import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes } from "@triliumnext/highlightjs";
import { ensureMimeTypes, highlight, highlightAuto, loadTheme, Themes, type AutoHighlightResult, type HighlightResult, type Theme } from "@triliumnext/highlightjs";
import mime_types from "./mime_types.js";
import options from "./options.js";
import { t } from "./i18n.js";
import { copyText } from "./clipboard.js";
let highlightingLoaded = false;
/**
* Identifies all the code blocks (as `pre code`) under the specified hierarchy and uses the highlight.js library to obtain the highlighted text which is then applied on to the code blocks.
* Additionally, adds a "Copy to clipboard" button.
*
* @param $container the container under which to look for code blocks and to apply syntax highlighting to them.
*/
export async function applySyntaxHighlight($container: JQuery<HTMLElement>) {
if (!isSyntaxHighlightEnabled()) {
return;
export async function formatCodeBlocks($container: JQuery<HTMLElement>) {
const syntaxHighlightingEnabled = isSyntaxHighlightEnabled();
if (syntaxHighlightingEnabled) {
await ensureMimeTypesForHighlighting();
}
await ensureMimeTypesForHighlighting();
const codeBlocks = $container.find("pre code");
for (const codeBlock of codeBlocks) {
const normalizedMimeType = extractLanguageFromClassList(codeBlock);
@ -23,10 +25,22 @@ export async function applySyntaxHighlight($container: JQuery<HTMLElement>) {
continue;
}
applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType);
applyCopyToClipboardButton($(codeBlock));
if (syntaxHighlightingEnabled) {
applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType);
}
}
}
export function applyCopyToClipboardButton($codeBlock: JQuery<HTMLElement>) {
const $copyButton = $("<button>")
.addClass("bx component icon-action tn-tool-button bx-copy copy-button")
.attr("title", t("code_block.copy_title"))
.on("click", () => copyText($codeBlock.text()));
$codeBlock.parent().append($copyButton);
}
/**
* Applies syntax highlight to the given code block (assumed to be <pre><code>), using highlight.js.
*/
@ -34,7 +48,7 @@ export async function applySingleBlockSyntaxHighlight($codeBlock: JQuery<HTMLEle
$codeBlock.parent().toggleClass("hljs");
const text = $codeBlock.text();
let highlightedText = null;
let highlightedText: HighlightResult | AutoHighlightResult | null = null;
if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) {
await ensureMimeTypesForHighlighting();
highlightedText = highlightAuto(text);
@ -66,7 +80,7 @@ export async function ensureMimeTypesForHighlighting() {
export function loadHighlightingTheme(themeName: string) {
const themePrefix = "default:";
let theme = null;
let theme: Theme | null = null;
if (themeName.includes(themePrefix)) {
theme = Themes[themeName.substring(themePrefix.length)];
}

@ -529,6 +529,31 @@ pre:not(.hljs) {
font-size: 100%;
}
pre {
--copy-button-margin-size: .35em;
position: relative;
}
pre > button.copy-button {
position: absolute;
top: var(--copy-button-margin-size);
right: var(--copy-button-margin-size);
}
:root pre:has(> button.copy-button) > code {
margin-right: calc(var(--icon-button-size) + (var(--copy-button-margin-size) * 2));
}
pre > button.copy-button:hover {
color: inherit !important;
opacity: 1;
}
pre > button.copy-button:active {
background-color: unset !important;
}
.pointer {
cursor: pointer;
}

@ -1830,7 +1830,10 @@
"word_wrapping": "Word wrapping",
"theme_none": "No syntax highlighting",
"theme_group_light": "Light themes",
"theme_group_dark": "Dark themes"
"theme_group_dark": "Dark themes",
"copy_success": "The code block was copied to clipboard.",
"copy_failed": "The code block could not be copied to the clipboard due to lack of permissions.",
"copy_title": "Copy to clipboard"
},
"classic_editor_toolbar": {
"title": "Formatting"

@ -93,16 +93,6 @@ declare global {
getSelectedExternalLink(): string | undefined;
setSelectedExternalLink(externalLink: string | null | undefined);
setNote(noteId: string);
markRegExp(regex: RegExp, opts: {
element: string;
className: string;
separateWordSearch: boolean;
caseSensitive: boolean;
done?: () => void;
});
unmark(opts?: {
done: () => void;
});
}
interface JQueryStatic {

@ -248,10 +248,10 @@ export default class FindWidget extends NoteContextAwareWidget {
case "code":
return this.codeHandler;
case "text":
return this.textHandler;
default:
const readOnly = await this.noteContext?.isReadOnly();
return readOnly ? this.htmlHandler : this.textHandler;
default:
console.warn("FindWidget: Unsupported note type for find widget", this.note?.type);
}
}

@ -1,6 +1,7 @@
// ck-find-result and ck-find-result_selected are the styles ck-editor
// uses for highlighting matches, use the same one on CodeMirror
// for consistency
import type Mark from "mark.js";
import utils from "../services/utils.js";
import type FindWidget from "./find.js";
import type { FindResult } from "./find.js";
@ -13,6 +14,7 @@ export default class FindInHtml {
private parent: FindWidget;
private currentIndex: number;
private $results: JQuery<HTMLElement> | null;
private mark?: Mark;
constructor(parent: FindWidget) {
this.parent = parent;
@ -21,21 +23,24 @@ export default class FindInHtml {
}
async performFind(searchTerm: string, matchCase: boolean, wholeWord: boolean) {
await import("mark.js");
const $content = await this.parent?.noteContext?.getContentElement();
if (!$content || !$content.length) {
return Promise.resolve({ totalFound: 0, currentFound: 0 });
}
if (!this.mark) {
this.mark = new (await import("mark.js")).default($content[0]);
}
const wholeWordChar = wholeWord ? "\\b" : "";
const regExp = new RegExp(wholeWordChar + utils.escapeRegExp(searchTerm) + wholeWordChar, matchCase ? "g" : "gi");
return new Promise<FindResult>((res) => {
$content?.unmark({
this.mark!.unmark({
done: () => {
$content.markRegExp(regExp, {
this.mark!.markRegExp(regExp, {
element: "span",
className: FIND_RESULT_CSS_CLASSNAME,
separateWordSearch: false,
caseSensitive: matchCase,
done: async () => {
this.$results = $content.find(`.${FIND_RESULT_CSS_CLASSNAME}`);
const scrollingContainer = $content[0].closest('.scrolling-container');
@ -73,10 +78,7 @@ export default class FindInHtml {
}
async findBoxClosed(totalFound: number, currentFound: number) {
const $content = await this.parent?.noteContext?.getContentElement();
if (typeof $content?.unmark === 'function') {
$content.unmark();
}
this.mark?.unmark();
}
async jumpTo() {

@ -6,6 +6,7 @@ import { t } from "../../services/i18n.js";
import LoadResults from "../../services/load_results.js";
import type { AttributeRow } from "../../services/load_results.js";
import FNote from "../../entities/fnote.js";
import options from "../../services/options.js";
export default class EditButton extends OnClickButtonWidget {
isEnabled(): boolean {
@ -27,6 +28,10 @@ export default class EditButton extends OnClickButtonWidget {
}
async refreshWithNote(note: FNote): Promise<void> {
if (options.is("databaseReadonly")) {
this.toggleInt(false);
return;
}
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
this.toggleInt(false);
} else {

@ -12,7 +12,7 @@ import { createChatSession, checkSessionExists, setupStreamingResponse, getDirec
import { extractInChatToolSteps } from "./message_processor.js";
import { validateEmbeddingProviders } from "./validation.js";
import type { MessageData, ToolExecutionStep, ChatData } from "./types.js";
import { applySyntaxHighlight } from "../../services/syntax_highlight.js";
import { formatCodeBlocks } from "../../services/syntax_highlight.js";
import "../../stylesheets/llm_chat.css";
@ -925,7 +925,7 @@ export default class LlmChatPanel extends BasicWidget {
// Apply syntax highlighting if this is the final update
if (isDone) {
applySyntaxHighlight($(assistantMessageEl as HTMLElement));
formatCodeBlocks($(assistantMessageEl as HTMLElement));
// Update message in the data model for storage
// Find the last assistant message to update, or add a new one if none exists

@ -2,7 +2,7 @@
* Utility functions for LLM Chat
*/
import { marked } from "marked";
import { applySyntaxHighlight } from "../../services/syntax_highlight.js";
import { formatCodeBlocks } from "../../services/syntax_highlight.js";
/**
* Format markdown content for display
@ -62,7 +62,7 @@ export function escapeHtml(text: string): string {
* Apply syntax highlighting to content
*/
export function applyHighlighting(element: HTMLElement): void {
applySyntaxHighlight($(element));
formatCodeBlocks($(element));
}
/**

@ -1172,16 +1172,19 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
let noneCollapsedYet = true;
this.tree.getRootNode().visit((node) => {
if (node.isExpanded() && !noteIdsToKeepExpanded.has(node.data.noteId)) {
node.setExpanded(false);
if (noneCollapsedYet) {
toastService.showMessage(t("note_tree.auto-collapsing-notes-after-inactivity"));
noneCollapsedYet = false;
if (!options.is("databaseReadonly")) {
// can't change expanded notes when database is readonly
this.tree.getRootNode().visit((node) => {
if (node.isExpanded() && !noteIdsToKeepExpanded.has(node.data.noteId)) {
node.setExpanded(false);
if (noneCollapsedYet) {
toastService.showMessage(t("note_tree.auto-collapsing-notes-after-inactivity"));
noneCollapsedYet = false;
}
}
}
}, false);
}, false);
}
this.filterHoistedBranch(true);
}, 600 * 1000);

@ -3,6 +3,7 @@ import utils from "../../services/utils.js";
import linkService from "../../services/link.js";
import server from "../../services/server.js";
import type FNote from "../../entities/fnote.js";
import options from "../../services/options.js";
import type { ExcalidrawElement, Theme } from "@excalidraw/excalidraw/element/types";
import type { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, LibraryItem, SceneData } from "@excalidraw/excalidraw/types";
import type { JSX } from "react";
@ -447,6 +448,9 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
}
onChangeHandler() {
if (options.is("databaseReadonly")) {
return;
}
// changeHandler is called upon any tiny change in excalidraw. button clicked, hover, etc.
// make sure only when a new element is added, we actually save something.
const isNewSceneVersion = this.isNewSceneVersion();
@ -540,7 +544,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
this.saveData();
},
onChange: () => this.onChangeHandler(),
viewModeEnabled: false,
viewModeEnabled: options.is("databaseReadonly"),
zenModeEnabled: false,
gridModeEnabled: false,
isCollaborating: false,
@ -567,6 +571,10 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
* info: sceneVersions are not incrementing. it seems to be a pseudo-random number
*/
isNewSceneVersion() {
if (options.is("databaseReadonly")) {
return false;
}
const sceneVersion = this.getSceneVersion();
return (

@ -1,17 +1,19 @@
import { ALLOWED_PROTOCOLS } from "../../../services/link.js";
import { MIME_TYPE_AUTO } from "@triliumnext/commons";
import 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";
import utils from "../../../services/utils.js";
import emojiDefinitionsUrl from "@triliumnext/ckeditor5/emoji_definitions/en.json?url";
import { copyText } from "../../../services/clipboard.js";
const TEXT_FORMATTING_GROUP = {
label: "Text formatting",
icon: "text"
};
export function buildConfig() {
export function buildConfig(): EditorConfig {
return {
image: {
styles: {
@ -111,6 +113,9 @@ export function buildConfig() {
defaultMimeType: MIME_TYPE_AUTO,
enabled: isSyntaxHighlightEnabled
},
clipboard: {
copy: copyText
},
// This value must be kept in sync with the language defined in webpack.config.js.
language: "en"
};

@ -1,5 +1,5 @@
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
import { applySyntaxHighlight } from "../../services/syntax_highlight.js";
import { formatCodeBlocks } from "../../services/syntax_highlight.js";
import type FNote from "../../entities/fnote.js";
import type { CommandListenerData, EventData } from "../../components/app_context.js";
import { getLocaleById } from "../../services/i18n.js";
@ -125,7 +125,7 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
}
await this.#applyInlineMermaid();
await applySyntaxHighlight(this.$content);
await formatCodeBlocks(this.$content);
}
async #applyInlineMermaid() {

@ -215,8 +215,6 @@ class ListOrGridView extends ViewMode {
const highlightedTokens = this.parentNote.highlightedTokens || [];
if (highlightedTokens.length > 0) {
await import("mark.js");
const regex = highlightedTokens.map((token) => utils.escapeRegExp(token)).join("|");
this.highlightRegex = new RegExp(regex, "gi");
@ -320,11 +318,10 @@ class ListOrGridView extends ViewMode {
$expander.on("click", () => this.toggleContent($card, note, !$card.hasClass("expanded")));
if (this.highlightRegex) {
$card.find(".note-book-title").markRegExp(this.highlightRegex, {
const Mark = new (await import("mark.js")).default($card.find(".note-book-title")[0]);
Mark.markRegExp(this.highlightRegex, {
element: "span",
className: "ck-find-result",
separateWordSearch: false,
caseSensitive: false
className: "ck-find-result"
});
}
@ -362,11 +359,10 @@ class ListOrGridView extends ViewMode {
});
if (this.highlightRegex) {
$renderedContent.markRegExp(this.highlightRegex, {
const Mark = new (await import("mark.js")).default($renderedContent[0]);
Mark.markRegExp(this.highlightRegex, {
element: "span",
className: "ck-find-result",
separateWordSearch: false,
caseSensitive: false
className: "ck-find-result"
});
}

@ -21,7 +21,7 @@ export default defineConfig(() => ({
plugins: [
viteStaticCopy({
targets: assets.map((asset) => ({
src: `src/${asset}/**/*`,
src: `src/${asset}/*`,
dest: asset
}))
}),

@ -68,8 +68,8 @@ module.exports = {
]
},
rebuildConfig: {
force: false,
onlyModules: []
force: true,
extraModules: [ "better-sqlite3" ]
},
makers: [
{

@ -66,6 +66,7 @@
],
"minify": true,
"thirdParty": true,
"declaration": false,
"esbuildOptions": {
"splitting": false,
"loader": {

@ -438,15 +438,15 @@
</li>
<li>TODO
<ul>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/work/Send%20invites%20for%20christmas%20par.html"
target="detail">Send invites for christmas party</a>
</li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/tesco/Buy%20milk.html"
target="detail">Buy milk</a>
</li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/mall/Buy%20some%20book%20for%20Bob.html"
target="detail">Buy some book for Bob</a>
</li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/work/Send%20invites%20for%20christmas%20par.html"
target="detail">Send invites for christmas party</a>
</li>
</ul>
</li>
<li>Implementation
@ -477,12 +477,12 @@
</li>
<li>shopping
<ul>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/mall/Buy%20some%20book%20for%20Bob.html"
target="detail">Buy some book for Bob</a>
</li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/tesco/Buy%20milk.html"
target="detail">Buy milk</a>
</li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/mall/Buy%20some%20book%20for%20Bob.html"
target="detail">Buy some book for Bob</a>
</li>
</ul>
</li>
<li>groceries

@ -18,28 +18,22 @@
height="150">
</figure>
<p><strong>Welcome to TriliumNext Notes!</strong>
</p>
<p>This is initial "demo" document provided by TriliumNext by default to
showcase some of its features and also give you some ideas how you might
structure your notes. You can play with it, modify note content and tree
structure as you wish.</p>
<p>If you need any help, visit TriliumNext website: <a href="https://github.com/TriliumNext">https://github.com/TriliumNext</a>
</p>
<h3>Cleanup</h3>
<p>Once you're finished with experimenting and want to cleanup these pages,
you can simply delete them all.</p>
<h3>Formatting</h3>
<p>TriliumNext supports classic formatting like <em>italic</em>, <strong>bold</strong>, <em><strong>bold and italic</strong></em>.
Of course you can add links like this one pointing to <a href="http://www.google.com">google.com</a>
</p>
<p>Lists</p>
<p><strong>Ordered:</strong>
</p>
<ol>
<li>First Item</li>
@ -54,7 +48,6 @@
</li>
</ol>
<p><strong>Unordered:</strong>
</p>
<ul>
<li>Item</li>

@ -14,22 +14,17 @@
<div class="ck-content">
<h2>Main characters</h2>
<p>… here put main characters …</p>
<p>&nbsp;</p>
<h2>Plot</h2>
<p>… describe main plot lines …</p>
<p>&nbsp;</p>
<h2>Tone</h2>
<p>&nbsp;</p>
<h2>Genre</h2>
<p>scifi / drama / romance</p>
<p>&nbsp;</p>
<h2>Similar books</h2>
<ul>
<li></li>
</ul>

@ -14,14 +14,11 @@
<div class="ck-content">
<p>Checkout Kindle daily deals: <a href="https://www.amazon.com/gp/feature.html?docId=1000677541">https://www.amazon.com/gp/feature.html?docId=1000677541</a>
</p>
<ul>
<li>Cixin Liu - <a href="https://www.amazon.com/Dark-Forest-Remembrance-Earths-Past/dp/0765386690/ref=pd_bxgy_14_img_2?_encoding=UTF8&amp;pd_rd_i=0765386690&amp;pd_rd_r=AB0J179TM9NTEAMHE240&amp;pd_rd_w=FAhxX&amp;pd_rd_wg=pLGK7&amp;psc=1&amp;refRID=AB0J179TM9NTEAMHE240">The Dark Forest</a>
</li>
<li>Ann Leckie - <a href="https://www.amazon.com/Ancillary-Sword-Imperial-Radch-Leckie/dp/0316246654/ref=pd_sim_14_1?_encoding=UTF8&amp;pd_rd_i=0316246654&amp;pd_rd_r=D7KDTGZFP7YM1YSYVY4G&amp;pd_rd_w=jkn28&amp;pd_rd_wg=JVhtw&amp;psc=1&amp;refRID=D7KDTGZFP7YM1YSYVY4G">Ancillary Sword</a>
</li>
</ul>
</div>

@ -18,25 +18,21 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">buy milk&nbsp;&nbsp;</span>
</label>
</li>
<li>
<label class="todo-list__label">
<input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">do the laundry&nbsp;&nbsp;</span>
</label>
</li>
<li>
<label class="todo-list__label">
<input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">watch TV&nbsp;&nbsp;</span>
</label>
</li>
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">eat ice cream&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -20,9 +20,10 @@
is first created it will try to automatically determine the programming
language, should that fail it is possible to manually adjust it. The color
scheme for the syntax highlighting is adjustable in settings.&nbsp;</p><pre><code class="language-application-javascript-env-frontend">function helloWorld() {
alert("Hello world");
}</code></pre>
}</code></pre>
<p>For larger pieces of code it is better to use a code note, which uses
a fully-fledged code editor (CodeMirror). For an example of a code note,
see&nbsp;<a class="reference-link" href="../Scripting%20examples/Custom%20request%20handler.js">Custom request handler</a>.</p>

@ -15,9 +15,7 @@
<div class="ck-content">
<p><span class="math-tex">\(% \f is defined as #1f(#2) using the macro \f\relax{x} = \int_{-\infty}^\infty &nbsp; &nbsp; \f\hat\xi\,e^{2 \pi i \xi x} &nbsp; &nbsp; \,d\xi\)</span>Some
math examples:</p><span class="math-tex">\[\displaystyle \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }\]</span>
<p>Another:</p><span class="math-tex">\[\displaystyle \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)\]</span>
<p>Inline math is also possible:&nbsp;<span class="math-tex">\(c^2 = a^2 + b^2\)</span>&nbsp;</p>
<p>&nbsp;</p>
</div>

@ -18,7 +18,7 @@
href="https://en.wikipedia.org/wiki/Short_story">short story</a>by American writer <a href="https://en.wikipedia.org/wiki/Isaac_Asimov">Isaac Asimov</a>.
It first appeared in the November 1956 issue of <a href="https://en.wikipedia.org/wiki/Science_Fiction_Quarterly"><em>Science Fiction Quarterly</em></a>.</p>
<section
class="include-note" data-note-id="ZWCYra81yOFO" data-box-size="medium">&nbsp;</section>
class="include-note" data-note-id="VsFbpoySMCE3" data-box-size="medium">&nbsp;</section>
<p>This page demonstrates two things:</p>
<ul>
<li>possibility to <a href="#root/_hidden/_help/_help_KSZ04uQ2D1St/_help_iPIMuisry3hd/_help_nBAXQFj20hS1">include one note into another</a>

@ -14,7 +14,6 @@
<div class="ck-content">
<p>You can read some explanation on how this journal works here: <a href="https://github.com/zadam/trilium/wiki/Day-notes">https://github.com/zadam/trilium/wiki/Day-notes</a>
</p>
</div>
</div>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -17,7 +17,6 @@
<li>XBox</li>
<li>Candles</li>
<li><a href="https://www.amazon.ca/Anker-SoundCore-Portable-Bluetooth-Resistance/dp/B01MTB55WH?pd_rd_wg=honW8&amp;pd_rd_r=c9bb7c0f-0051-4da7-991f-4ca711a1b3e3&amp;pd_rd_w=ciUpR&amp;ref_=pd_gw_simh&amp;pf_rd_r=K10XKX0NGPDNTYYP4BS4&amp;pf_rd_p=5f1b460b-78c1-580e-929e-2878fe4859e8">Portable speakers</a>
</li>
<li>...?</li>
</ul>

@ -14,10 +14,8 @@
<div class="ck-content">
<p>Wiki: <a href="https://en.wikipedia.org/wiki/Trusted_timestamping">https://en.wikipedia.org/wiki/Trusted_timestamping</a>
</p>
<p>Bozho: <a href="https://techblog.bozho.net/using-trusted-timestamping-java/">https://techblog.bozho.net/using-trusted-timestamping-java/</a>
</p>
<p><strong>Trusted timestamping</strong> is the process of <a href="https://en.wikipedia.org/wiki/Computer_security">securely</a> keeping
track of the creation and modification time of a document. Security here

@ -16,7 +16,6 @@
<p>Miscellaneous notes done on monday ...</p>
<p>&nbsp;</p>
<p>Interesting video: <a href="https://www.youtube.com/watch?v=_eSAF_qT_FY&amp;feature=youtu.be">https://www.youtube.com/watch?v=_eSAF_qT_FY&amp;feature=youtu.be</a>
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
width="209" height="300">
</figure>
<p>Maybe CodeNames? <a href="https://boardgamegeek.com/boardgame/178900/codenames">https://boardgamegeek.com/boardgame/178900/codenames</a>
</p>
</div>
</div>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

@ -24,17 +24,14 @@
<span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1"
data-footnote-id="6qz4pm021mi" role="doc-noteref" id="fnref6qz4pm021mi"><sup><a href="#fn6qz4pm021mi">[1]</a></sup>
</span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="6qz4pm021mi" role="doc-endnote" id="fn6qz4pm021mi"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="6qz4pm021mi"><sup><strong><a href="#fnref6qz4pm021mi">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

@ -26,16 +26,13 @@
been brought to its knees.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="o6g991vkrwj" role="doc-noteref"
id="fnrefo6g991vkrwj"><sup><a href="#fno6g991vkrwj">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="o6g991vkrwj" role="doc-endnote" id="fno6g991vkrwj"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="o6g991vkrwj"><sup><strong><a href="#fnrefo6g991vkrwj">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

@ -22,16 +22,13 @@
around 1450 in polished drystone walls.<span class="footnote-reference"
data-footnote-reference="" data-footnote-index="1" data-footnote-id="4prjheuho88"
role="doc-noteref" id="fnref4prjheuho88"><sup><a href="#fn4prjheuho88">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="4prjheuho88" role="doc-endnote" id="fn4prjheuho88"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="4prjheuho88"><sup><strong><a href="#fnref4prjheuho88">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

@ -23,16 +23,13 @@
by earthquakes.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="ej5sd0bakne" role="doc-noteref"
id="fnrefej5sd0bakne"><sup><a href="#fnej5sd0bakne">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="ej5sd0bakne" role="doc-endnote" id="fnej5sd0bakne"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="ej5sd0bakne"><sup><strong><a href="#fnrefej5sd0bakne">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

@ -26,17 +26,14 @@
<span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1"
data-footnote-id="4kitkusvyi3" role="doc-noteref" id="fnref4kitkusvyi3"><sup><a href="#fn4kitkusvyi3">[1]</a></sup>
</span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="4kitkusvyi3" role="doc-endnote" id="fn4kitkusvyi3"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="4kitkusvyi3"><sup><strong><a href="#fnref4kitkusvyi3">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

@ -23,17 +23,14 @@
<span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1"
data-footnote-id="o0o2das7ljm" role="doc-noteref" id="fnrefo0o2das7ljm"><sup><a href="#fno0o2das7ljm">[1]</a></sup>
</span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="o0o2das7ljm" role="doc-endnote" id="fno0o2das7ljm"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="o0o2das7ljm"><sup><strong><a href="#fnrefo0o2das7ljm">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

@ -23,16 +23,13 @@
the complex.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="zzzjn52iwk" role="doc-noteref"
id="fnrefzzzjn52iwk"><sup><a href="#fnzzzjn52iwk">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="zzzjn52iwk" role="doc-endnote" id="fnzzzjn52iwk"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="zzzjn52iwk"><sup><strong><a href="#fnrefzzzjn52iwk">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

Some files were not shown because too many files have changed in this diff Show More