From 1acbf5ba45d1b1eef8a86f42a0503e597e33437e Mon Sep 17 00:00:00 2001
From: Elian Doran The resulting collection will contain all the children of the collection,
while maintaining the hierarchy.
#printLandscape.#printPageSize attribute,
with one of the following values: A0,
For example, to change the font of the document from the one defined by - the theme or the user to a serif one:
body {
- --main-font-family: serif !important;
- --detail-font-family: var(--main-font-family) !important;
+ the theme or the user to a serif one::root {
+ --print-font-family: serif;
+ --print-font-size: 11pt;
}
To remark:
- Multiple CSS notes can be add by using multiple
~printCss relations.
- - If the note pointing to the
printCss doesn't
+ - If the note pointing to the
printCss doesn't
have the right note type or mime type, it will be ignored.
- - If migrating from a previous version where Custom app-wide CSS, there's no need for
-
@media print { since the style-sheet is used only for printing.
+ - If migrating from a previous version where Custom app-wide CSS, there's no need for
+
@media print { since the style-sheet is used only for printing.
Under the hood
Both printing and exporting as PDF use the same mechanism: a note is rendered
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html
index 0f3b39ee11..605062dcaa 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note types with split view.html
@@ -1,8 +1,8 @@
-
Split view is a feature of Mermaid Diagrams and
+
Split view is a feature of Mermaid Diagrams and
Markdown notes which displays both the source code on one side
+ class="reference-link" href="#root/_help_6RM1Q7ppFVoj">Markdown notes which displays both the source code on one side
and the preview of the content on the other.
-Mermaid Diagrams also
+
Mermaid Diagrams also
allow changing between a horizontal or a vertical split, to accommodate
for the various sizes of diagrams.
Display modes and interaction
@@ -20,12 +20,12 @@
Preview which displays only the rendering of the diagram or text
in full screen, especially useful for read-only notes.
-These buttons can be found near the Note buttons section
- on the New Layout,
- or in the Floating buttons on
+
These buttons can be found near the Note buttons section
+ on the New Layout,
+ or in the Floating buttons on
the old layout.
The display node is stored at note level.
Relation to read-only notes
-If a note is marked as read-only,
- the source view will not be editable. While in preview mode, marking a
- note as read-only has no effect since the preview itself is not editable.
\ No newline at end of file
+If a note is marked as read-only, the
+ source view will not be editable. While in preview mode, marking a note
+ as read-only has no effect since the preview itself is not editable.
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html
index fbd042717a..ecd52a4302 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Data directory.html
@@ -70,6 +70,21 @@
this:TRILIUM_DATA_DIR=/home/myuser/data/my-trilium-data trilium
You can then save the above command as a shell script on your path for
convenience.
+Electron user data directory (desktop only)
+When running the desktop application, Electron stores internal data (caches,
+ spell-check dictionaries, session storage, etc.) separately from the Trilium
+ data directory. By default this goes to the system's application data folder
+ (e.g. %APPDATA% on Windows), which may be
+ undesirable in corporate environments with roaming profiles or when running
+ in portable mode.
+When TRILIUM_DATA_DIR is set (e.g. via the
+ trilium-portablescript), the Electron user data is automatically
+ redirected into ${TRILIUM_DATA_DIR}/electron-user-data/,
+ so no files are written to the system's roaming profile.
+If you need to store the Electron data in a different location than the
+ Trilium data directory, you can set the TRILIUM_ELECTRON_DATA_DIR environment
+ variable to an explicit path.
Fine-grained directory/path location
Apart from the data directory, some of the subdirectories of it can be
moved elsewhere by changing an environment variable:
@@ -129,5 +144,13 @@
Path to Configuration (config.ini or environment variables) file.
+
+ TRILIUM_ELECTRON_DATA_DIR
+
+ ${TRILIUM_DATA_DIR}/electron-user-data (portable)
+ or system appData (default)
+ Directory where Electron stores internal data such as caches and spell-check
+ dictionaries (desktop only).
+
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html
index 40d7ddd89c..e1ea2fc9b7 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Installation & Setup/Desktop Installation.html
@@ -23,7 +23,9 @@
trilium-portable: Launches Trilium in
portable mode, where the data directory is
created within the application's directory, making it easy to move the
- entire setup.
+ entire setup. Electron's internal data (caches, dictionaries, etc.) is
+ also stored within the data directory, so no files are written to the system's
+ roaming profile.
trilium-safe-mode: Boots Trilium in "safe
mode," disabling any startup scripts that might cause the application to
crash.
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html
index 2607200081..d468c589c9 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types.html
@@ -9,8 +9,7 @@
note where to place the new one and select:
- Insert note after, to put the new note underneath the one selected.
- - Insert child note, to insert the note as a child of the selected
+
- Insert child note, to insert the note as a child of the selected
note.
@@ -21,8 +20,7 @@
When adding a link in a Text note, type the desired title of
the new note and press Enter. Afterwards the type of the note will be asked.
- Similarly, when creating a new tab, type the desired title and press Enter.
+ Similarly, when creating a new tab, type the desired title and press Enter.
Changing the type of a note
It is possible to change the type of a note after it has been created
@@ -32,96 +30,94 @@
edit the source of a note.
Supported note types
The following note types are supported by Trilium:
-
-
-
-
- Note Type
- Description
-
-
-
-
- Text
-
- The default note type, which allows for rich text formatting, images,
- admonitions and right-to-left support.
-
-
- Code
-
- Uses a mono-space font and can be used to store larger chunks of code
- or plain text than a text note, and has better syntax highlighting.
-
-
- Saved Search
-
- Stores the information about a search (the search text, criteria, etc.)
- for later use. Can be used for quick filtering of a large amount of notes,
- for example. The search can easily be triggered.
-
-
- Relation Map
-
- Allows easy creation of notes and relations between them. Can be used
- for mainly relational data such as a family tree.
-
-
- Note Map
-
- Displays the relationships between the notes, whether via relations or
- their hierarchical structure.
-
-
- Render Note
-
- Used in Scripting,
- it displays the HTML content of another note. This allows displaying any
- kind of content, provided there is a script behind it to generate it.
-
-
- Collections
-
- Displays the children of the note either as a grid, a list, or for a more
- specialized case: a calendar.
-
-
Generally useful for easy reading of short notes.
-
-
- Mermaid Diagrams
-
- Displays diagrams such as bar charts, flow charts, state diagrams, etc.
- Requires a bit of technical knowledge since the diagrams are written in
- a specialized format.
-
-
- Canvas
-
- Allows easy drawing of sketches, diagrams, handwritten content. Uses the
- same technology behind excalidraw.com.
-
-
- Web View
-
- Displays the content of an external web page, similar to a browser.
-
-
- Mind Map
-
- Easy for brainstorming ideas, by placing them in a hierarchical layout.
-
-
- Geo Map
-
- Displays the children of the note as a geographical map, one use-case
- would be to plan vacations. It even has basic support for tracks. Notes
- can also be created from it.
-
-
- File
-
- Represents an uploaded file such as PDFs, images, video or audio files.
-
-
-
-
\ No newline at end of file
+
+
+
+ Note Type
+ Description
+
+
+
+
+ Text
+
+ The default note type, which allows for rich text formatting, images,
+ admonitions and right-to-left support.
+
+
+ Code
+
+ Uses a mono-space font and can be used to store larger chunks of code
+ or plain text than a text note, and has better syntax highlighting.
+
+
+ Saved Search
+
+ Stores the information about a search (the search text, criteria, etc.)
+ for later use. Can be used for quick filtering of a large amount of notes,
+ for example. The search can easily be triggered.
+
+
+ Relation Map
+
+ Allows easy creation of notes and relations between them. Can be used
+ for mainly relational data such as a family tree.
+
+
+ Note Map
+
+ Displays the relationships between the notes, whether via relations or
+ their hierarchical structure.
+
+
+ Render Note
+
+ Used in Scripting,
+ it displays the HTML content of another note. This allows displaying any
+ kind of content, provided there is a script behind it to generate it.
+
+
+ Collections
+
+ Displays the children of the note either as a grid, a list, or for a more
+ specialized case: a calendar.
+
+
Generally useful for easy reading of short notes.
+
+
+ Mermaid Diagrams
+
+ Displays diagrams such as bar charts, flow charts, state diagrams, etc.
+ Requires a bit of technical knowledge since the diagrams are written in
+ a specialized format.
+
+
+ Canvas
+
+ Allows easy drawing of sketches, diagrams, handwritten content. Uses the
+ same technology behind excalidraw.com.
+
+
+ Web View
+
+ Displays the content of an external web page, similar to a browser.
+
+
+ Mind Map
+
+ Easy for brainstorming ideas, by placing them in a hierarchical layout.
+
+
+ Geo Map
+
+ Displays the children of the note as a geographical map, one use-case
+ would be to plan vacations. It even has basic support for tracks. Notes
+ can also be created from it.
+
+
+ File
+
+ Represents an uploaded file such as PDFs, images, video or audio files.
+
+
+
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html
index 152140d5ed..f64ba7eade 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/File.html
@@ -5,8 +5,7 @@
create a File note type directly:
- Drag a file into the Note Tree.
- - Right click a note and select Import into note and point it to
+
- Right click a note and select Import into note and point it to
one of the supported files.
Supported file types
@@ -83,30 +82,28 @@
href="#root/_help_BlN9DFI679QC">Ribbon.
- Download, which will download the file for local use.
- - Open, will will open the file with the system-default application.
- - Upload new revision to replace the file with a new one.
+ - Open, will will open the file with the system-default application.
+ - Upload new revision to replace the file with a new one.
-
- It is not possible to change the note type of a File note.
- Convert into an attachment from the note menu.
+
+ It is not possible to change the note type of a File note.
+ Convert into an attachment from the note menu.
Relation with other notes
-
Files are also displayed in the Note List based
on their type:
-
-
-
+
+
+ -
+
Non-image files can be embedded into text notes as read-only widgets via
+ the Include Note functionality.
+
+ -
+
Image files can be embedded into text notes like normal images via
+ Image references.
- - Non-image files can be embedded into text notes as read-only widgets via
- the Include Note functionality.
- - Image files can be embedded into text notes like normal images via
- Image references.
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html
index 74cf113776..2d3d41e7f9 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Markdown.html
@@ -1,13 +1,12 @@
-Trilium has always supported Markdown through its import feature,
+
Trilium has always supported Markdown through its import feature,
however the file was either transformed to a Text note
- (converted to Trilium's internal HTML format) or saved as a Code note
+ href="#root/_help_iPIMuisry3hd">Text note (converted to Trilium's internal
+ HTML format) or saved as a Code note
with only syntax highlight.
This note type is a split view, meaning that both the source code and
a preview of the document are displayed side-by-side. See Note types with split view for
- more information.
+ href="#root/_help_SL5f1Auq7sVN">Note types with split view for more
+ information.
Rationale
The goal of this note type is to fill a gap: rendering Markdown but not
altering its structure or its whitespace which would inevitably change
@@ -33,65 +32,53 @@
The following features are supported by Trilium's Markdown format and
will show up in the preview pane:
- - All standard and GitHub-flavored syntax (basic formatting, tables, blockquotes)
- - Code blocks with syntax highlight (e.g.
```js)
- and automatic syntax highlight
- - Block quotes & admonitions
-
- - Math Equations
-
- - Mermaid Diagrams using
-
```mermaid
-
- -
-
Include Note (no
- builtin Markdown syntax, but HTML syntax works just fine):
<section class="include-note" data-note-id="vJDjQm0VK8Na" data-box-size="expandable">
-
+ -
+
All standard and GitHub-flavored syntax (basic formatting, tables, blockquotes)
+
+ -
+
Code blocks with syntax highlight (e.g. <!--CODE_BLOCK_1776493385878_0-->mermaid
+
+
+ -
+
Include Note (no
+ builtin Markdown syntax, but HTML syntax works just fine):
<section class="include-note" data-note-id="vJDjQm0VK8Na" data-box-size="expandable">
+
</section>n
-
- -
-
Internal (reference) links via
- its HTML syntax, or through a Wikilinks-like format (only
- Note ID):
[[Hg8TS5ZOxti6]]
-
+
+ -
+
Internal (reference) links via
+ its HTML syntax, or through a Wikilinks-like format (only
+ Note ID):
[[Hg8TS5ZOxti6]]
+
Creating Markdown notes
There are two ways to create a Markdown note:
- - Create a new note (e.g. in the Note Tree)
+
- Create a new note (e.g. in the Note Tree)
and select the type Markdown, just like all the other note types.
- - Create a note of type Code and
- select as the language either Markdown or GitHub-Flavored Markdown.
+
- Create a note of type Code and
+ select as the language either Markdown or GitHub-Flavored Markdown.
This maintains compatibility with your existing notes prior to the introduction
of this feature.
Import/export
-
By default, when importing a single Markdown file it automatically gets
- converted to a Text note.
+ converted to a Text note.
To avoid that and have it imported as a Markdown note instead:
- -
-
Right click the Note Tree and
- select Import into note.
-
- -
-
Select the file normally.
-
- -
-
Uncheck Import HTML, Markdown and TXT as text notes if it's unclear from the metadata.
-
+ - Right click the Note Tree and
+ select Import into note.
+ - Select the file normally.
+ - Uncheck Import HTML, Markdown and TXT as text notes if it's unclear from the metadata.
-
@@ -105,9 +92,8 @@
Conversion between text notes and Markdown notes
Currently there is no built-in functionality to convert a Text note
- into a Markdown note or vice-versa. We do have plans to address this in
- the future.
+ href="#root/_help_iPIMuisry3hd">Text note into a Markdown note or vice-versa.
+ We do have plans to address this in the future.
This can be achieved manually, for a single note:
- Export the file as Markdown, with single format.
@@ -135,6 +121,6 @@
This feature of synchronizing the scroll is based on blocks but it's provided
on a best-effort basis since our underlying Markdown library doesn't support
this feature natively, so we had to implement our own algorithm. Feel free
- to report issues,
- but always provide a sample Markdown file to be able to reproduce it.
+ to report issues, but always provide a
+ sample Markdown file to be able to reproduce it.
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html
index d6ff44cd93..a17a7ea0a8 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Mermaid Diagrams.html
@@ -12,8 +12,8 @@
the diagram.
This note type is a split view, meaning that both the source code and
a preview of the document are displayed side-by-side. See Note types with split view for
- more information.
+ href="#root/_help_SL5f1Auq7sVN">Note types with split view for more
+ information.
Sample diagrams
Starting with v0.103.0, Mermaid diagrams no longer start with a sample
flowchart, but instead a pane at the bottom will show all the supported
@@ -52,34 +52,30 @@
- The preview can be moved around by holding the left mouse button and dragging.
- - Zooming can also be done by using the scroll wheel.
- - The zoom and position on the preview will remain fixed as the diagram
- changes, to be able to work more easily with large diagrams.
-
+ - Zooming can also be done by using the scroll wheel.
+ - The zoom and position on the preview will remain fixed as the diagram
+ changes, to be able to work more easily with large diagrams.
+
- The size of the source/preview panes can be adjusted by hovering over
the border between them and dragging it with the mouse.
- In the Floating buttons area:
- The source/preview can be laid out left-right or bottom-top via the Move editing pane to the left / bottom option.
- - Press Lock editing to automatically mark the note as read-only.
+
- Press Lock editing to automatically mark the note as read-only.
In this mode, the code pane is hidden and the diagram is displayed full-size.
Similarly, press Unlock editing to mark a read-only note as editable.
- - Press the Copy image reference to the clipboard to be able to insert
- the image representation of the diagram into a text note. See Image references for more information.
- - Press the Export diagram as SVG to download a scalable/vector rendering
- of the diagram. Can be used to present the diagram without degrading when
- zooming.
+ - Press the Copy image reference to the clipboard to be able to insert
+ the image representation of the diagram into a text note. See Image references for more information.
+ - Press the Export diagram as SVG to download a scalable/vector rendering
+ of the diagram. Can be used to present the diagram without degrading when
+ zooming.
- Press the Export diagram as PNG to download a normal image (at
1x scale, raster) of the diagram. Can be used to send the diagram in more
traditional channels such as e-mail.
-
-
+
+
Errors in the diagram
If there is an error in the source code, the error will be displayed in
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html
index 4a33c30bfe..efff5c09b9 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html
@@ -13,13 +13,11 @@
- HTML language for the legacy/vanilla method, with what needs to be displayed
(for example
<p>Hello world.</p>).
- - JSX for the Preact-based approach (see below).
-
+ - JSX for the Preact-based approach (see below).
+
Create a Render Note.
- Assign the renderNote relation to
+ Assign the renderNote relation to
point at the previously created code note.
Legacy scripting using jQuery
@@ -48,9 +46,10 @@ $dateEl.text(new Date());
need to provide a HTML anymore.
Here are the steps to creating a simple render note:
Create a note of type Render Note.
+Create a child Code note
with JSX as the language.
As an example, use the following content:
export default function() {
@@ -60,17 +59,20 @@ $dateEl.text(new Date());
</>
);
}
- ~renderNote relation
- pointing to the newly created child.In the parent render note, define a ~renderNote relation
+ pointing to the newly created child.
Refresh the render note and it should display a “Hello world” message.
+It's possible to refresh the note via:
If you would like us to work on these features, consider supporting us.
Fore more information see Formatting toolbar.
Here's a list of various features supported by text notes:
-| Dedicated article | -Feature | -
|---|---|
| General formatting - | -
-
|
-
| Lists - | -
-
|
-
| Block quotes & admonitions - | -
-
|
-
| Tables - | -
-
|
-
| Developer-specific formatting - | -
-
|
-
| Footnotes - | -
-
|
-
| Images - | -
-
|
-
| Links - | -
-
|
-
| Include Note - | -
-
|
-
| Insert buttons - | -
-
|
-
| Other features - | -
-
|
-
| Premium features - | -
-
|
-
Text notes are usually opened in edit mode. However, they may open in - read-only mode if the note is too big or the note is explicitly marked - as read-only. For more information, see Read-Only Notes.
-There are numerous keyboard shortcuts to format the text without having - to use the mouse. For a reference of all the key combinations, see - Keyboard Shortcuts. In addition, see Markdown-like formatting as an alternative - to the keyboard shortcuts.
-For the text editing functionality, Trilium uses a commercial product - (with an open-source base) called CKEditor. - This brings the benefit of having a powerful WYSIWYG (What You See Is What - You Get) editor.
\ No newline at end of file +| Dedicated article | +Feature | +
|---|---|
| General formatting + | +
+
|
+
| Lists + | +
+
|
+
| Block quotes & admonitions + | +
+
|
+
| Tables + | +
+
|
+
| Developer-specific formatting + | +
+
|
+
| Footnotes + | +
+
|
+
| Images + | +
+
|
+
| Links + | +
+
|
+
| Include Note + | +
+
|
+
| Insert buttons + | +
+
|
+
| Other features + | +
+
|
+
| Premium features + | +
+
|
+
Text notes are usually opened in edit mode. However, they may open in + read-only mode if the note is too big or the note is explicitly marked + as read-only. For more information, see Read-Only Notes.
+There are numerous keyboard shortcuts to format the text without having + to use the mouse. For a reference of all the key combinations, see + Keyboard Shortcuts. In addition, see Markdown-like formatting as an alternative + to the keyboard shortcuts.
+For the text editing functionality, Trilium uses a commercial product + (with an open-source base) called CKEditor. + This brings the benefit of having a powerful WYSIWYG (What You See Is What + You Get) editor.
\ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md index aae807a49d..8b2fdc9998 100644 --- a/docs/Developer Guide/Developer Guide/Documentation.md +++ b/docs/Developer Guide/Developer Guide/Documentation.md @@ -1,5 +1,5 @@ # Documentation -There are multiple types of documentation for Trilium:
+There are multiple types of documentation for Trilium:
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json
index d1b8f5d265..92af202232 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -3984,42 +3984,42 @@
"name": "internalLink",
"value": "s1aBHPd79XYj",
"isInheritable": false,
- "position": 30
+ "position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "6RM1Q7ppFVoj",
"isInheritable": false,
- "position": 40
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "CoFPLs3dRlXc",
- "isInheritable": false,
- "position": 50
+ "position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "8YBEPzcpUgxw",
"isInheritable": false,
- "position": 60
+ "position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "IjZS7iK5EXtb",
"isInheritable": false,
- "position": 70
+ "position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "XpOYSgsLkTJy",
"isInheritable": false,
- "position": 80
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "CoFPLs3dRlXc",
+ "isInheritable": false,
+ "position": 60
},
{
"type": "label",
@@ -10147,17 +10147,24 @@
{
"type": "relation",
"name": "internalLink",
- "value": "XpOYSgsLkTJy",
+ "value": "SL5f1Auq7sVN",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
- "value": "0Ofbk1aSuVRu",
+ "value": "XpOYSgsLkTJy",
"isInheritable": false,
"position": 30
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "0Ofbk1aSuVRu",
+ "isInheritable": false,
+ "position": 40
+ },
{
"type": "label",
"name": "shareAlias",
@@ -10171,13 +10178,6 @@
"value": "bx bx-selection",
"isInheritable": false,
"position": 20
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "SL5f1Auq7sVN",
- "isInheritable": false,
- "position": 40
}
],
"format": "markdown",
@@ -10839,39 +10839,53 @@
"type": "text",
"mime": "text/html",
"attributes": [
- {
- "type": "label",
- "name": "iconClass",
- "value": "bx bxl-markdown",
- "isInheritable": false,
- "position": 30
- },
- {
- "type": "label",
- "name": "shareAlias",
- "value": "markdown",
- "isInheritable": false,
- "position": 40
- },
{
"type": "relation",
"name": "internalLink",
"value": "Oau6X9rCuegd",
"isInheritable": false,
- "position": 50
+ "position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "iPIMuisry3hd",
"isInheritable": false,
- "position": 60
+ "position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "6f9hih2hXXZk",
"isInheritable": false,
+ "position": 30
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "SL5f1Auq7sVN",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "nBAXQFj20hS1",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "hrZ1D00cLbal",
+ "isInheritable": false,
+ "position": 60
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "m1lbrzyKDaRB",
+ "isInheritable": false,
"position": 70
},
{
@@ -10886,56 +10900,21 @@
"name": "internalLink",
"value": "wy8So3yZZlH9",
"isInheritable": false,
- "position": 150
+ "position": 90
},
{
- "type": "relation",
- "name": "internalLink",
- "value": "SL5f1Auq7sVN",
+ "type": "label",
+ "name": "iconClass",
+ "value": "bx bxl-markdown",
"isInheritable": false,
- "position": 160
+ "position": 30
},
{
- "type": "relation",
- "name": "internalLink",
- "value": "NwBbFdNZ9h7O",
+ "type": "label",
+ "name": "shareAlias",
+ "value": "markdown",
"isInheritable": false,
- "position": 170
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "YfYAtQBcfo5V",
- "isInheritable": false,
- "position": 180
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "s1aBHPd79XYj",
- "isInheritable": false,
- "position": 190
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "nBAXQFj20hS1",
- "isInheritable": false,
- "position": 200
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "hrZ1D00cLbal",
- "isInheritable": false,
- "position": 210
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "m1lbrzyKDaRB",
- "isInheritable": false,
- "position": 220
+ "position": 40
}
],
"format": "markdown",
diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
index b110d09b03..221ba93c9a 100644
--- a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
+++ b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
@@ -99,9 +99,9 @@ To do so:
For example, to change the font of the document from the one defined by the theme or the user to a serif one:
```
-body {
- --main-font-family: serif !important;
- --detail-font-family: var(--main-font-family) !important;
+:root {
+ --print-font-family: serif;
+ --print-font-size: 11pt;
}
```
diff --git a/docs/User Guide/User Guide/Installation & Setup/Data directory.md b/docs/User Guide/User Guide/Installation & Setup/Data directory.md
index 3a447f82f2..b9df162b9e 100644
--- a/docs/User Guide/User Guide/Installation & Setup/Data directory.md
+++ b/docs/User Guide/User Guide/Installation & Setup/Data directory.md
@@ -97,4 +97,4 @@ Apart from the data directory, some of the subdirectories of it can be moved els
| `TRILIUM_TMP_DIR` | `${TRILIUM_DATA_DIR}/tmp` | Directory where temporary files are stored (for example when opening in an external app). |
| `TRILIUM_ANONYMIZED_DB_DIR` | `${TRILIUM_DATA_DIR}/anonymized-db` | Directory where a Anonymized Database is stored. |
| `TRILIUM_CONFIG_INI_PATH` | `${TRILIUM_DATA_DIR}/config.ini` | Path to Configuration (config.ini or environment variables) file. |
-| `TRILIUM_ELECTRON_DATA_DIR` | `${TRILIUM_DATA_DIR}/electron-user-data` (portable) or system appData (default) | Directory where Electron stores internal data such as caches and spell-check dictionaries (desktop only). |
+| `TRILIUM_ELECTRON_DATA_DIR` | `${TRILIUM_DATA_DIR}/electron-user-data` (portable) or system appData (default) | Directory where Electron stores internal data such as caches and spell-check dictionaries (desktop only). |
\ No newline at end of file
diff --git a/docs/User Guide/User Guide/Note Types/Markdown.md b/docs/User Guide/User Guide/Note Types/Markdown.md
index b96b627329..4913eb5f83 100644
--- a/docs/User Guide/User Guide/Note Types/Markdown.md
+++ b/docs/User Guide/User Guide/Note Types/Markdown.md
@@ -25,15 +25,12 @@ Even if Markdown is now specially treated by having a preview mechanism, Trilium
The following features are supported by Trilium's Markdown format and will show up in the preview pane:
* All standard and GitHub-flavored syntax (basic formatting, tables, blockquotes)
-* Code blocks with syntax highlight (e.g. ` ```js `) and automatic syntax highlight
-* Block quotes & admonitions
-* Math Equations
-* Mermaid Diagrams using ` ```mermaid `
+* Code blocks with syntax highlight (e.g. `mermaid`
* Include Note (no builtin Markdown syntax, but HTML syntax works just fine):
```
For example, to change the font of the document from the one defined by - the theme or the user to a serif one:
:root {
+ the theme or the user to a serif one:body {
--print-font-family: serif;
--print-font-size: 11pt;
}
+
To remark:
- Multiple CSS notes can be add by using multiple
~printCss relations.
diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
index 221ba93c9a..7055825a43 100644
--- a/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
+++ b/docs/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.md
@@ -99,12 +99,15 @@ To do so:
For example, to change the font of the document from the one defined by the theme or the user to a serif one:
```
-:root {
+body {
--print-font-family: serif;
--print-font-size: 11pt;
}
```
+> [!IMPORTANT]
+> When altering `--print-font-family`, make sure the change is done at `body` level and not `:root`, since otherwise it won't be picked up due to specificity rules.
+
To remark:
* Multiple CSS notes can be add by using multiple `~printCss` relations.
From cc010e15680713aea8ed704827075b77341bf0ca Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 09:54:19 +0300
Subject: [PATCH 13/46] chore(desktop/appimage): fix path for packaging
---
apps/desktop/scripts/build-appimage.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/desktop/scripts/build-appimage.sh b/apps/desktop/scripts/build-appimage.sh
index d4016ce134..ec728470a9 100755
--- a/apps/desktop/scripts/build-appimage.sh
+++ b/apps/desktop/scripts/build-appimage.sh
@@ -30,7 +30,7 @@ case "$ARCH" in
esac
# Find the packaged app directory
-PACKAGED_DIR="$DESKTOP_DIR/out/$PRODUCT_NAME-linux-$ARCH"
+PACKAGED_DIR="$DESKTOP_DIR/dist/out/$PRODUCT_NAME-linux-$ARCH"
if [ ! -d "$PACKAGED_DIR" ]; then
echo "Error: Packaged app not found at $PACKAGED_DIR"
echo "Run 'electron-forge make' or 'electron-forge package' first."
@@ -40,7 +40,7 @@ fi
echo "Building AppImage from: $PACKAGED_DIR"
# Create AppDir structure
-APPDIR="$DESKTOP_DIR/out/$PRODUCT_NAME.AppDir"
+APPDIR="$DESKTOP_DIR/dist/out/$PRODUCT_NAME.AppDir"
rm -rf "$APPDIR"
mkdir -p "$APPDIR"
From c0b1ff31e5c51c539510b3d65e58dba0d95f678c Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 09:56:41 +0300
Subject: [PATCH 14/46] fix(server): safe import strips out bookmarks from note
---
.../src/services/html_sanitizer.spec.ts | 22 +++++++++++++++++++
apps/server/src/services/html_sanitizer.ts | 1 +
2 files changed, 23 insertions(+)
diff --git a/apps/server/src/services/html_sanitizer.spec.ts b/apps/server/src/services/html_sanitizer.spec.ts
index dfbba8fd71..6af366a329 100644
--- a/apps/server/src/services/html_sanitizer.spec.ts
+++ b/apps/server/src/services/html_sanitizer.spec.ts
@@ -50,4 +50,26 @@ describe("sanitize", () => {
`;
expect(html_sanitizer.sanitize(dirty)).toBe(clean);
});
+
+ describe("bookmark anchors", () => {
+ it("preserves id attribute on empty tags (CKEditor bookmarks)", () => {
+ const dirty = ``;
+ expect(html_sanitizer.sanitize(dirty)).toBe(dirty);
+ });
+
+ it("preserves id attribute on tags with bookmark class", () => {
+ const dirty = ``;
+ expect(html_sanitizer.sanitize(dirty)).toBe(dirty);
+ });
+
+ it("strips id attribute from non-anchor tags to prevent DOM clobbering", () => {
+ const dirty = `content`;
+ expect(html_sanitizer.sanitize(dirty)).toBe(`content`);
+ });
+
+ it("strips id attribute from
tags to prevent DOM clobbering", () => {
+ const dirty = `
`;
+ expect(html_sanitizer.sanitize(dirty)).toBe(`
`);
+ });
+ });
});
diff --git a/apps/server/src/services/html_sanitizer.ts b/apps/server/src/services/html_sanitizer.ts
index f304dcf150..76262c2a32 100644
--- a/apps/server/src/services/html_sanitizer.ts
+++ b/apps/server/src/services/html_sanitizer.ts
@@ -42,6 +42,7 @@ function sanitize(dirtyHtml: string) {
allowedTags: allowedTags as string[],
allowedAttributes: {
"*": ["class", "style", "title", "src", "href", "hash", "disabled", "align", "alt", "center", "data-*"],
+ a: ["id"], // CKEditor bookmark anchors use
input: ["type", "checked"],
img: ["width", "height"],
code: [ "spellcheck" ]
From 5b957dd111c0f6b9443a69dfc3ee9832ab7cdf52 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:06:32 +0300
Subject: [PATCH 15/46] chore(server): start processing bookmarks
---
apps/server/src/services/notes.spec.ts | 34 ++++++++++++++++++
apps/server/src/services/notes.ts | 49 ++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
create mode 100644 apps/server/src/services/notes.spec.ts
diff --git a/apps/server/src/services/notes.spec.ts b/apps/server/src/services/notes.spec.ts
new file mode 100644
index 0000000000..f19b9be4b7
--- /dev/null
+++ b/apps/server/src/services/notes.spec.ts
@@ -0,0 +1,34 @@
+import { describe, expect, it } from "vitest";
+import { findBookmarks } from "./notes.js";
+
+describe("findBookmarks", () => {
+ it("extracts bookmark IDs from empty anchor tags", () => {
+ const content = `Hello
World
`;
+ expect(findBookmarks(content)).toEqual(["chapter-1"]);
+ });
+
+ it("extracts multiple bookmarks", () => {
+ const content = `Text
`;
+ expect(findBookmarks(content)).toEqual(["intro", "conclusion"]);
+ });
+
+ it("returns empty array when no bookmarks exist", () => {
+ const content = `No bookmarks here
`;
+ expect(findBookmarks(content)).toEqual([]);
+ });
+
+ it("ignores anchor tags with href (regular links, not bookmarks)", () => {
+ const content = `link`;
+ expect(findBookmarks(content)).toEqual([]);
+ });
+
+ it("handles bookmarks with various valid ID characters", () => {
+ const content = ``;
+ expect(findBookmarks(content)).toEqual(["my_bookmark-2.0"]);
+ });
+
+ it("does not produce duplicates", () => {
+ const content = ``;
+ expect(findBookmarks(content)).toEqual(["same"]);
+ });
+});
diff --git a/apps/server/src/services/notes.ts b/apps/server/src/services/notes.ts
index 708cab285d..719afbe225 100644
--- a/apps/server/src/services/notes.ts
+++ b/apps/server/src/services/notes.ts
@@ -454,6 +454,54 @@ function findImageLinks(content: string, foundLinks: FoundLink[]) {
return content.replace(/src="[^"]*\/api\/images\//g, 'src="api/images/');
}
+/**
+ * Extracts bookmark IDs from CKEditor bookmark anchors (`` without href).
+ * Bookmarks are stored as labels on the note so they can be looked up without parsing content.
+ */
+export function findBookmarks(content: string): string[] {
+ const re = /]*><\/a>/g;
+ const bookmarks: string[] = [];
+ let match;
+
+ while ((match = re.exec(content))) {
+ // Skip anchors that also have an href (those are regular links, not bookmarks)
+ if (match[0].includes("href=")) {
+ continue;
+ }
+
+ const id = match[1];
+ if (!bookmarks.includes(id)) {
+ bookmarks.push(id);
+ }
+ }
+
+ return bookmarks;
+}
+
+function saveBookmarks(note: BNote, content: string) {
+ const foundBookmarks = findBookmarks(content);
+ const existingBookmarks = note.getLabels("internalBookmark");
+
+ for (const bookmarkId of foundBookmarks) {
+ const existing = existingBookmarks.find((l) => l.value === bookmarkId);
+
+ if (!existing) {
+ new BAttribute({
+ noteId: note.noteId,
+ type: "label",
+ name: "internalBookmark",
+ value: bookmarkId
+ }).save();
+ }
+ }
+
+ // Remove bookmarks that are no longer in the content
+ const unusedBookmarks = existingBookmarks.filter((l) => !foundBookmarks.includes(l.value));
+ for (const unused of unusedBookmarks) {
+ unused.markAsDeleted();
+ }
+}
+
function findInternalLinks(content: string, foundLinks: FoundLink[]) {
const re = /href="[^"]*#root[a-zA-Z0-9_\/]*\/([a-zA-Z0-9_]+)\/?"/g;
let match;
@@ -695,6 +743,7 @@ function saveLinks(note: BNote, content: string | Buffer) {
content = findImageLinks(content, foundLinks);
content = findInternalLinks(content, foundLinks);
content = findIncludeNoteLinks(content, foundLinks);
+ saveBookmarks(note, content);
({ forceFrontendReload, content } = checkImageAttachments(note, content));
} else if (note.type === "relationMap" && typeof content === "string") {
From 79d639108b75a03d297064b5ffe47e5045b568af Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:12:41 +0300
Subject: [PATCH 16/46] feat(status_bar): display system links in dev mode
---
apps/client/src/widgets/layout/StatusBar.css | 3 +-
apps/client/src/widgets/layout/StatusBar.tsx | 6 ++
.../widgets/ribbon/AutoLinkAttributesTab.tsx | 65 +++++++++++++++++++
3 files changed, 73 insertions(+), 1 deletion(-)
create mode 100644 apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx
diff --git a/apps/client/src/widgets/layout/StatusBar.css b/apps/client/src/widgets/layout/StatusBar.css
index c8d01c83be..f0a10ab8b8 100644
--- a/apps/client/src/widgets/layout/StatusBar.css
+++ b/apps/client/src/widgets/layout/StatusBar.css
@@ -87,7 +87,8 @@
font-weight: 600;
}
- .inherited-attributes-widget {
+ .inherited-attributes-widget,
+ .auto-link-attributes-widget {
display: inline;
> div {
diff --git a/apps/client/src/widgets/layout/StatusBar.tsx b/apps/client/src/widgets/layout/StatusBar.tsx
index 077c59ef4f..50657867cd 100644
--- a/apps/client/src/widgets/layout/StatusBar.tsx
+++ b/apps/client/src/widgets/layout/StatusBar.tsx
@@ -26,6 +26,7 @@ import LinkButton from "../react/LinkButton";
import { ParentComponent } from "../react/react_utils";
import { ContentLanguagesModal, NoteTypeCodeNoteList, NoteTypeOptionsModal, useLanguageSwitcher, useMimeTypes } from "../ribbon/BasicPropertiesTab";
import AttributeEditor, { AttributeEditorImperativeHandlers } from "../ribbon/components/AttributeEditor";
+import AutoLinkAttributesTab from "../ribbon/AutoLinkAttributesTab";
import InheritedAttributesTab from "../ribbon/InheritedAttributesTab";
import { NoteSizeWidget, useNoteMetadata } from "../ribbon/NoteInfoTab";
import { NotePathsWidget, useSortedNotePaths } from "../ribbon/NotePathsTab";
@@ -401,6 +402,11 @@ function AttributesPane({ note, noteContext, attributesShown, setAttributesShown
{t("inherited_attribute_list.title")}
+ {glob.isDev &&
+ {t("auto_link_attribute_list.title")}
+
+ }
+
;
+
+export default function AutoLinkAttributesTab({ note, componentId }: AutoLinkAttributesTabArgs) {
+ const [autoLinkAttributes, setAutoLinkAttributes] = useState();
+
+ function refresh() {
+ if (!note) return;
+ const attrs = note.getAttributes().filter((attr) => attr.isAutoLink && attr.noteId === note.noteId);
+ setAutoLinkAttributes(attrs);
+ }
+
+ useEffect(refresh, [note]);
+ useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
+ if (loadResults.getAttributeRows(componentId).find((attr) => attributes.isAffecting(attr, note))) {
+ refresh();
+ }
+ });
+
+ if (!autoLinkAttributes?.length) {
+ return null;
+ }
+
+ return (
+
+
+ {joinElements(autoLinkAttributes.map((attribute) => (
+
+ )), " ")}
+
+
+ );
+}
+
+function AutoLinkAttribute({ attribute }: { attribute: FAttribute }) {
+ const [html, setHtml] = useState("");
+
+ useEffect(() => {
+ renderAutoLink(attribute).then(setHtml);
+ }, [attribute]);
+
+ return ;
+}
+
+async function renderAutoLink(attribute: FAttribute) {
+ const note = await froca.getNote(attribute.value);
+ if (!note) return "";
+
+ const link = `${escapeHtml(note.title)}`;
+ return `~${escapeHtml(attribute.name)}=${link}`;
+}
+
+function escapeHtml(text: string) {
+ const el = document.createElement("span");
+ el.textContent = text;
+ return el.innerHTML;
+}
From bdf4e40577b712d9deb619894572358a35c9d9d1 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:14:58 +0300
Subject: [PATCH 17/46] fix(server): bookmarks not processed due to
self-closing tag
---
apps/client/src/translations/en/translation.json | 3 +++
apps/server/src/services/notes.spec.ts | 8 ++++++++
apps/server/src/services/notes.ts | 2 +-
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index e284c16b78..ef322e1fb1 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -860,6 +860,9 @@
"no_inherited_attributes": "No inherited attributes.",
"none": "none"
},
+ "auto_link_attribute_list": {
+ "title": "System Links"
+ },
"note_info_widget": {
"note_id": "Note ID",
"created": "Created",
diff --git a/apps/server/src/services/notes.spec.ts b/apps/server/src/services/notes.spec.ts
index f19b9be4b7..38d99fc7d7 100644
--- a/apps/server/src/services/notes.spec.ts
+++ b/apps/server/src/services/notes.spec.ts
@@ -31,4 +31,12 @@ describe("findBookmarks", () => {
const content = ``;
expect(findBookmarks(content)).toEqual(["same"]);
});
+
+ it("matches self-closing bookmark anchors (CKEditor empty elements)", () => {
+ const content = `Text
More
`;
+ // CKEditor may also output without closing tag
+ const contentNoClose = `Text
More
`;
+ expect(findBookmarks(content)).toEqual(["my-bookmark"]);
+ expect(findBookmarks(contentNoClose)).toEqual(["my-bookmark"]);
+ });
});
diff --git a/apps/server/src/services/notes.ts b/apps/server/src/services/notes.ts
index 719afbe225..56499f0288 100644
--- a/apps/server/src/services/notes.ts
+++ b/apps/server/src/services/notes.ts
@@ -459,7 +459,7 @@ function findImageLinks(content: string, foundLinks: FoundLink[]) {
* Bookmarks are stored as labels on the note so they can be looked up without parsing content.
*/
export function findBookmarks(content: string): string[] {
- const re = /]*><\/a>/g;
+ const re = /]*>(<\/a>)?/g;
const bookmarks: string[] = [];
let match;
From 84fff307230ff288b054f85a49294d7fa872143c Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:17:39 +0300
Subject: [PATCH 18/46] feat(server): mark bookmarks as internal links
---
apps/client/src/entities/fattribute.ts | 10 +++++++++-
apps/server/src/services/attributes.ts | 2 +-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/entities/fattribute.ts b/apps/client/src/entities/fattribute.ts
index 07a2b22c4f..bdccead31d 100644
--- a/apps/client/src/entities/fattribute.ts
+++ b/apps/client/src/entities/fattribute.ts
@@ -66,7 +66,15 @@ class FAttribute {
}
get isAutoLink() {
- return this.type === "relation" && ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name);
+ if (this.type === "relation") {
+ return ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name);
+ }
+
+ if (this.type === "label") {
+ return this.name === "internalBookmark";
+ }
+
+ return false;
}
get toString() {
diff --git a/apps/server/src/services/attributes.ts b/apps/server/src/services/attributes.ts
index 2e1a207447..c9642d4777 100644
--- a/apps/server/src/services/attributes.ts
+++ b/apps/server/src/services/attributes.ts
@@ -77,7 +77,7 @@ function getAttributeNames(type: string, nameLike: string) {
}
}
- names = names.filter((name) => !["internalLink", "imageLink", "includeNoteLink", "relationMapLink"].includes(name));
+ names = names.filter((name) => !["internalLink", "imageLink", "includeNoteLink", "relationMapLink", "internalBookmark"].includes(name));
names.sort((a, b) => {
const aPrefix = a.toLowerCase().startsWith(nameLike);
From 4dcbd36b2de94d37f1e9ef32dd366695cf762350 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:22:58 +0300
Subject: [PATCH 19/46] feat(text): add a slash command for bookmarks
---
packages/ckeditor5/src/extra_slash_commands.ts | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/packages/ckeditor5/src/extra_slash_commands.ts b/packages/ckeditor5/src/extra_slash_commands.ts
index ee6068dc3d..fb86e18efa 100644
--- a/packages/ckeditor5/src/extra_slash_commands.ts
+++ b/packages/ckeditor5/src/extra_slash_commands.ts
@@ -17,6 +17,8 @@ import internalLinkIcon from './icons/trilium.svg?raw';
import noteIcon from './icons/note.svg?raw';
import importMarkdownIcon from './icons/markdown-mark.svg?raw';
import { icons as mathIcons, MathUI } from '@triliumnext/ckeditor5-math';
+import { BookmarkUI } from "ckeditor5";
+import bxBookmark from "boxicons/svg/regular/bx-bookmark.svg?raw";
type SlashCommandDefinition = SlashCommandEditorConfig["extraCommands"][number];
@@ -74,6 +76,19 @@ export default function buildExtraCommands(): SlashCommandDefinition[] {
description: "Import a markdown file into this note",
icon: importMarkdownIcon,
commandName: MARKDOWN_IMPORT_COMMAND
+ },
+ {
+ id: "bookmark",
+ title: "Bookmark",
+ description: "Insert a bookmark anchor for internal linking",
+ aliases: [ "anchor" ],
+ icon: bxBookmark,
+ execute: (editor: Editor) => {
+ // Defer to the next event loop tick so the slash command fully finishes
+ // its DOM/selection cleanup; _showFormView needs the view and mapper to
+ // be in a settled state for balloon positioning.
+ setTimeout(() => (editor.plugins.get(BookmarkUI) as any)._showFormView(), 0);
+ }
}
];
}
From ae004c4334c4b2c69862fc2ac8eb833b913c325a Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:28:29 +0300
Subject: [PATCH 20/46] feat(text): basic insert link with anchors
---
apps/client/src/services/link.ts | 4 ++-
.../src/translations/en/translation.json | 4 ++-
apps/client/src/widgets/dialogs/add_link.tsx | 32 +++++++++++++++++--
.../type_widgets/text/EditableText.tsx | 8 +++++
.../type_widgets/text/ReadOnlyText.tsx | 14 +++++++-
apps/server/src/becca/entities/battribute.ts | 10 +++++-
.../commons/src/lib/builtin_attributes.ts | 1 +
7 files changed, 67 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/services/link.ts b/apps/client/src/services/link.ts
index 3405161dd5..b6c5dbfdb6 100644
--- a/apps/client/src/services/link.ts
+++ b/apps/client/src/services/link.ts
@@ -60,6 +60,8 @@ export interface ViewScope {
*/
tocPreviousVisible?: boolean;
tocCollapsedHeadings?: Set;
+ /** When set, scrolls to a bookmark anchor within the note after navigation. */
+ bookmark?: string;
}
interface CreateLinkOptions {
@@ -244,7 +246,7 @@ export function parseNavigationStateFromUrl(url: string | undefined) {
hoistedNoteId = value;
} else if (name === "searchString") {
searchString = value; // supports triggering search from URL, e.g. #?searchString=blabla
- } else if (["viewMode", "attachmentId"].includes(name)) {
+ } else if (["viewMode", "attachmentId", "bookmark"].includes(name)) {
(viewScope as any)[name] = value;
} else if (name === "popup") {
openInPopup = true;
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ef322e1fb1..3145ff7f03 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -41,6 +41,8 @@
"link_title_mirrors": "link title mirrors the note's current title",
"link_title_arbitrary": "link title can be changed arbitrarily",
"link_title": "Link title",
+ "bookmark": "Bookmark (optional)",
+ "bookmark_none": "None (link to note)",
"button_add_link": "Add link"
},
"branch_prefix": {
@@ -861,7 +863,7 @@
"none": "none"
},
"auto_link_attribute_list": {
- "title": "System Links"
+ "title": "System Attributes"
},
"note_info_widget": {
"note_id": "Note ID",
diff --git a/apps/client/src/widgets/dialogs/add_link.tsx b/apps/client/src/widgets/dialogs/add_link.tsx
index 4bb1d1711c..9d1f5522ff 100644
--- a/apps/client/src/widgets/dialogs/add_link.tsx
+++ b/apps/client/src/widgets/dialogs/add_link.tsx
@@ -5,6 +5,7 @@ import FormRadioGroup from "../react/FormRadioGroup";
import NoteAutocomplete from "../react/NoteAutocomplete";
import { useRef, useState, useEffect } from "preact/hooks";
import tree from "../../services/tree";
+import froca from "../../services/froca";
import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
import { logError } from "../../services/ws";
import FormGroup from "../react/FormGroup.js";
@@ -24,6 +25,8 @@ export default function AddLinkDialog() {
const [ linkTitle, setLinkTitle ] = useState("");
const [ linkType, setLinkType ] = useState();
const [ suggestion, setSuggestion ] = useState(null);
+ const [ bookmarks, setBookmarks ] = useState([]);
+ const [ selectedBookmark, setSelectedBookmark ] = useState("");
const [ shown, setShown ] = useState(false);
const hasSubmittedRef = useRef(false);
@@ -61,6 +64,11 @@ export default function AddLinkDialog() {
const noteId = tree.getNoteIdFromUrl(suggestion.notePath);
if (noteId) {
setDefaultLinkTitle(noteId);
+ froca.getNote(noteId).then((note) => {
+ const bkms = note?.getLabels("internalBookmark").map((l) => l.value) ?? [];
+ setBookmarks(bkms);
+ setSelectedBookmark("");
+ });
}
resetExternalLink();
}
@@ -114,8 +122,11 @@ export default function AddLinkDialog() {
hasSubmittedRef.current = false;
if (suggestion.notePath) {
- // Handle note link
- opts.addLink(suggestion.notePath, linkType === "reference-link" ? null : linkTitle);
+ // Handle note link, optionally with a bookmark anchor
+ const path = selectedBookmark
+ ? `${suggestion.notePath}?bookmark=${encodeURIComponent(selectedBookmark)}`
+ : suggestion.notePath;
+ opts.addLink(path, linkType === "reference-link" ? null : linkTitle);
} else if (suggestion.externalLink) {
// Handle external link
opts.addLink(suggestion.externalLink, linkTitle, true);
@@ -123,6 +134,8 @@ export default function AddLinkDialog() {
}
setSuggestion(null);
+ setBookmarks([]);
+ setSelectedBookmark("");
setShown(false);
}}
show={shown}
@@ -138,6 +151,21 @@ export default function AddLinkDialog() {
/>
+ {bookmarks.length > 0 && (
+
+
+
+ )}
+
{!opts?.hasSelection && (
{(linkType !== "external-link") && (
diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx
index 8cacc40e25..a002cde7e9 100644
--- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx
+++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx
@@ -263,6 +263,14 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
// We are not using CKEditor's built-in watch dog content, instead we are using the data we store regularly in the spaced update (see `dataSaved`).
editor.setData(contentRef.current);
parentComponent?.triggerEvent("textEditorRefreshed", { ntxId, editor });
+
+ // Scroll to bookmark anchor if navigated with ?bookmark=...
+ const viewScope = noteContext?.viewScope;
+ if (viewScope?.bookmark) {
+ const el = editor.editing.view.getDomRoot()?.querySelector(`[id="${CSS.escape(viewScope.bookmark)}"]`);
+ el?.scrollIntoView({ behavior: "smooth", block: "center" });
+ viewScope.bookmark = undefined;
+ }
}}
/>}
diff --git a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx
index da7e08ebab..a58b21de54 100644
--- a/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx
+++ b/apps/client/src/widgets/type_widgets/text/ReadOnlyText.tsx
@@ -6,7 +6,7 @@ import "@triliumnext/ckeditor5";
import clsx from "clsx";
import { Ref } from "preact";
-import { useEffect, useLayoutEffect, useMemo } from "preact/hooks";
+import { useEffect, useLayoutEffect, useMemo, useRef as usePreactRef } from "preact/hooks";
import appContext from "../../../components/app_context";
import FNote from "../../../entities/fnote";
@@ -24,6 +24,17 @@ import { loadIncludedNote, refreshIncludedNote, setupImageOpening } from "./util
export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetProps) {
const blob = useNoteBlob(note);
const { isRtl } = useNoteLanguage(note);
+ const readOnlyContentRef = usePreactRef(null);
+
+ // Scroll to bookmark anchor if navigated with ?bookmark=...
+ useEffect(() => {
+ const viewScope = noteContext?.viewScope;
+ if (!viewScope?.bookmark || !readOnlyContentRef.current) return;
+
+ const el = readOnlyContentRef.current.querySelector(`[id="${CSS.escape(viewScope.bookmark)}"]`);
+ el?.scrollIntoView({ behavior: "smooth", block: "center" });
+ viewScope.bookmark = undefined;
+ }, [blob]);
return (
<>
@@ -31,6 +42,7 @@ export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetPro
html={blob?.content ?? ""}
ntxId={ntxId}
dir={isRtl ? "rtl" : "ltr"}
+ contentRef={readOnlyContentRef}
/>
diff --git a/apps/server/src/becca/entities/battribute.ts b/apps/server/src/becca/entities/battribute.ts
index dbb6502113..997b297750 100644
--- a/apps/server/src/becca/entities/battribute.ts
+++ b/apps/server/src/becca/entities/battribute.ts
@@ -119,7 +119,15 @@ class BAttribute extends AbstractBeccaEntity {
}
isAutoLink() {
- return this.type === "relation" && ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name);
+ if (this.type === "relation") {
+ return ["internalLink", "imageLink", "relationMapLink", "includeNoteLink"].includes(this.name);
+ }
+
+ if (this.type === "label") {
+ return this.name === "internalBookmark";
+ }
+
+ return false;
}
get note() {
diff --git a/packages/commons/src/lib/builtin_attributes.ts b/packages/commons/src/lib/builtin_attributes.ts
index 76cdf033dc..64a2ad48d7 100644
--- a/packages/commons/src/lib/builtin_attributes.ts
+++ b/packages/commons/src/lib/builtin_attributes.ts
@@ -91,6 +91,7 @@ export default [
{ type: "label", name: "printPageSize" },
{ type: "label", name: "printScale" },
{ type: "label", name: "printMargins" },
+ { type: "label", name: "internalBookmark" },
// relation names
{ type: "relation", name: "internalLink" },
From 480da09bcca6d43305230a336b0ba47bbdcaa42f Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:30:03 +0300
Subject: [PATCH 21/46] fix(client): `internalBookmark` not hidden
---
apps/client/src/services/attribute_renderer.ts | 7 ++++---
apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx | 4 ++++
apps/client/src/widgets/type_widgets/text/EditableText.tsx | 7 -------
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/apps/client/src/services/attribute_renderer.ts b/apps/client/src/services/attribute_renderer.ts
index 01d6337367..c93620b3b2 100644
--- a/apps/client/src/services/attribute_renderer.ts
+++ b/apps/client/src/services/attribute_renderer.ts
@@ -7,6 +7,10 @@ async function renderAttribute(attribute: FAttribute, renderIsInheritable: boole
const isInheritable = renderIsInheritable && attribute.isInheritable ? `(inheritable)` : "";
const $attr = $("");
+ if (attribute.isAutoLink) {
+ return $attr;
+ }
+
if (attribute.type === "label") {
$attr.append(document.createTextNode(`#${attribute.name}${isInheritable}`));
@@ -15,9 +19,6 @@ async function renderAttribute(attribute: FAttribute, renderIsInheritable: boole
$attr.append(document.createTextNode(formatValue(attribute.value)));
}
} else if (attribute.type === "relation") {
- if (attribute.isAutoLink) {
- return $attr;
- }
// when the relation has just been created, then it might not have a value
if (attribute.value) {
diff --git a/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx b/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx
index 132a55a8a3..1d54edbfb6 100644
--- a/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx
+++ b/apps/client/src/widgets/ribbon/AutoLinkAttributesTab.tsx
@@ -51,6 +51,10 @@ function AutoLinkAttribute({ attribute }: { attribute: FAttribute }) {
}
async function renderAutoLink(attribute: FAttribute) {
+ if (attribute.type === "label") {
+ return `#${escapeHtml(attribute.name)}=${escapeHtml(attribute.value)}`;
+ }
+
const note = await froca.getNote(attribute.value);
if (!note) return "";
diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx
index a002cde7e9..760e00e489 100644
--- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx
+++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx
@@ -264,13 +264,6 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
editor.setData(contentRef.current);
parentComponent?.triggerEvent("textEditorRefreshed", { ntxId, editor });
- // Scroll to bookmark anchor if navigated with ?bookmark=...
- const viewScope = noteContext?.viewScope;
- if (viewScope?.bookmark) {
- const el = editor.editing.view.getDomRoot()?.querySelector(`[id="${CSS.escape(viewScope.bookmark)}"]`);
- el?.scrollIntoView({ behavior: "smooth", block: "center" });
- viewScope.bookmark = undefined;
- }
}}
/>}
From 131e10f4fe5810efa055173dd5f49bc85c057ec9 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:35:46 +0300
Subject: [PATCH 22/46] fix(text): clicking on a bookmark link won't scroll
properly
---
.../src/widgets/type_widgets/text/EditableText.tsx | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx
index 760e00e489..26d021168b 100644
--- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx
+++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx
@@ -61,6 +61,17 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
onContentChange(newContent) {
contentRef.current = newContent;
watchdogRef.current?.editor?.setData(newContent);
+
+ // Scroll to bookmark anchor if navigated with ?bookmark=...
+ const viewScope = noteContext?.viewScope;
+ if (viewScope?.bookmark) {
+ requestAnimationFrame(() => {
+ const el = watchdogRef.current?.editor?.editing.view.getDomRoot()
+ ?.querySelector(`[id="${CSS.escape(viewScope.bookmark!)}"]`);
+ el?.scrollIntoView({ behavior: "smooth", block: "center" });
+ viewScope.bookmark = undefined;
+ });
+ }
},
dataSaved(savedData) {
// Store back the saved data in order to retrieve it in case the CKEditor crashes.
From 7219fc875d6622ea35cec202aa45dddaaff901d9 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:43:14 +0300
Subject: [PATCH 23/46] feat(text): improve display of reference links with
bookmarks
---
apps/client/src/services/link.ts | 15 +++++++++++++--
.../src/stylesheets/theme-next/notes/text.css | 9 +++++++++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/services/link.ts b/apps/client/src/services/link.ts
index b6c5dbfdb6..1e375a994a 100644
--- a/apps/client/src/services/link.ts
+++ b/apps/client/src/services/link.ts
@@ -434,6 +434,13 @@ async function loadReferenceLinkTitle($el: JQuery, href: string | n
const title = await getReferenceLinkTitle(href);
$el.text(title);
+ if (viewScope?.bookmark) {
+ $el.append($("").append(
+ $("").addClass("bx bx-bookmark"),
+ document.createTextNode(viewScope.bookmark)
+ ));
+ }
+
if (note) {
const icon = await getLinkIcon(noteId, viewScope.viewMode);
@@ -459,8 +466,8 @@ async function getReferenceLinkTitle(href: string) {
return attachment ? attachment.title : "[missing attachment]";
}
- return note.title;
+ return note.title;
}
function getReferenceLinkTitleSync(href: string) {
@@ -483,8 +490,12 @@ function getReferenceLinkTitleSync(href: string) {
return attachment ? attachment.title : "[missing attachment]";
}
- return note.title;
+ if (viewScope?.bookmark) {
+ return `${note.title} - ${viewScope.bookmark}`;
+ }
+
+ return note.title;
}
if (glob.device !== "print") {
diff --git a/apps/client/src/stylesheets/theme-next/notes/text.css b/apps/client/src/stylesheets/theme-next/notes/text.css
index 5e58895ef5..8832d9a9cf 100644
--- a/apps/client/src/stylesheets/theme-next/notes/text.css
+++ b/apps/client/src/stylesheets/theme-next/notes/text.css
@@ -714,6 +714,15 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
text-decoration: underline;
}
+.ck-content a.reference-link small {
+ margin-left: 0.25em;
+ opacity: 0.5;
+
+ >span {
+ font-size: 0.7em;
+ }
+}
+
/*
* Read-only text content
*/
From b01feed4a291909c656fb4affa5487542e21e572 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:45:51 +0300
Subject: [PATCH 24/46] feat(text): add bookmark title for non-mirrored link
---
apps/client/src/widgets/dialogs/add_link.tsx | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/widgets/dialogs/add_link.tsx b/apps/client/src/widgets/dialogs/add_link.tsx
index 9d1f5522ff..e61e476190 100644
--- a/apps/client/src/widgets/dialogs/add_link.tsx
+++ b/apps/client/src/widgets/dialogs/add_link.tsx
@@ -27,6 +27,7 @@ export default function AddLinkDialog() {
const [ suggestion, setSuggestion ] = useState(null);
const [ bookmarks, setBookmarks ] = useState([]);
const [ selectedBookmark, setSelectedBookmark ] = useState("");
+ const [ noteTitle, setNoteTitle ] = useState("");
const [ shown, setShown ] = useState(false);
const hasSubmittedRef = useRef(false);
@@ -44,8 +45,9 @@ export default function AddLinkDialog() {
}, [ opts ]);
async function setDefaultLinkTitle(noteId: string) {
- const noteTitle = await tree.getNoteTitle(noteId);
- setLinkTitle(noteTitle);
+ const title = await tree.getNoteTitle(noteId);
+ setNoteTitle(title);
+ setLinkTitle(title);
}
function resetExternalLink() {
@@ -79,6 +81,14 @@ export default function AddLinkDialog() {
}
}, [suggestion]);
+ useEffect(() => {
+ if (selectedBookmark) {
+ setLinkTitle(`${noteTitle} - ${selectedBookmark}`);
+ } else {
+ setLinkTitle(noteTitle);
+ }
+ }, [selectedBookmark, noteTitle]);
+
function onShown() {
const $autocompleteEl = refToJQuerySelector(autocompleteRef);
if (!opts?.text) {
@@ -136,6 +146,7 @@ export default function AddLinkDialog() {
setSuggestion(null);
setBookmarks([]);
setSelectedBookmark("");
+ setNoteTitle("");
setShown(false);
}}
show={shown}
From 15c121f95025c963ed9270a3f359a03a62f5ea92 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 10:54:57 +0300
Subject: [PATCH 25/46] docs(user): document linking to bookmarks
---
.../Navigation/Bookmarks.html | 5 ++
.../Notes/Printing & Exporting as PDF.html | 21 +++----
.../User Guide/Note Types/Text/Bookmarks.html | 60 ++++++++++++++++---
.../Developer Guide/Documentation.md | 2 +-
docs/User Guide/!!!meta.json | 42 +++++++++++++
.../Navigation/Bookmarks.md | 3 +
.../User Guide/Note Types/Text/Bookmarks.md | 25 +++++++-
7 files changed, 133 insertions(+), 25 deletions(-)
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.html
index 07943c9924..32231b4d69 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.html
@@ -1,3 +1,8 @@
+
Frequently used notes can be bookmarked, which will make them appear in
the Launch Bar for
easy access.
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html
index 95992cf3d4..721b20ed26 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF.html
@@ -61,8 +61,7 @@ class="admonition note">
- To print in landscape mode instead of portrait (useful for big diagrams
or slides), add
#printLandscape.
- - By default, the resulting PDF will be in Letter format. It is possible
+
- By default, the resulting PDF will be in Letter format. It is possible
to adjust it to another page size via the
#printPageSize attribute,
with one of the following values: A0,
- First create a collection.
- Configure it to use List View.
- - Print the collection note normally.
+ - Print the collection note normally.
The resulting collection will contain all the children of the collection,
while maintaining the hierarchy.
@@ -102,9 +100,9 @@ class="admonition note">
href="#root/_help_4TIF1oA4VQRO">Options and assigning a key combination
for:
- - Print Active Note
+
- Print Active Note
- - Export Active Note as PDF
+
- Export Active Note as PDF
Constraints & limitations
@@ -180,13 +178,12 @@ class="admonition note">
To remark:
- Multiple CSS notes can be add by using multiple
~printCss relations.
- - If the note pointing to the
printCss doesn't
+ - If the note pointing to the
printCss doesn't
have the right note type or mime type, it will be ignored.
- - If migrating from a previous version where Custom app-wide CSS, there's no need for
-
@media print { since the style-sheet is used only for printing.
+ - If migrating from a previous version where Custom app-wide CSS, there's no need for
+
@media print { since the style-sheet is used only for printing.
Under the hood
Both printing and exporting as PDF use the same mechanism: a note is rendered
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html
index bc7b346e33..25cb62942d 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html
@@ -1,5 +1,12 @@
+
Bookmarks allows creating links to a certain
- part of a note, such as referencing a particular heading.
+ part of a note, such as referencing a particular heading or section within
+ a note.
Technically, bookmarks are HTML anchors.
This feature was introduced in TriliumNext 0.94.0.
Interaction
@@ -7,13 +14,16 @@
- To create a bookmark:
- Place the cursor at the desired position where to place the bookmark.
- - Look for the
+
- Look for the
button in the Formatting toolbar,
and then press the
button.
-
+ - Alternatively, use Slash Commands and
+ look for Bookmark.
+
- To place a link to a bookmark:
@@ -23,9 +33,41 @@
-Limitations
-
- - Currently it's not possible to create a link to a bookmark from a different
- note. This functionality will be added after the internal links feature
- is enhanced to support bookmarks.
-
\ No newline at end of file
+Linking across notes
+Trilium v0.103.0 introduces cross-note bookmarks, which makes it possible
+ to create Internal (reference) links which
+ point to a specific bookmark in that document.
+To do so:
+
+ -
+
First, create a bookmark in the target note using the same process as
+ described above.
+
+ -
+
In another note, press Ctrl+L to insert an internal
+ link. Select the target note containing bookmarks.
+
+ -
+
If the target note contains bookmarks, a section will appear underneath
+ the note selector with the list of bookmarks.
+
+ -
+
Add the link normally.
+
+
+Clicking on a reference link pointing to a bookmark will automatically
+ scroll to the desired section.
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md
index 8b2fdc9998..18b7705caf 100644
--- a/docs/Developer Guide/Developer Guide/Documentation.md
+++ b/docs/Developer Guide/Developer Guide/Documentation.md
@@ -1,5 +1,5 @@
# Documentation
-There are multiple types of documentation for Trilium:
+There are multiple types of documentation for Trilium:
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json
index 92af202232..61858de2ff 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -5413,6 +5413,20 @@
"value": "bx bx-bookmarks",
"isInheritable": false,
"position": 30
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "oSuaNgyyKnhu",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "iPIMuisry3hd",
+ "isInheritable": false,
+ "position": 50
}
],
"format": "markdown",
@@ -7305,6 +7319,34 @@
"value": "bookmarks",
"isInheritable": false,
"position": 30
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "u3YFHC9tQlpm",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "xYmIYSP6wE3F",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "ZlN4nump6EbW",
+ "isInheritable": false,
+ "position": 60
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "hrZ1D00cLbal",
+ "isInheritable": false,
+ "position": 70
}
],
"format": "markdown",
diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.md b/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.md
index 332ec24172..321cf83f21 100644
--- a/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.md
+++ b/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks.md
@@ -1,4 +1,7 @@
# Bookmarks
+> [!NOTE]
+> Not to be confused with the Bookmarks concept for Text notes which acts like anchors, allowing navigation to a particular section.
+
Frequently used notes can be bookmarked, which will make them appear in the Launch Bar for easy access.
## Configuring the launch bar
diff --git a/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md b/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md
index e6321b5b89..6a699b139c 100644
--- a/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md
+++ b/docs/User Guide/User Guide/Note Types/Text/Bookmarks.md
@@ -1,5 +1,8 @@
# Bookmarks
-Bookmarks allows creating [links](Links.md) to a certain part of a note, such as referencing a particular heading.
+> [!NOTE]
+> Not to be confused with [bookmarked notes](../../Basic%20Concepts%20and%20Features/Navigation/Bookmarks.md), which simply pins a particular note to the Launch Bar for easy access.
+
+Bookmarks allows creating [links](Links.md) to a certain part of a note, such as referencing a particular heading or section within a note.
Technically, bookmarks are HTML anchors.
@@ -10,10 +13,26 @@ This feature was introduced in TriliumNext 0.94.0.
* To create a bookmark:
* Place the cursor at the desired position where to place the bookmark.
* Look for the
button in the Formatting toolbar, and then press the
button.
+ * Alternatively, use Slash Commands and look for _Bookmark_.
* To place a link to a bookmark:
* Place the cursor at the desired position of the link.
* From the [link](Links.md) pane, select the _Bookmarks_ section and select the desired bookmark.
-## Limitations
+## Linking across notes
-* Currently it's not possible to create a link to a bookmark from a different note. This functionality will be added after the internal links feature is enhanced to support bookmarks.
\ No newline at end of file
+Trilium v0.103.0 introduces cross-note bookmarks, which makes it possible to create Internal (reference) links which point to a specific bookmark in that document.
+
+To do so:
+
+1. First, create a bookmark in the target note using the same process as described above.
+2. In another note, press Ctrl+L to insert an internal link. Select the target note containing bookmarks.
+3. If the target note contains bookmarks, a section will appear underneath the note selector with the list of bookmarks.
+4. Add the link normally.
+
+Clicking on a reference link pointing to a bookmark will automatically scroll to the desired section.
+
+> [!NOTE]
+> For notes created prior to Trilium v0.103.0, you might notice that the bookmarks might not be identified:
+>
+> * To fix this, simply go that note and make any change (e.g. inserting a space), this will trigger the recalculation of the links.
+> * This limitation is intentional in order not to have to re-process all the notes, looking for anchors.
\ No newline at end of file
From 4244b66ceaccceb5515b5c6a645c43b0e85d23df Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 18 Apr 2026 11:04:20 +0300
Subject: [PATCH 26/46] feat(text): rebrand bookmarks to anchors
---
.../src/translations/en/translation.json | 4 +-
apps/client/src/widgets/dialogs/add_link.tsx | 4 +-
.../doc_notes/en/User Guide/!!!meta.json | 2 +-
.../Navigation/Bookmarks.html | 5 -
..._Bookmarks_plus.png => 1_Anchors_plus.png} | Bin
.../User Guide/Note Types/Text/Anchors.html | 62 ++++++
.../{Bookmarks_plus.png => Anchors_plus.png} | Bin
.../User Guide/Note Types/Text/Bookmarks.html | 73 -------
.../Links/Internal (reference) links.html | 7 +-
docs/User Guide/!!!meta.json | 197 ++++++++----------
.../Navigation/Bookmarks.md | 3 -
..._Bookmarks_plus.png => 1_Anchors_plus.png} | Bin
.../User Guide/Note Types/Text/Anchors.md | 36 ++++
.../{Bookmarks_plus.png => Anchors_plus.png} | Bin
.../User Guide/Note Types/Text/Bookmarks.md | 38 ----
.../Note Types/Text/Insert buttons.md | 2 +-
.../ckeditor5/src/extra_slash_commands.ts | 8 +-
.../ckeditor5/src/translation_overrides.ts | 11 +-
18 files changed, 210 insertions(+), 242 deletions(-)
rename apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/{1_Bookmarks_plus.png => 1_Anchors_plus.png} (100%)
create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Anchors.html
rename apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/{Bookmarks_plus.png => Anchors_plus.png} (100%)
delete mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Text/Bookmarks.html
rename docs/User Guide/User Guide/Note Types/Text/{1_Bookmarks_plus.png => 1_Anchors_plus.png} (100%)
create mode 100644 docs/User Guide/User Guide/Note Types/Text/Anchors.md
rename docs/User Guide/User Guide/Note Types/Text/{Bookmarks_plus.png => Anchors_plus.png} (100%)
delete mode 100644 docs/User Guide/User Guide/Note Types/Text/Bookmarks.md
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 3145ff7f03..79cf207780 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -41,8 +41,8 @@
"link_title_mirrors": "link title mirrors the note's current title",
"link_title_arbitrary": "link title can be changed arbitrarily",
"link_title": "Link title",
- "bookmark": "Bookmark (optional)",
- "bookmark_none": "None (link to note)",
+ "anchor": "Anchor (optional)",
+ "anchor_none": "None (link to note)",
"button_add_link": "Add link"
},
"branch_prefix": {
diff --git a/apps/client/src/widgets/dialogs/add_link.tsx b/apps/client/src/widgets/dialogs/add_link.tsx
index e61e476190..f68e38bfc4 100644
--- a/apps/client/src/widgets/dialogs/add_link.tsx
+++ b/apps/client/src/widgets/dialogs/add_link.tsx
@@ -163,13 +163,13 @@ export default function AddLinkDialog() {
{bookmarks.length > 0 && (
-
+