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

This commit is contained in:
Elian Doran 2025-05-27 19:34:49 +03:00 committed by GitHub
commit a1dda3c01a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
237 changed files with 2645 additions and 2302 deletions

View File

@ -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. 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. 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.

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

View File

@ -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");
});
%}

View File

@ -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");
});
%}

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import froca from "./froca.js";
import linkService from "./link.js"; import linkService from "./link.js";
import utils from "./utils.js"; import utils from "./utils.js";
import { t } from "./i18n.js"; import { t } from "./i18n.js";
import toast from "./toast.js";
let clipboardBranchIds: string[] = []; let clipboardBranchIds: string[] = [];
let clipboardMode: string | null = null; let clipboardMode: string | null = null;
@ -108,6 +109,39 @@ function isClipboardEmpty() {
return clipboardBranchIds.length === 0; 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 { export default {
pasteAfter, pasteAfter,
pasteInto, pasteInto,

View File

@ -9,7 +9,7 @@ import treeService from "./tree.js";
import FNote from "../entities/fnote.js"; import FNote from "../entities/fnote.js";
import FAttachment from "../entities/fattachment.js"; import FAttachment from "../entities/fattachment.js";
import imageContextMenuService from "../menus/image_context_menu.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 { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
import renderDoc from "./doc_renderer.js"; import renderDoc from "./doc_renderer.js";
import { t } from "../services/i18n.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 linkService.loadReferenceLinkTitle($(el));
} }
await applySyntaxHighlight($renderedContent); await formatCodeBlocks($renderedContent);
} else if (note instanceof FNote) { } else if (note instanceof FNote) {
await renderChildrenList($renderedContent, note); await renderChildrenList($renderedContent, note);
} }

View File

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

View File

@ -1,20 +1,22 @@
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 mime_types from "./mime_types.js";
import options from "./options.js"; import options from "./options.js";
import { t } from "./i18n.js";
import { copyText } from "./clipboard.js";
let highlightingLoaded = false; 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. * 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. * @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>) { export async function formatCodeBlocks($container: JQuery<HTMLElement>) {
if (!isSyntaxHighlightEnabled()) { const syntaxHighlightingEnabled = isSyntaxHighlightEnabled();
return; if (syntaxHighlightingEnabled) {
}
await ensureMimeTypesForHighlighting(); await ensureMimeTypesForHighlighting();
}
const codeBlocks = $container.find("pre code"); const codeBlocks = $container.find("pre code");
for (const codeBlock of codeBlocks) { for (const codeBlock of codeBlocks) {
@ -23,9 +25,21 @@ export async function applySyntaxHighlight($container: JQuery<HTMLElement>) {
continue; continue;
} }
applyCopyToClipboardButton($(codeBlock));
if (syntaxHighlightingEnabled) {
applySingleBlockSyntaxHighlight($(codeBlock), normalizedMimeType); 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. * 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"); $codeBlock.parent().toggleClass("hljs");
const text = $codeBlock.text(); const text = $codeBlock.text();
let highlightedText = null; let highlightedText: HighlightResult | AutoHighlightResult | null = null;
if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) { if (normalizedMimeType === mime_types.MIME_TYPE_AUTO) {
await ensureMimeTypesForHighlighting(); await ensureMimeTypesForHighlighting();
highlightedText = highlightAuto(text); highlightedText = highlightAuto(text);
@ -66,7 +80,7 @@ export async function ensureMimeTypesForHighlighting() {
export function loadHighlightingTheme(themeName: string) { export function loadHighlightingTheme(themeName: string) {
const themePrefix = "default:"; const themePrefix = "default:";
let theme = null; let theme: Theme | null = null;
if (themeName.includes(themePrefix)) { if (themeName.includes(themePrefix)) {
theme = Themes[themeName.substring(themePrefix.length)]; theme = Themes[themeName.substring(themePrefix.length)];
} }

View File

@ -529,6 +529,31 @@ pre:not(.hljs) {
font-size: 100%; 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 { .pointer {
cursor: pointer; cursor: pointer;
} }

View File

@ -1830,7 +1830,10 @@
"word_wrapping": "Word wrapping", "word_wrapping": "Word wrapping",
"theme_none": "No syntax highlighting", "theme_none": "No syntax highlighting",
"theme_group_light": "Light themes", "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": { "classic_editor_toolbar": {
"title": "Formatting" "title": "Formatting"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1172,6 +1172,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
let noneCollapsedYet = true; let noneCollapsedYet = true;
if (!options.is("databaseReadonly")) {
// can't change expanded notes when database is readonly
this.tree.getRootNode().visit((node) => { this.tree.getRootNode().visit((node) => {
if (node.isExpanded() && !noteIdsToKeepExpanded.has(node.data.noteId)) { if (node.isExpanded() && !noteIdsToKeepExpanded.has(node.data.noteId)) {
node.setExpanded(false); node.setExpanded(false);
@ -1182,6 +1184,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
} }
} }
}, false); }, false);
}
this.filterHoistedBranch(true); this.filterHoistedBranch(true);
}, 600 * 1000); }, 600 * 1000);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -438,15 +438,15 @@
</li> </li>
<li>TODO <li>TODO
<ul> <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" <li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/tesco/Buy%20milk.html"
target="detail">Buy milk</a> target="detail">Buy milk</a>
</li> </li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/mall/Buy%20some%20book%20for%20Bob.html" <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> target="detail">Buy some book for Bob</a>
</li> </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> </ul>
</li> </li>
<li>Implementation <li>Implementation
@ -477,12 +477,12 @@
</li> </li>
<li>shopping <li>shopping
<ul> <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" <li><a href="root/Trilium%20Demo/Scripting%20examples/Task%20manager/Locations/tesco/Buy%20milk.html"
target="detail">Buy milk</a> target="detail">Buy milk</a>
</li> </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> </ul>
</li> </li>
<li>groceries <li>groceries

View File

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

View File

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

View File

@ -14,14 +14,11 @@
<div class="ck-content"> <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>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> </p>
<ul> <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>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>
<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>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> </li>
</ul> </ul>
</div> </div>

View File

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

View File

@ -20,9 +20,10 @@
is first created it will try to automatically determine the programming is first created it will try to automatically determine the programming
language, should that fail it is possible to manually adjust it. The color 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() { 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>
alert("Hello world");
}</code></pre>
<p>For larger pieces of code it is better to use a code note, which uses <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, 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> see&nbsp;<a class="reference-link" href="../Scripting%20examples/Custom%20request%20handler.js">Custom request handler</a>.</p>

View File

@ -15,9 +15,7 @@
<div class="ck-content"> <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 <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> 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>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>Inline math is also possible:&nbsp;<span class="math-tex">\(c^2 = a^2 + b^2\)</span>&nbsp;</p>
<p>&nbsp;</p> <p>&nbsp;</p>
</div> </div>

View File

@ -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>. 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> 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 <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> <p>This page demonstrates two things:</p>
<ul> <ul>
<li>possibility to <a href="#root/_hidden/_help/_help_KSZ04uQ2D1St/_help_iPIMuisry3hd/_help_nBAXQFj20hS1">include one note into another</a> <li>possibility to <a href="#root/_hidden/_help/_help_KSZ04uQ2D1St/_help_iPIMuisry3hd/_help_nBAXQFj20hS1">include one note into another</a>

View File

@ -14,7 +14,6 @@
<div class="ck-content"> <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>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> </p>
</div> </div>
</div> </div>

View File

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

View File

@ -17,7 +17,6 @@
<li>XBox</li> <li>XBox</li>
<li>Candles</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><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>...?</li> <li>...?</li>
</ul> </ul>

View File

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

View File

@ -16,7 +16,6 @@
<p>Miscellaneous notes done on monday ...</p> <p>Miscellaneous notes done on monday ...</p>
<p>&nbsp;</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>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>
<p>&nbsp;</p> <p>&nbsp;</p>
<p>&nbsp;</p> <p>&nbsp;</p>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,17 +24,14 @@
<span <span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1" 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> data-footnote-id="6qz4pm021mi" role="doc-noteref" id="fnref6qz4pm021mi"><sup><a href="#fn6qz4pm021mi">[1]</a></sup>
</span> </span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

View File

@ -26,16 +26,13 @@
been brought to its knees.<span class="footnote-reference" data-footnote-reference="" been brought to its knees.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="o6g991vkrwj" role="doc-noteref" data-footnote-index="1" data-footnote-id="o6g991vkrwj" role="doc-noteref"
id="fnrefo6g991vkrwj"><sup><a href="#fno6g991vkrwj">[1]</a></sup></span> id="fnrefo6g991vkrwj"><sup><a href="#fno6g991vkrwj">[1]</a></sup></span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

View File

@ -22,16 +22,13 @@
around 1450 in polished drystone walls.<span class="footnote-reference" around 1450 in polished drystone walls.<span class="footnote-reference"
data-footnote-reference="" data-footnote-index="1" data-footnote-id="4prjheuho88" data-footnote-reference="" data-footnote-index="1" data-footnote-id="4prjheuho88"
role="doc-noteref" id="fnref4prjheuho88"><sup><a href="#fn4prjheuho88">[1]</a></sup></span> role="doc-noteref" id="fnref4prjheuho88"><sup><a href="#fn4prjheuho88">[1]</a></sup></span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

View File

@ -23,16 +23,13 @@
by earthquakes.<span class="footnote-reference" data-footnote-reference="" by earthquakes.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="ej5sd0bakne" role="doc-noteref" data-footnote-index="1" data-footnote-id="ej5sd0bakne" role="doc-noteref"
id="fnrefej5sd0bakne"><sup><a href="#fnej5sd0bakne">[1]</a></sup></span> id="fnrefej5sd0bakne"><sup><a href="#fnej5sd0bakne">[1]</a></sup></span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

View File

@ -26,17 +26,14 @@
<span <span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1" 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> data-footnote-id="4kitkusvyi3" role="doc-noteref" id="fnref4kitkusvyi3"><sup><a href="#fn4kitkusvyi3">[1]</a></sup>
</span> </span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

View File

@ -23,17 +23,14 @@
<span <span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1" 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> data-footnote-id="o0o2das7ljm" role="doc-noteref" id="fnrefo0o2das7ljm"><sup><a href="#fno0o2das7ljm">[1]</a></sup>
</span> </span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

View File

@ -23,16 +23,13 @@
the complex.<span class="footnote-reference" data-footnote-reference="" the complex.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="zzzjn52iwk" role="doc-noteref" data-footnote-index="1" data-footnote-id="zzzjn52iwk" role="doc-noteref"
id="fnrefzzzjn52iwk"><sup><a href="#fnzzzjn52iwk">[1]</a></sup></span> id="fnrefzzzjn52iwk"><sup><a href="#fnzzzjn52iwk">[1]</a></sup></span>
</p> </p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes"> <ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1" <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> 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 <div
class="footnote-content" data-footnote-content=""> 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><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> </p>
</div> </div>
</li> </li>

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