Active content problem of safety, especially when this active content
+ comes from a third-party such as if it is downloaded from a website and
+ then imported into Trilium.
+Sometimes active content can cause issues with the UI or the server, preventing
+ it from functioning properly. Safe mode allows
+ starting Trilium in such a way that active content is not loaded by default
+ at start-up, allowing the user to fix the problematic scripts or widgets.
+These are the types of active content in Trilium, along with a few examples
+ of what untrusted content of that type could cause:
+For some active content types, such as backend scripts with custom triggering
+ conditions a toggle button will appear. This makes it possible to easily
+ disable scripts or widgets, but also to re-enable them if an import was
+ made with safe mode active.
+
Since Safe import is disabled, make sure you trust the source as
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 69b0d18893..1bac949e3e 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
@@ -2,17 +2,24 @@
-
Render Note is used in Scripting .
- It works by displaying the HTML of a Code note,
- via an attribute.
+Render Note is a special case of front-end scripting which
+ allows rendering custom content inside a note. This makes it possible to
+ create custom dashboards, or to use a custom note editor.
+The content can either be a vanilla HTML, or Preact JSX.
Creating a render note
Create a Code note
- with the HTML language, with what needs to be displayed (for example
- <p>Hello world.</p>).
+ with the:
+
+ 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).
+
+
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
@@ -41,33 +48,29 @@ $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 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() {
+ As an example, use the following content:export default function() {
return (
<>
<p>Hello world.</p>
</>
);
}
-
-
- 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.
-
+
+ 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.
Refreshing the note
It's possible to refresh the note via:
Examples
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html
new file mode 100644
index 0000000000..80cbebbfeb
--- /dev/null
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html
@@ -0,0 +1,29 @@
+Unlike front-end scripts which
+ run on the client / browser-side, back-end scripts run directly on the
+ Node.js environment of the Trilium server.
+Back-end scripts can be used both on a Server Installation (where
+ it will run on the device the server is running on), or on the
+ Desktop Installation (where it will run on the PC).
+Advantages of backend scripts
+The benefit of backend scripts is that they can be pretty powerful, for
+ example to have access to the underlying system, for example it can read
+ files or execute processes.
+However, the main benefit of backend scripts is that they have easier
+ access to the notes since the information about them is already loaded
+ in memory. Whereas on the client, notes have to be manually loaded first.
+Creating a backend script
+Create a new Code note
+ and select the language JS backend .
+Running backend scripts
+Backend scripts can be either run manually (via the Execute button on
+ the script page), or they can be triggered on certain events.
+In addition, scripts can be run automatically when the server starts up,
+ on a fixed time interval or when a certain event occurs (such as an attribute
+ being modified). For more information, see the dedicated Events page.
+Script API
+Trilium exposes a set of APIs that can be directly consumed by scripts,
+ under the api object. For a reference of
+ this API, see Backend API .
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html
index d2866b0813..3e54232a5d 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html
@@ -5,139 +5,139 @@
Global events are attached to the script note via label. Simply create
e.g. "run" label with some of these values and script note will be executed
once the event occurs.
-
-
-
- Label
- Description
-
-
-
-
- run
-
-
- Defines on which events script should run. Possible values are:
-
- frontendStartup - when Trilium frontend starts up (or is refreshed),
- but not on mobile.
- mobileStartup - when Trilium frontend starts up (or is refreshed),
- on mobile.
- backendStartup - when Trilium backend starts up
- hourly - run once an hour. You can use additional label runAtHour to
- specify at which hour, on the back-end.
- daily - run once a day, on the back-end
-
-
-
-
- runOnInstance
-
- Specifies that the script should only run on a particular Trilium instance .
-
-
- runAtHour
-
- On which hour should this run. Should be used together with #run=hourly.
- Can be defined multiple times for more runs during the day.
-
-
-
-
+
+
+
+
+ Label
+ Description
+
+
+
+
+ run
+
+
+ Defines on which events script should run. Possible values are:
+
+ backendStartup - when Trilium backend starts
+ up
+ hourly - run once an hour. You can use
+ additional label runAtHour to specify at
+ which hour, on the back-end.
+ daily - run once a day, on the back-end
+
+
+
+
+ runOnInstance
+
+ Specifies that the script should only run on a particular Trilium instance .
+
+
+ runAtHour
+
+ On which hour should this run. Should be used together with #run=hourly.
+ Can be defined multiple times for more runs during the day.
+
+
+
+
Entity events
Other events are bound to some entity, these are defined as relations -
meaning that script is triggered only if note has this script attached
to it through relations (or it can inherit it).
-
-
-
- Relation
- Trigger condition
- Origin entity (see below)
-
-
-
-
- runOnNoteCreation
-
- executes when note is created on backend. Use this relation if you want
- to run the script for all notes created under a specific subtree. In that
- case, create it on the subtree root note and make it inheritable. A new
- note created within the subtree (any depth) will trigger the script.
- The BNote that got created.
-
-
- runOnChildNoteCreation
-
- executes when new note is created under the note where this relation is
- defined
- The BNote of the child that got created.
-
-
- runOnNoteTitleChange
-
- executes when note title is changed (includes note creation as well)
- The BNote of the note whose title got changed.
-
-
- runOnNoteContentChange
-
- executes when note content is changed (includes note creation as well).
- The BNote of the note whose content got
- changed.
-
-
- runOnNoteChange
-
- executes when note is changed (includes note creation as well). Does not
- include content changes
- The BNote of the note that got changed.
-
-
- runOnNoteDeletion
-
- executes when note is being deleted
- The BNote of the note that got (soft) deleted.
-
-
- runOnBranchCreation
-
- executes when a branch is created. Branch is a link between parent note
- and child note and is created e.g. when cloning or moving note.
- The BBranch that got created.
-
-
- runOnBranchChange
-
- executes when a branch is updated. (since v0.62)
- The BBranch that got changed.
-
-
- runOnBranchDeletion
-
- executes when a branch is deleted. Branch is a link between parent note
- and child note and is deleted e.g. when moving note (old branch/link is
- deleted).
- The BBranch that got (soft) deleted.
-
-
- runOnAttributeCreation
-
- executes when new attribute is created for the note which defines this
- relation
- The BAttribute that got created.
-
-
- runOnAttributeChange
-
- executes when the attribute is changed of a note which defines this relation.
- This is triggered also when the attribute is deleted
- The BAttribute that got changed.
-
-
-
-
+
+
+
+
+ Relation
+ Trigger condition
+ Origin entity (see below)
+
+
+
+
+ runOnNoteCreation
+
+ executes when note is created on backend. Use this relation if you want
+ to run the script for all notes created under a specific subtree. In that
+ case, create it on the subtree root note and make it inheritable. A new
+ note created within the subtree (any depth) will trigger the script.
+ The BNote that got created.
+
+
+ runOnChildNoteCreation
+
+ executes when new note is created under the note where this relation is
+ defined
+ The BNote of the child that got created.
+
+
+ runOnNoteTitleChange
+
+ executes when note title is changed (includes note creation as well)
+ The BNote of the note whose title got changed.
+
+
+ runOnNoteContentChange
+
+ executes when note content is changed (includes note creation as well).
+ The BNote of the note whose content got
+ changed.
+
+
+ runOnNoteChange
+
+ executes when note is changed (includes note creation as well). Does not
+ include content changes
+ The BNote of the note that got changed.
+
+
+ runOnNoteDeletion
+
+ executes when note is being deleted
+ The BNote of the note that got (soft) deleted.
+
+
+ runOnBranchCreation
+
+ executes when a branch is created. Branch is a link between parent note
+ and child note and is created e.g. when cloning or moving note.
+ The BBranch that got created.
+
+
+ runOnBranchChange
+
+ executes when a branch is updated. (since v0.62)
+ The BBranch that got changed.
+
+
+ runOnBranchDeletion
+
+ executes when a branch is deleted. Branch is a link between parent note
+ and child note and is deleted e.g. when moving note (old branch/link is
+ deleted).
+ The BBranch that got (soft) deleted.
+
+
+ runOnAttributeCreation
+
+ executes when new attribute is created for the note which defines this
+ relation
+ The BAttribute that got created.
+
+
+ runOnAttributeChange
+
+ executes when the attribute is changed of a note which defines this relation.
+ This is triggered also when the attribute is deleted
+ The BAttribute that got changed.
+
+
+
+
Origin entity
When a script is run by an event such as the ones described above,
Frontend API
-The frontend api supports two styles, regular scripts that are run with
- the current app and note context, and widgets that export an object to
- Trilium to be used in the UI. In both cases, the frontend api of Trilium
- is available to scripts running in the frontend context as global variable
- api. The members and methods of the api can be seen on the Script API page.
+Front-end scripts are custom JavaScript notes that are run on the client
+ (browser environment)
+There are four flavors of front-end scripts:
+
+
+
+
+
+
+
+
+ Regular scripts
+ These are run with the current app and note context. These can be run
+ either manually or automatically on start-up.
+
+
+ Custom Widgets
+
+ These can introduce new UI elements in various positions, such as near
+ the Note Tree ,
+ content area or even the Right Sidebar .
+
+
+ Launch Bar Widgets
+
+ Similar to Custom Widgets ,
+ but dedicated to the Launch Bar .
+ These can simply introduce new buttons or graphical elements to the bar.
+
+
+ Render Note
+
+ This allows rendering custom content inside a note, using either HTML
+ or Preact JSX.
+
+
+
+
+For more advanced behaviors that do not require a user interface (e.g.
+ batch modifying notes), see Backend scripts .
Scripts
-Scripts don't have any special requirements. They can be run at will using
- the execute button in the UI or they can be configured to run at certain
- times using Attributes on the note containing
- the script.
-Global Events
-This attribute is called #run and it can
- have any of the following values:
+Scripts don't have any special requirements. They can be run manually
+ using the Execute button on the code note or they can be run automatically;
+ to do so, set the run label to
+ either:
- frontendStartup - executes on frontend
- upon startup.
- mobileStartup - executes on mobile frontend
- upon startup.
- backendStartup - executes on backend upon
- startup.
- hourly - executes once an hour on backend.
- daily - executes once a day on backend.
-
-Entity Events
-These events are triggered by certain relations to
- other notes. Meaning that the script is triggered only if the note has
- this script attached to it through relations (or it can inherit it).
-
- runOnNoteCreation - executes when note
- is created on backend.
- runOnNoteTitleChange - executes when note
- title is changed (includes note creation as well).
- runOnNoteContentChange - executes when
- note content is changed (includes note creation as well).
- runOnNoteChange - executes when note is
- changed (includes note creation as well).
- runOnNoteDeletion - executes when note
- is being deleted.
- runOnBranchCreation - executes when a branch
- is created. Branch is a link between parent note and child note and is
- created e.g. when cloning or moving note.
- runOnBranchDeletion - executes when a branch
- is delete. Branch is a link between parent note and child note and is deleted
- e.g. when moving note (old branch/link is deleted).
- runOnChildNoteCreation - executes when
- new note is created under this note.
- runOnAttributeCreation - executes when
- new attribute is created under this note.
- runOnAttributeChange - executes when attribute
- is changed under this note.
+ frontendStartup - when Trilium frontend
+ starts up (or is refreshed), but not on mobile.
+ mobileStartup - when Trilium frontend starts
+ up (or is refreshed), on mobile.
+
+ Backend scripts have more powerful triggering conditions, for example
+ they can run automatically on a hourly or daily basis, but also on events
+ such as when a note is created or an attribute is modified. See the server-side
+ Events for more information.
+
Widgets
-Conversely to scripts, widgets do have some specific requirements in order
- to work. A widget must:
+Widgets require a certain format in order for Trilium to be able to integrate
+ them into the UI.
- Extend BasicWidget or
- one of it's subclasses.
- Create a new instance and assign it to module.exports.
- Define a parentWidget member to determine
- where it should be displayed.
- Define a position (integer) that determines
- the location via sort order.
- Have a #widget attribute on the containing
- note.
- Create, render, and return your element in the render function.
-
- For BasicWidget and
- NoteContextAwareWidget you should create this.$widget and
- render it in doRender().
- For RightPanelWidget the
- this.$widgetand doRender() are already
- handled and you should instead return the value in doRenderBody().
-
-
-
-parentWidget
-
- left-pane - This renders the widget on
- the left side of the screen where the note tree lives.
- center-pane - This renders the widget in
- the center of the layout in the same location that notes and splits appear.
- note-detail-pane - This renders the widget with the
- note in the center pane. This means it can appear multiple times with splits.
- right-pane - This renders the widget to
- the right of any opened notes.
+ For legacy widgets, the script note must export a BasicWidget or
+ a derived one (see Note context aware widget or
+ Right pane widget ).
+ For Preact widgets, a built-in helper called defineWidget needs
+ to be used.
+For more information, see Custom Widgets .
+Script API
+The front-end API of Trilium is available to all scripts running in the
+ front-end context as global variable api.
+ For a reference of the API, see Frontend API .
Tutorial
For more information on building widgets, take a look at Widget Basics .
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html
index d46ac8b84f..416c15c107 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html
@@ -20,14 +20,15 @@
Creating a custom widget
Create a Code note.
- Set the language to:
+ Set the language to:
JavaScript (frontend) for legacy widgets using jQuery.
JSX for Preact widgets. You might need to go to Options → Code to enable
the language first.
-
- Apply the #widget label .
+
+ Apply the #widget label .
Getting started with a simple example
Let's start by creating a widget that shows a message near the content
@@ -61,76 +62,81 @@ export default defineWidget({
should appear underneath the content area.
Widget location (parent widget)
A widget can be placed in one of the following sections of the applications:
-
-
-
-
-
-
-
-
-
- Value for parentWidget
-
- Description
- Sample widget
- Special requirements
-
-
-
-
- left-pane
-
- Appears within the same pane that holds the Note Tree .
- Same as above, with only a different parentWidget.
- None.
-
-
- center-pane
-
- In the content area. If a split is open, the widget will span all of the
- splits.
- See example above.
- None.
-
-
- note-detail-pane
-
-
- In the content area, inside the note detail area. If a split is open,
- the widget will be contained inside the split.
- This is ideal if the widget is note-specific.
-
- Note context aware widget
-
-
-
- The widget must export a class and not an instance of the class
- (e.g. no new) because it needs to be multiplied for each note,
- so that splits work correctly.
- Since the class is exported instead of an instance, the parentWidget getter
- must be static, otherwise the widget is ignored.
-
-
-
-
- right-pane
-
- In the Right Sidebar ,
- as a dedicated section.
- Right pane widget
-
-
-
- Although not mandatory, it's best to use a RightPanelWidget instead
- of a BasicWidget or a NoteContextAwareWidget.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Value for parentWidget
+
+ Description
+ Sample widget
+ Special requirements
+
+
+
+
+ left-pane
+
+ Appears within the same pane that holds the Note Tree .
+ Same as above, with only a different parentWidget.
+ None.
+
+
+ center-pane
+
+ In the content area. If a split is open, the widget will span all of the
+ splits.
+ See example above.
+ None.
+
+
+ note-detail-pane
+
+
+ In the content area, inside the note detail area. If a split is open,
+ the widget will be contained inside the split.
+ This is ideal if the widget is note-specific.
+
+ Note context aware widget
+
+
+
+ The widget must export a class and not an
+ instance of the class (e.g. no new) because
+ it needs to be multiplied for each note, so that splits work correctly.
+ Since the class is exported instead of an
+ instance, the parentWidget getter must be
+ static, otherwise the widget is ignored.
+
+
+
+
+ right-pane
+
+ In the Right Sidebar ,
+ as a dedicated section.
+ Right pane widget
+
+
+
+ Although not mandatory, it's best to use a RightPanelWidget instead
+ of a BasicWidget or a NoteContextAwareWidget.
+
+
+
+
+
To position the widget somewhere else, just change the value passed to
get parentWidget()for legacy widgets or the parent field
@@ -143,4 +149,13 @@ class="ck-table-resized">
to the Launch Bar .
See Launch Bar Widgets for
more information.
- Custom position
\ No newline at end of file
+ Custom position
+ The position of a custom widget is defined via a position integer.
+ In legacy widgets:
class MyWidget extends api.BasicWidget {
+ // [..
+ get position() { return 10; }
+}
+ In Preact widgets:
export default defineWidget({
+ // [...]
+ position: 10
+});
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html
index 3552ca4faa..c2b91ba4ed 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html
@@ -5,25 +5,31 @@
Unlike Custom Widgets ,
the process of setting up a launch bar widget is slightly different:
- Create a Code note of type JavaScript (front-end) .
+ Create a Code note of type JavaScript (front-end) or JSX (for Preact-based
+ widgets).
Don't set #widget, as that attribute is
reserved for Custom Widgets .
- In the Global menu ,
+ In the Global menu ,
select Configure launchbar .
- In the Visible Launchers section, select Add a custom widget .
- Give the newly created launcher a name (and optionally a name).
- In the Promoted Attributes section,
- modify the widget field to point to the newly created note.
- Refresh the UI.
+ In the Visible Launchers section, select Add a custom widget .
+ Give the newly created launcher a name (and optionally a name).
+ In the Promoted Attributes section,
+ modify the widget field to point to the newly created note.
+ Refresh the
+ UI.
\ 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 8b43e75bd5..6631466e38 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 df7c61602d..817f450e42 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -6443,6 +6443,131 @@
"dataFileName": "3_Zen mode_image.png"
}
]
+ },
+ {
+ "isClone": false,
+ "noteId": "YzMcWlCVeW09",
+ "notePath": [
+ "pOsGYCXsbNQG",
+ "gh7bpGYxajRS",
+ "YzMcWlCVeW09"
+ ],
+ "title": "Active content",
+ "notePosition": 110,
+ "prefix": null,
+ "isExpanded": false,
+ "type": "text",
+ "mime": "text/html",
+ "attributes": [
+ {
+ "type": "label",
+ "name": "iconClass",
+ "value": "bx bxs-widget",
+ "isInheritable": false,
+ "position": 30
+ },
+ {
+ "type": "label",
+ "name": "shareAlias",
+ "value": "active-content",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "MgibgPcfeuGz",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "1vHRoWCEjj0L",
+ "isInheritable": false,
+ "position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "yIhgI5H7A2Sm",
+ "isInheritable": false,
+ "position": 100
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HcABDtFCkbFN",
+ "isInheritable": false,
+ "position": 110
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "RnaPdbciOfeq",
+ "isInheritable": false,
+ "position": 120
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "SPirpZypehBG",
+ "isInheritable": false,
+ "position": 130
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "mHbBMPDPkVV5",
+ "isInheritable": false,
+ "position": 150
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "AlhDUqhENtH7",
+ "isInheritable": false,
+ "position": 160
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "pKK96zzmvBGf",
+ "isInheritable": false,
+ "position": 170
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "gOKqSJgXLcIj",
+ "isInheritable": false,
+ "position": 180
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "64ZTlUPgEPtW",
+ "isInheritable": false,
+ "position": 190
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HI6GBBIduIgv",
+ "isInheritable": false,
+ "position": 200
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "IjZS7iK5EXtb",
+ "isInheritable": false,
+ "position": 210
+ }
+ ],
+ "format": "markdown",
+ "dataFileName": "Active content.md",
+ "attachments": []
}
]
},
@@ -9884,6 +10009,13 @@
"value": "render-note",
"isInheritable": false,
"position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "yIhgI5H7A2Sm",
+ "isInheritable": false,
+ "position": 90
}
],
"format": "markdown",
@@ -16113,20 +16245,6 @@
"type": "text",
"mime": "text/markdown",
"attributes": [
- {
- "type": "relation",
- "name": "internalLink",
- "value": "GLks18SNjxmC",
- "isInheritable": false,
- "position": 10
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "zEY4DaJG4YT5",
- "isInheritable": false,
- "position": 20
- },
{
"type": "relation",
"name": "internalLink",
@@ -16147,6 +16265,90 @@
"value": "bx bx-window",
"isInheritable": false,
"position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Q2z6av6JZVWm",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "GhurYZjh8e1V",
+ "isInheritable": false,
+ "position": 60
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "M8IppdwVHSjG",
+ "isInheritable": false,
+ "position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "MgibgPcfeuGz",
+ "isInheritable": false,
+ "position": 80
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "SPirpZypehBG",
+ "isInheritable": false,
+ "position": 90
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "RnaPdbciOfeq",
+ "isInheritable": false,
+ "position": 100
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "oPVyFC7WL2Lp",
+ "isInheritable": false,
+ "position": 110
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "4Gn3psZKsfSm",
+ "isInheritable": false,
+ "position": 120
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "xYmIYSP6wE3F",
+ "isInheritable": false,
+ "position": 130
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HcABDtFCkbFN",
+ "isInheritable": false,
+ "position": 140
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HI6GBBIduIgv",
+ "isInheritable": false,
+ "position": 150
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "GPERMystNGTB",
+ "isInheritable": false,
+ "position": 160
}
],
"format": "markdown",
@@ -16791,6 +16993,13 @@
"value": "launch-bar-widgets",
"isInheritable": false,
"position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "s8alTXmpFR61",
+ "isInheritable": false,
+ "position": 70
}
],
"format": "markdown",
@@ -17248,9 +17457,52 @@
"value": "bx bx-server",
"isInheritable": false,
"position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "yIhgI5H7A2Sm",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "WOcw2SLH6tbX",
+ "isInheritable": false,
+ "position": 60
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "poXkQfguuA0U",
+ "isInheritable": false,
+ "position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "MEtfsqa5VwNi",
+ "isInheritable": false,
+ "position": 80
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "6f9hih2hXXZk",
+ "isInheritable": false,
+ "position": 90
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "GPERMystNGTB",
+ "isInheritable": false,
+ "position": 100
}
],
"format": "markdown",
+ "dataFileName": "Backend scripts.md",
"attachments": [],
"dirFileName": "Backend scripts",
"children": [
diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Active content.md b/docs/User Guide/User Guide/Basic Concepts and Features/Active content.md
new file mode 100644
index 0000000000..68aaec16f4
--- /dev/null
+++ b/docs/User Guide/User Guide/Basic Concepts and Features/Active content.md
@@ -0,0 +1,33 @@
+# Active content
+_Active content_ is a generic name for powerful features in Trilium, these range from customizing the UI to advanced scripting that can alter your notes or even your PC.
+
+## Safe import
+
+Active content problem of safety, especially when this active content comes from a third-party such as if it is downloaded from a website and then imported into Trilium.
+
+When [importing](Import%20%26%20Export.md) .zip archives into Trilium, _safe mode_ is active by default which will try to prevent untrusted code from executing. For example, a [custom widget](../Scripting/Frontend%20Basics/Custom%20Widgets.md) needs the `#widget` [label](../Advanced%20Usage/Attributes/Labels.md) in order to function; safe import works by renaming that label to `#disabled:widget`.
+
+## Safe mode
+
+Sometimes active content can cause issues with the UI or the server, preventing it from functioning properly. Safe mode allows starting Trilium in such a way that active content is not loaded by default at start-up, allowing the user to fix the problematic scripts or widgets.
+
+## Types of active content
+
+These are the types of active content in Trilium, along with a few examples of what untrusted content of that type could cause:
+
+| Name | Disabled on a safe [import](Import%20%26%20Export.md) | Description | Potential risks of untrusted code |
+| --- | --- | --- | --- |
+| [Front-end scripts](../Scripting/Frontend%20Basics.md) | Yes | Allow running arbitrary code on the client (UI) of Trilium, which can alter the user interface. | A malicious script can execute server-side code, access un-encrypted notes or change their contents. |
+| Custom Widgets | Yes | Can add new UI features to Trilium, for example by adding a new section in the Right Sidebar . | The UI can be altered in such a way that it can be used to extract sensitive information or it can simply cause the application to crash. |
+| Backend scripts | Yes | Can run custom code on the server of Trilium (Node.js environment), with full access to the notes and the database. | Has access to all the unencrypted notes, but with full access to the database it can completely destroy the data. It also has access to execute other applications or alter the files and folders on the server). |
+| Web View | Yes | Displays a website inside a note. | Can point to a phishing website which can collect the data (for example on a log in page). |
+| Render Note | Yes | Renders custom content inside a note, such as a dashboard or a new editor that is not officially supported by Trilium. | Can affect the UI similar to front-end scripts or custom widgets since the scripts are not completely encapsulated, or they can act similar to a web view where they can collect data entered by the user. |
+| Custom app-wide CSS | No | Can alter the layout and style of the UI using CSS, applied regardless of theme. | Generally less problematic than the rest of active content, but a badly written CSS can affect the layout of the application, requiring the use of Safe mode to be able to use the application. |
+| [Custom themes](../Theme%20development) | No | Can change the style of the entire UI. | Similar to custom app-wide CSS. |
+| Icon Packs | No | Introduces new icons that can be used for notes. | Generally are more contained and less prone to cause issues, but they can cause performance issues (for example if the icon pack has millions of icons in it). |
+
+## Active content badge
+
+Starting with v0.102.0, on the New Layout a badge will be displayed near the note title, indicating that an active content is detected. Clicking the badge will reveal a menu with various options related to that content type, for example to open the documentation or to configure the execution of scripts.
+
+For some active content types, such as backend scripts with custom triggering conditions a toggle button will appear. This makes it possible to easily disable scripts or widgets, but also to re-enable them if an import was made with safe mode active.
\ No newline at end of file
diff --git a/docs/User Guide/User Guide/Note Types/Render Note.md b/docs/User Guide/User Guide/Note Types/Render Note.md
index c0c4e4eeaa..60387ce507 100644
--- a/docs/User Guide/User Guide/Note Types/Render Note.md
+++ b/docs/User Guide/User Guide/Note Types/Render Note.md
@@ -1,11 +1,15 @@
# Render Note
-Render Note is used in Scripting . It works by displaying the HTML of a Code note, via an attribute.
+Render Note is a special case of [front-end scripting](../Scripting/Frontend%20Basics.md) which allows rendering custom content inside a note. This makes it possible to create custom dashboards, or to use a custom note editor.
+
+The content can either be a vanilla HTML, or Preact JSX.
## Creating a render note
-1. Create a Code note with the HTML language, with what needs to be displayed (for example `Hello world.
`).
+1. Create a Code note with the:
+ 1. HTML language for the legacy/vanilla method, with what needs to be displayed (for example `Hello world.
`).
+ 2. JSX for the Preact-based approach (see below).
2. Create a Render Note .
3. Assign the `renderNote` [relation](../Advanced%20Usage/Attributes.md) to point at the previously created code note.
@@ -44,7 +48,7 @@ Here are the steps to creating a simple render note:
2. Create a child Code note with JSX as the language.
As an example, use the following content:
- ```jsx
+ ```
export default function() {
return (
<>
diff --git a/docs/User Guide/User Guide/Scripting/Backend scripts.md b/docs/User Guide/User Guide/Scripting/Backend scripts.md
new file mode 100644
index 0000000000..7a6da5e3b7
--- /dev/null
+++ b/docs/User Guide/User Guide/Scripting/Backend scripts.md
@@ -0,0 +1,24 @@
+# Backend scripts
+Unlike [front-end scripts](Frontend%20Basics.md) which run on the client / browser-side, back-end scripts run directly on the Node.js environment of the Trilium server.
+
+Back-end scripts can be used both on a Server Installation (where it will run on the device the server is running on), or on the Desktop Installation (where it will run on the PC).
+
+## Advantages of backend scripts
+
+The benefit of backend scripts is that they can be pretty powerful, for example to have access to the underlying system, for example it can read files or execute processes.
+
+However, the main benefit of backend scripts is that they have easier access to the notes since the information about them is already loaded in memory. Whereas on the client, notes have to be manually loaded first.
+
+## Creating a backend script
+
+Create a new Code note and select the language _JS backend_.
+
+## Running backend scripts
+
+Backend scripts can be either run manually (via the Execute button on the script page), or they can be triggered on certain events.
+
+In addition, scripts can be run automatically when the server starts up, on a fixed time interval or when a certain event occurs (such as an attribute being modified). For more information, see the dedicated Events page.
+
+## Script API
+
+Trilium exposes a set of APIs that can be directly consumed by scripts, under the `api` object. For a reference of this API, see Backend API .
\ No newline at end of file
diff --git a/docs/User Guide/User Guide/Scripting/Backend scripts/Events.md b/docs/User Guide/User Guide/Scripting/Backend scripts/Events.md
index 27fedb9489..ff6f1973e4 100644
--- a/docs/User Guide/User Guide/Scripting/Backend scripts/Events.md
+++ b/docs/User Guide/User Guide/Scripting/Backend scripts/Events.md
@@ -5,7 +5,7 @@
Global events are attached to the script note via label. Simply create e.g. "run" label with some of these values and script note will be executed once the event occurs.
-Label Description runDefines on which events script should run. Possible values are:
frontendStartup - when Trilium frontend starts up (or is refreshed), but not on mobile.mobileStartup - when Trilium frontend starts up (or is refreshed), on mobile.backendStartup - when Trilium backend starts uphourly - run once an hour. You can use additional label runAtHour to specify at which hour, on the back-end.daily - run once a day, on the back-endrunOnInstanceSpecifies that the script should only run on a particular Trilium instance . runAtHourOn which hour should this run. Should be used together with #run=hourly. Can be defined multiple times for more runs during the day.
+Label Description runDefines on which events script should run. Possible values are:
backendStartup - when Trilium backend starts uphourly - run once an hour. You can use additional label runAtHour to specify at which hour, on the back-end.daily - run once a day, on the back-endrunOnInstanceSpecifies that the script should only run on a particular Trilium instance . runAtHourOn which hour should this run. Should be used together with #run=hourly. Can be defined multiple times for more runs during the day.
## Entity events
diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics.md b/docs/User Guide/User Guide/Scripting/Frontend Basics.md
index 13c47823dc..52118eba5a 100644
--- a/docs/User Guide/User Guide/Scripting/Frontend Basics.md
+++ b/docs/User Guide/User Guide/Scripting/Frontend Basics.md
@@ -1,56 +1,39 @@
# Frontend Basics
-## Frontend API
+Front-end scripts are custom JavaScript notes that are run on the client (browser environment)
-The frontend api supports two styles, regular scripts that are run with the current app and note context, and widgets that export an object to Trilium to be used in the UI. In both cases, the frontend api of Trilium is available to scripts running in the frontend context as global variable `api`. The members and methods of the api can be seen on the [Script API](Script%20API.md) page.
+There are four flavors of front-end scripts:
+
+| | |
+| --- | --- |
+| Regular scripts | These are run with the current app and note context. These can be run either manually or automatically on start-up. |
+| Custom Widgets | These can introduce new UI elements in various positions, such as near the Note Tree , content area or even the Right Sidebar . |
+| Launch Bar Widgets | Similar to Custom Widgets , but dedicated to the Launch Bar . These can simply introduce new buttons or graphical elements to the bar. |
+| Render Note | This allows rendering custom content inside a note, using either HTML or Preact JSX. |
+
+For more advanced behaviors that do not require a user interface (e.g. batch modifying notes), see Backend scripts .
## Scripts
-Scripts don't have any special requirements. They can be run at will using the execute button in the UI or they can be configured to run at certain times using [Attributes](../Advanced%20Usage/Attributes.md) on the note containing the script.
+Scripts don't have any special requirements. They can be run manually using the _Execute_ button on the code note or they can be run automatically; to do so, set the `run` [label](../Advanced%20Usage/Attributes/Labels.md) to either:
-### Global Events
+* `frontendStartup` - when Trilium frontend starts up (or is refreshed), but not on mobile.
+* `mobileStartup` - when Trilium frontend starts up (or is refreshed), on mobile.
-This attribute is called `#run` and it can have any of the following values:
-
-* `frontendStartup` - executes on frontend upon startup.
-* `mobileStartup` - executes on mobile frontend upon startup.
-* `backendStartup` - executes on backend upon startup.
-* `hourly` - executes once an hour on backend.
-* `daily` - executes once a day on backend.
-
-### Entity Events
-
-These events are triggered by certain [relations](../Advanced%20Usage/Attributes.md) to other notes. Meaning that the script is triggered only if the note has this script attached to it through relations (or it can inherit it).
-
-* `runOnNoteCreation` - executes when note is created on backend.
-* `runOnNoteTitleChange` - executes when note title is changed (includes note creation as well).
-* `runOnNoteContentChange` - executes when note content is changed (includes note creation as well).
-* `runOnNoteChange` - executes when note is changed (includes note creation as well).
-* `runOnNoteDeletion` - executes when note is being deleted.
-* `runOnBranchCreation` - executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.
-* `runOnBranchDeletion` - executes when a branch is delete. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).
-* `runOnChildNoteCreation` - executes when new note is created under this note.
-* `runOnAttributeCreation` - executes when new attribute is created under this note.
-* `runOnAttributeChange` - executes when attribute is changed under this note.
+> [!NOTE]
+> Backend scripts have more powerful triggering conditions, for example they can run automatically on a hourly or daily basis, but also on events such as when a note is created or an attribute is modified. See the server-side Events for more information.
## Widgets
-Conversely to scripts, widgets do have some specific requirements in order to work. A widget must:
+Widgets require a certain format in order for Trilium to be able to integrate them into the UI.
-* Extend [BasicWidget](https://triliumnext.github.io/Notes/frontend_api/BasicWidget.html) or one of it's subclasses.
-* Create a new instance and assign it to `module.exports`.
-* Define a `parentWidget` member to determine where it should be displayed.
-* Define a `position` (integer) that determines the location via sort order.
-* Have a `#widget` attribute on the containing note.
-* Create, render, and return your element in the render function.
- * For [BasicWidget](https://triliumnext.github.io/Notes/frontend_api/BasicWidget.html) and [NoteContextAwareWidget](https://triliumnext.github.io/Notes/frontend_api/NoteContextAwareWidget.html)you should create `this.$widget` and render it in `doRender()`.
- * For [RightPanelWidget](https://triliumnext.github.io/Notes/frontend_api/RightPanelWidget.html) the `this.$widget` and `doRender()` are already handled and you should instead return the value in `doRenderBody()`.
+* For legacy widgets, the script note must export a `BasicWidget` or a derived one (see Note context aware widget or Right pane widget ).
+* For Preact widgets, a built-in helper called `defineWidget` needs to be used.
-### parentWidget
+For more information, see Custom Widgets .
-* `left-pane` - This renders the widget on the left side of the screen where the note tree lives.
-* `center-pane` - This renders the widget in the center of the layout in the same location that notes and splits appear.
-* `note-detail-pane` - This renders the widget _with_ the note in the center pane. This means it can appear multiple times with splits.
-* `right-pane` - This renders the widget to the right of any opened notes.
+## Script API
+
+The front-end API of Trilium is available to all scripts running in the front-end context as global variable `api`. For a reference of the API, see Frontend API .
### Tutorial
diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md
index dec4cfd3fe..9963e37c1b 100644
--- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md
+++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md
@@ -63,7 +63,7 @@ export default defineWidget({
A widget can be placed in one of the following sections of the applications:
-Value for parentWidget Description Sample widget Special requirements left-paneAppears within the same pane that holds the Note Tree . Same as above, with only a different parentWidget. None. center-paneIn the content area. If a split is open, the widget will span all of the splits. See example above. None. note-detail-paneIn the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.
This is ideal if the widget is note-specific.
Note context aware widget The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly. Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored. right-paneIn the Right Sidebar , as a dedicated section. Right pane widget Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
+Value for parentWidget Description Sample widget Special requirements left-paneAppears within the same pane that holds the Note Tree . Same as above, with only a different parentWidget. None. center-paneIn the content area. If a split is open, the widget will span all of the splits. See example above. None. note-detail-paneIn the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.
This is ideal if the widget is note-specific.
Note context aware widget The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly. Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored. right-paneIn the Right Sidebar , as a dedicated section. Right pane widget Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
To position the widget somewhere else, just change the value passed to `get parentWidget()` for legacy widgets or the `parent` field for Preact. Do note that some positions such as `note-detail-pane` and `right-pane` have special requirements that need to be accounted for (see the table above).
@@ -71,4 +71,24 @@ To position the widget somewhere else, just change the value passed to `get pare
Launch bar widgets are similar to _Custom widgets_ but are specific to the Launch Bar . See Launch Bar Widgets for more information.
-## Custom position
\ No newline at end of file
+## Custom position
+
+The position of a custom widget is defined via a `position` integer.
+
+In legacy widgets:
+
+```
+class MyWidget extends api.BasicWidget {
+ // [..
+ get position() { return 10; }
+}
+```
+
+In Preact widgets:
+
+```
+export default defineWidget({
+ // [...]
+ position: 10
+});
+```
\ No newline at end of file
diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.md
index 7f9bef7a4d..e5d25b0198 100644
--- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.md
+++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.md
@@ -5,12 +5,12 @@ Launch bar widgets are a subset of Custom Widgets , the process of setting up a launch bar widget is slightly different:
-1. Create a Code note of type _JavaScript (front-end)_.
+1. Create a Code note of type _JavaScript (front-end)_ or JSX (for Preact-based widgets).
* The script itself uses the same concepts as Custom Widgets , including the use of a `NoteContextAwareWidget` or a `BasicWidget` (according to needs).
- * As examples, see Note Title Widget and Analog Watch .
+ * As examples in both legacy and Preact format, see Note Title Widget and Analog Watch .
2. Don't set `#widget`, as that attribute is reserved for Custom Widgets .
3. In the Global menu , select _Configure launchbar_.
4. In the _Visible Launchers_ section, select _Add a custom widget_.
5. Give the newly created launcher a name (and optionally a name).
6. In the Promoted Attributes section, modify the _widget_ field to point to the newly created note.
-7. Refresh the UI.
\ No newline at end of file
+7. [Refresh](../../Troubleshooting/Refreshing%20the%20application.md) the UI.
\ No newline at end of file
From b72b82ff1ae41d4eeec8d54a24742ed479f2d2b4 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 12:37:37 +0200
Subject: [PATCH 054/247] feat(badges/content): add badge for app themes
---
apps/client/src/translations/en/translation.json | 1 +
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 10 ++++++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ea5fda411a..d65d2b6bce 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2297,6 +2297,7 @@
"type_app_css": "Custom CSS",
"type_render_note": "Render note",
"type_web_view": "Web view",
+ "type_app_theme": "Custom theme",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}.",
"menu_docs": "Open documentation",
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 80b37ec291..1987985cdb 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -12,10 +12,10 @@ import FormToggle from "../react/FormToggle";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || a.name === "appCss");
-const activeContentLabels = [ "iconPack", "widget", "appCss" ] as const;
+const activeContentLabels = [ "iconPack", "widget", "appCss", "appTheme" ] as const;
interface ActiveContentInfo {
- type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss" | "renderNote" | "webView";
+ type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss" | "renderNote" | "webView" | "appTheme";
isEnabled: boolean;
canToggleEnabled: boolean;
}
@@ -57,6 +57,10 @@ const typeMappings: Record
Date: Sun, 15 Feb 2026 12:42:46 +0200
Subject: [PATCH 055/247] feat(badges/content): make app theme toggleable
---
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 1987985cdb..26360afaea 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -11,7 +11,8 @@ import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react
import FormToggle from "../react/FormToggle";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
-const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || a.name === "appCss");
+const NON_DANGEROUS_ACTIVE_CONTENT = [ "appCss", "appTheme" ];
+const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || NON_DANGEROUS_ACTIVE_CONTENT.includes(a.name));
const activeContentLabels = [ "iconPack", "widget", "appCss", "appTheme" ] as const;
interface ActiveContentInfo {
@@ -24,7 +25,7 @@ const typeMappings: Record = {
iconPack: {
icon: "bx bx-package",
@@ -281,9 +282,12 @@ function useActiveContentInfo(note: FNote | null | undefined) {
type = "frontendScript";
isEnabled = note.hasLabel("widget") || note.hasLabel("run");
canToggleEnabled = note.hasLabelOrDisabled("widget") || note.hasLabelOrDisabled("run");
+ } else if (note.type === "code" && note.hasLabelOrDisabled("appTheme")) {
+ isEnabled = note.hasLabel("appTheme");
+ canToggleEnabled = true;
}
- for (const labelToCheck of activeContentLabels ) {
+ for (const labelToCheck of activeContentLabels) {
if (note.hasLabel(labelToCheck)) {
type = labelToCheck;
break;
From 29855112c85047b33faa4d8c1309abe5e4821d23 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:13:18 +0200
Subject: [PATCH 056/247] refactor(client): move note properties mapping
outside collection properties
---
.../note_bars/CollectionProperties.tsx | 136 +-----------
.../src/widgets/react/NotePropertyMenu.tsx | 194 ++++++++++++++++++
.../ribbon/collection-properties-config.tsx | 74 +------
3 files changed, 206 insertions(+), 198 deletions(-)
create mode 100644 apps/client/src/widgets/react/NotePropertyMenu.tsx
diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx
index 5dba675e6d..597769a36b 100644
--- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx
+++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx
@@ -2,18 +2,16 @@ import "./CollectionProperties.css";
import { t } from "i18next";
import { ComponentChildren } from "preact";
-import { useContext, useRef } from "preact/hooks";
-import { Fragment } from "preact/jsx-runtime";
+import { useRef } from "preact/hooks";
import FNote from "../../entities/fnote";
import { ViewTypeOptions } from "../collections/interface";
import Dropdown from "../react/Dropdown";
-import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
-import FormTextBox from "../react/FormTextBox";
-import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault, useNoteProperty, useTriliumEvent } from "../react/hooks";
+import { FormDropdownDivider, FormListItem } from "../react/FormList";
+import { useNoteProperty, useTriliumEvent } from "../react/hooks";
import Icon from "../react/Icon";
-import { ParentComponent } from "../react/react_utils";
-import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../ribbon/collection-properties-config";
+import { CheckBoxProperty, ViewProperty } from "../react/NotePropertyMenu";
+import { bookPropertiesConfig } from "../ribbon/collection-properties-config";
import { useViewType, VIEW_TYPE_MAPPINGS } from "../ribbon/CollectionPropertiesTab";
export const ICON_MAPPINGS: Record = {
@@ -107,127 +105,3 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption
);
}
-
-function ViewProperty({ note, property }: { note: FNote, property: BookProperty }) {
- switch (property.type) {
- case "button":
- return ;
- case "split-button":
- return ;
- case "checkbox":
- return ;
- case "number":
- return ;
- case "combobox":
- return ;
- }
-}
-
-function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonProperty }) {
- const parentComponent = useContext(ParentComponent);
-
- return (
- {
- if (!parentComponent) return;
- property.onClick({
- note,
- triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
- });
- }}
- >{property.label}
- );
-}
-
-function SplitButtonPropertyView({ note, property }: { note: FNote, property: SplitButtonProperty }) {
- const parentComponent = useContext(ParentComponent);
- const ItemsComponent = property.items;
- const clickContext = parentComponent && {
- note,
- triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
- };
-
- return (parentComponent &&
- clickContext && property.onClick(clickContext)}
- >
-
-
- );
-}
-
-function NumberPropertyView({ note, property }: { note: FNote, property: NumberProperty }) {
- //@ts-expect-error Interop with text box which takes in string values even for numbers.
- const [ value, setValue ] = useNoteLabel(note, property.bindToLabel);
- const disabled = property.disabled?.(note);
-
- return (
- e.stopPropagation()}
- >
- {property.label}
-
-
- );
-}
-
-function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) {
- const [ value, setValue ] = useNoteLabelWithDefault(note, property.bindToLabel, property.defaultValue ?? "");
-
- function renderItem(option: ComboBoxItem) {
- return (
- setValue(option.value)}
- >
- {option.label}
-
- );
- }
-
- return (
-
- {(property.options).map((option, index) => {
- if ("items" in option) {
- return (
-
- {option.title}
- {option.items.map(renderItem)}
- {index < property.options.length - 1 && }
-
- );
- }
- return renderItem(option);
-
- })}
-
- );
-}
-
-function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) {
- const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel);
- return (
-
- );
-}
diff --git a/apps/client/src/widgets/react/NotePropertyMenu.tsx b/apps/client/src/widgets/react/NotePropertyMenu.tsx
new file mode 100644
index 0000000000..3c01f7e192
--- /dev/null
+++ b/apps/client/src/widgets/react/NotePropertyMenu.tsx
@@ -0,0 +1,194 @@
+import { FilterLabelsByType } from "@triliumnext/commons";
+import { Fragment, VNode } from "preact";
+import { useContext } from "preact/hooks";
+
+import Component from "../../components/component";
+import FNote from "../../entities/fnote";
+import NoteContextAwareWidget from "../note_context_aware_widget";
+import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "./FormList";
+import FormTextBox from "./FormTextBox";
+import { useNoteLabelBoolean, useNoteLabelWithDefault } from "./hooks";
+import { ParentComponent } from "./react_utils";
+
+export interface ClickContext {
+ note: FNote;
+ triggerCommand: NoteContextAwareWidget["triggerCommand"];
+}
+
+export interface CheckBoxProperty {
+ type: "checkbox",
+ label: string;
+ bindToLabel: FilterLabelsByType;
+ icon?: string;
+}
+
+export interface ButtonProperty {
+ type: "button",
+ label: string;
+ title?: string;
+ icon?: string;
+ onClick(context: ClickContext): void;
+}
+
+export interface SplitButtonProperty extends Omit {
+ type: "split-button";
+ items({ note, parentComponent }: { note: FNote, parentComponent: Component }): VNode;
+}
+
+export interface NumberProperty {
+ type: "number",
+ label: string;
+ bindToLabel: FilterLabelsByType;
+ width?: number;
+ min?: number;
+ icon?: string;
+ disabled?: (note: FNote) => boolean;
+}
+
+export interface ComboBoxItem {
+ value: string;
+ label: string;
+}
+
+interface ComboBoxGroup {
+ title: string;
+ items: ComboBoxItem[];
+}
+
+export interface ComboBoxProperty {
+ type: "combobox",
+ label: string;
+ icon?: string;
+ bindToLabel: FilterLabelsByType;
+ /**
+ * The default value is used when the label is not set.
+ */
+ defaultValue?: string;
+ options: (ComboBoxItem | ComboBoxGroup)[];
+}
+
+export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty | SplitButtonProperty;
+
+export function ViewProperty({ note, property }: { note: FNote, property: BookProperty }) {
+ switch (property.type) {
+ case "button":
+ return ;
+ case "split-button":
+ return ;
+ case "checkbox":
+ return ;
+ case "number":
+ return ;
+ case "combobox":
+ return ;
+ }
+}
+
+function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonProperty }) {
+ const parentComponent = useContext(ParentComponent);
+
+ return (
+ {
+ if (!parentComponent) return;
+ property.onClick({
+ note,
+ triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
+ });
+ }}
+ >{property.label}
+ );
+}
+
+function SplitButtonPropertyView({ note, property }: { note: FNote, property: SplitButtonProperty }) {
+ const parentComponent = useContext(ParentComponent);
+ const ItemsComponent = property.items;
+ const clickContext = parentComponent && {
+ note,
+ triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
+ };
+
+ return (parentComponent &&
+ clickContext && property.onClick(clickContext)}
+ >
+
+
+ );
+}
+
+function NumberPropertyView({ note, property }: { note: FNote, property: NumberProperty }) {
+ //@ts-expect-error Interop with text box which takes in string values even for numbers.
+ const [ value, setValue ] = useNoteLabel(note, property.bindToLabel);
+ const disabled = property.disabled?.(note);
+
+ return (
+ e.stopPropagation()}
+ >
+ {property.label}
+
+
+ );
+}
+
+function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) {
+ const [ value, setValue ] = useNoteLabelWithDefault(note, property.bindToLabel, property.defaultValue ?? "");
+
+ function renderItem(option: ComboBoxItem) {
+ return (
+ setValue(option.value)}
+ >
+ {option.label}
+
+ );
+ }
+
+ return (
+
+ {(property.options).map((option, index) => {
+ if ("items" in option) {
+ return (
+
+ {option.title}
+ {option.items.map(renderItem)}
+ {index < property.options.length - 1 && }
+
+ );
+ }
+ return renderItem(option);
+
+ })}
+
+ );
+}
+
+function CheckBoxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) {
+ const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel);
+ return (
+
+ );
+}
diff --git a/apps/client/src/widgets/ribbon/collection-properties-config.tsx b/apps/client/src/widgets/ribbon/collection-properties-config.tsx
index 1f79217e9c..2d6c051e91 100644
--- a/apps/client/src/widgets/ribbon/collection-properties-config.tsx
+++ b/apps/client/src/widgets/ribbon/collection-properties-config.tsx
@@ -1,79 +1,19 @@
import { t } from "i18next";
+
+import Component from "../../components/component";
import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
-import NoteContextAwareWidget from "../note_context_aware_widget";
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, type MapLayer } from "../collections/geomap/map_layer";
import { ViewTypeOptions } from "../collections/interface";
-import { FilterLabelsByType } from "@triliumnext/commons";
import { DEFAULT_THEME, getPresentationThemes } from "../collections/presentation/themes";
-import { VNode } from "preact";
-import { useNoteLabel } from "../react/hooks";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
-import Component from "../../components/component";
+import { useNoteLabel } from "../react/hooks";
+import { BookProperty, ClickContext, ComboBoxItem } from "../react/NotePropertyMenu";
interface BookConfig {
properties: BookProperty[];
}
-export interface CheckBoxProperty {
- type: "checkbox",
- label: string;
- bindToLabel: FilterLabelsByType;
- icon?: string;
-}
-
-export interface ButtonProperty {
- type: "button",
- label: string;
- title?: string;
- icon?: string;
- onClick(context: BookContext): void;
-}
-
-export interface SplitButtonProperty extends Omit {
- type: "split-button";
- items({ note, parentComponent }: { note: FNote, parentComponent: Component }): VNode;
-}
-
-export interface NumberProperty {
- type: "number",
- label: string;
- bindToLabel: FilterLabelsByType;
- width?: number;
- min?: number;
- icon?: string;
- disabled?: (note: FNote) => boolean;
-}
-
-export interface ComboBoxItem {
- value: string;
- label: string;
-}
-
-interface ComboBoxGroup {
- title: string;
- items: ComboBoxItem[];
-}
-
-export interface ComboBoxProperty {
- type: "combobox",
- label: string;
- icon?: string;
- bindToLabel: FilterLabelsByType;
- /**
- * The default value is used when the label is not set.
- */
- defaultValue?: string;
- options: (ComboBoxItem | ComboBoxGroup)[];
-}
-
-export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty | SplitButtonProperty;
-
-interface BookContext {
- note: FNote;
- triggerCommand: NoteContextAwareWidget["triggerCommand"];
-}
-
export const bookPropertiesConfig: Record = {
grid: {
properties: []
@@ -211,7 +151,7 @@ function ListExpandDepth(context: { note: FNote, parentComponent: Component }) {
>
- )
+ );
}
function ListExpandDepthButton({ label, depth, note, parentComponent, checked }: { label: string, depth: number | "all", note: FNote, parentComponent: Component, checked?: boolean }) {
@@ -226,7 +166,7 @@ function ListExpandDepthButton({ label, depth, note, parentComponent, checked }:
}
function buildExpandListHandler(depth: number | "all") {
- return async ({ note, triggerCommand }: BookContext) => {
+ return async ({ note, triggerCommand }: ClickContext) => {
const { noteId } = note;
const existingValue = note.getLabelValue("expanded");
@@ -236,5 +176,5 @@ function buildExpandListHandler(depth: number | "all") {
await attributes.setLabel(noteId, "expanded", newValue);
triggerCommand("refreshNoteList", { noteId });
- }
+ };
}
From 1e5b294eb3997542a80f0a9bdfc70e843f735bed Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:19:12 +0200
Subject: [PATCH 057/247] feat(badges/content): adjustable base for app theme
---
.../widgets/layout/ActiveContentBadges.tsx | 30 +++++++++++++++++--
.../src/widgets/react/NotePropertyMenu.tsx | 2 ++
packages/commons/src/lib/attribute_names.ts | 3 ++
3 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 26360afaea..689854237d 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -10,6 +10,7 @@ import { BadgeWithDropdown } from "../react/Badge";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
+import { BookProperty, ViewProperty } from "../react/NotePropertyMenu";
const NON_DANGEROUS_ACTIVE_CONTENT = [ "appCss", "appTheme" ];
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || NON_DANGEROUS_ACTIVE_CONTENT.includes(a.name));
@@ -26,6 +27,7 @@ const typeMappings: Record = {
iconPack: {
icon: "bx bx-package",
@@ -61,7 +63,22 @@ const typeMappings: Record
)}
+ {additionalOptions?.length && (
+ <>
+ {additionalOptions?.map((property, i) => (
+
+ ))}
+
+ >
+ )}
+
openInAppHelpFromUrl(helpPage)}
diff --git a/apps/client/src/widgets/react/NotePropertyMenu.tsx b/apps/client/src/widgets/react/NotePropertyMenu.tsx
index 3c01f7e192..15f3723943 100644
--- a/apps/client/src/widgets/react/NotePropertyMenu.tsx
+++ b/apps/client/src/widgets/react/NotePropertyMenu.tsx
@@ -65,6 +65,7 @@ export interface ComboBoxProperty {
*/
defaultValue?: string;
options: (ComboBoxItem | ComboBoxGroup)[];
+ dropStart?: boolean;
}
export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty | SplitButtonProperty;
@@ -163,6 +164,7 @@ function ComboBoxPropertyView({ note, property }: { note: FNote, property: Combo
{(property.options).map((option, index) => {
if ("items" in option) {
diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts
index 31997489ce..da3b890c9c 100644
--- a/packages/commons/src/lib/attribute_names.ts
+++ b/packages/commons/src/lib/attribute_names.ts
@@ -64,6 +64,9 @@ type Labels = {
readOnly: boolean;
mapType: string;
mapRootNoteId: string;
+
+ appTheme: string;
+ appThemeBase: string;
}
/**
From 43ebbfc3217a235fe92777c249595bdbcdd9f2c0 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:21:29 +0200
Subject: [PATCH 058/247] chore(badges/content): add separator
---
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 1 +
apps/client/src/widgets/react/NotePropertyMenu.tsx | 10 +++++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 689854237d..162607b194 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -73,6 +73,7 @@ const typeMappings: Record
);
}
+ if ("type" in option) {
+ return ;
+ }
+
return renderItem(option);
})}
From 33ea2de2310529f7c6ea1c27a1d0d505c4518104 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:23:36 +0200
Subject: [PATCH 059/247] chore(badges/content): use translations
---
apps/client/src/translations/en/translation.json | 3 ++-
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 10 +++++-----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index d65d2b6bce..3656035497 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2310,6 +2310,7 @@
"menu_run_frontend_startup": "When the desktop frontend starts up",
"menu_run_mobile_startup": "When the mobile frontend starts up",
"menu_change_to_widget": "Change to widget",
- "menu_change_to_frontend_script": "Change to frontend script"
+ "menu_change_to_frontend_script": "Change to frontend script",
+ "menu_theme_base": "Theme base"
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 162607b194..870533ba58 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -68,15 +68,15 @@ const typeMappings: Record
Date: Sun, 15 Feb 2026 13:26:23 +0200
Subject: [PATCH 060/247] chore(badges/content): remove the base attribute for
legacy
---
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 2 +-
apps/client/src/widgets/react/NotePropertyMenu.tsx | 5 ++++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 870533ba58..781f827553 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -72,7 +72,7 @@ const typeMappings: Record
Date: Sun, 15 Feb 2026 13:31:45 +0200
Subject: [PATCH 061/247] style/list view: tweak
---
.../src/stylesheets/theme-next-dark.css | 9 +
.../src/stylesheets/theme-next-light.css | 9 +
.../src/stylesheets/theme-next/shell.css | 6 +-
.../collections/legacy/ListOrGridView.css | 208 +++++++++---------
4 files changed, 131 insertions(+), 101 deletions(-)
diff --git a/apps/client/src/stylesheets/theme-next-dark.css b/apps/client/src/stylesheets/theme-next-dark.css
index f8fb305726..e74180a230 100644
--- a/apps/client/src/stylesheets/theme-next-dark.css
+++ b/apps/client/src/stylesheets/theme-next-dark.css
@@ -290,6 +290,15 @@
--ck-editor-toolbar-button-on-shadow: 1px 1px 2px rgba(0, 0, 0, .75);
--ck-editor-toolbar-dropdown-button-open-background: #ffffff14;
+ --note-list-view-icon-color: var(--left-pane-icon-color);
+ --note-list-view-large-icon-background: var(--note-icon-background-color);
+ --note-list-view-large-icon-color: var(--note-icon-color);
+ --note-list-view-search-result-highlight-background: transparent;
+ --note-list-view-search-result-highlight-color: var(--quick-search-result-highlight-color);
+ --note-list-view-content-background: rgba(0, 0, 0, .2);
+ --note-list-view-content-search-result-highlight-background: var(--quick-search-result-highlight-color);
+ --note-list-view-content-search-result-highlight-color: white;
+
--calendar-coll-event-background-saturation: 25%;
--calendar-coll-event-background-lightness: 20%;
--calendar-coll-event-background-color: #3c3c3c;
diff --git a/apps/client/src/stylesheets/theme-next-light.css b/apps/client/src/stylesheets/theme-next-light.css
index 2d7862ae00..70883f2368 100644
--- a/apps/client/src/stylesheets/theme-next-light.css
+++ b/apps/client/src/stylesheets/theme-next-light.css
@@ -288,6 +288,15 @@
--ck-editor-toolbar-button-on-shadow: none;
--ck-editor-toolbar-dropdown-button-open-background: #0000000f;
+ --note-list-view-icon-color: var(--left-pane-icon-color);
+ --note-list-view-large-icon-background: var(--note-icon-background-color);
+ --note-list-view-large-icon-color: var(--note-icon-color);
+ --note-list-view-search-result-highlight-background: transparent;
+ --note-list-view-search-result-highlight-color: var(--quick-search-result-highlight-color);
+ --note-list-view-content-background: #b1b1b133;
+ --note-list-view-content-search-result-highlight-background: var(--quick-search-result-highlight-color);
+ --note-list-view-content-search-result-highlight-color: white;
+
--calendar-coll-event-background-lightness: 95%;
--calendar-coll-event-background-saturation: 80%;
--calendar-coll-event-background-color: #eaeaea;
diff --git a/apps/client/src/stylesheets/theme-next/shell.css b/apps/client/src/stylesheets/theme-next/shell.css
index 0df9d6686a..1ac2f13e77 100644
--- a/apps/client/src/stylesheets/theme-next/shell.css
+++ b/apps/client/src/stylesheets/theme-next/shell.css
@@ -751,12 +751,14 @@ body[dir=rtl] #left-pane span.fancytree-node.protected > span.fancytree-custom-i
}
}
-#left-pane .fancytree-expander {
+#left-pane .fancytree-expander,
+.nested-note-list-item .note-expander {
opacity: 0.65;
transition: opacity 150ms ease-in;
}
-#left-pane .fancytree-expander:hover {
+#left-pane .fancytree-expander:hover,
+.nested-note-list-item .note-expander:hover {
opacity: 1;
transition: opacity 300ms ease-out;
}
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index e1d55a8680..7a09e177f8 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -104,6 +104,8 @@
text-align: center;
}
+/* #region List view */
+
@keyframes note-preview-show {
from {
opacity: 0;
@@ -112,108 +114,57 @@
}
}
-.nested-note-list {
+:root .nested-note-list {
--card-nested-section-indent: 40px;
+}
- h5 {
- display: flex;
- align-items: center;
- font-size: 1em;
- font-weight: normal;
- margin: 0;
+/* List item */
+.nested-note-list-item h5 {
+ display: flex;
+ align-items: center;
+ font-size: 1em;
+ font-weight: normal;
+ margin: 0;
- .note-expander {
- font-size: x-large;
- cursor: pointer;
- opacity: .65;
- margin-inline-end: 4px;
- }
-
- .tn-icon {
- color: var(--left-pane-icon-color);
- margin-inline-end: 8px;
- font-size: 1.2em;
- }
-
- .note-book-title {
- color: inherit;
- font-weight: normal;
- }
-
- .note-path {
- margin-left: 0.5em;
- vertical-align: middle;
- opacity: 0.5;
- order: -1;
- }
-
- .note-list-attributes {
- flex-grow: 1;
- text-align: right;
- font-size: .75em;
- opacity: .75;
- }
-
- .nested-note-list-item-menu {
- margin-inline-start: 8px;
- }
+ .note-expander {
+ margin-inline-end: 4px;
+ font-size: x-large;
+ cursor: pointer;
}
- .note-book-content {
- display: none;
- --background: rgba(0, 0, 0, .2);
- outline: 1px solid var(--background);
- border-radius: 8px;
- background-color: var(--background);
- overflow: hidden;
- user-select: text;
- animation: note-preview-show .25s ease-out;
- will-change: opacity;
+ .tn-icon {
+ margin-inline-end: 8px;
+ color: var(--note-list-view-icon-color);
+ font-size: 1.2em;
+ }
- &.note-book-content-ready {
- display: block;
- }
+ .note-book-title {
+ --link-hover-background: transparent;
+ --link-hover-color: currentColor;
+ color: inherit;
+ font-weight: normal;
+ }
- > .rendered-content > *:last-child {
- margin-bottom: 0;
- }
+ .note-path {
+ margin-left: 0.5em;
+ vertical-align: middle;
+ opacity: 0.5;
+ }
- &.type-text {
- padding: 8px 24px;
+ .note-list-attributes {
+ flex-grow: 1;
+ text-align: right;
+ font-size: .75em;
+ opacity: .75;
+ }
- .ck-content > *:last-child {
- margin-bottom: 0;
- }
- }
-
- &.type-protectedSession {
- padding: 20px;
- }
-
- &.type-image {
- padding: 0;
- }
-
- &.type-pdf {
- iframe {
- height: 50vh;
- }
-
- .file-footer {
- padding: 8px;
- }
- }
-
- &.type-webView {
- display: flex;
- flex-direction: column;
- justify-content: center;
- min-height: 50vh;
- }
+ .nested-note-list-item-menu {
+ margin-inline-start: 8px;
}
}
-.nested-note-list.search-results {
+/* List item (search results view) */
+.nested-note-list.search-results .nested-note-list-item {
span.tn-icon + span > span {
display: flex;
flex-direction: column-reverse;
@@ -237,23 +188,80 @@
flex-shrink: 0;
justify-content: center;
align-items: center;
- margin-inline-end: 12px;
- background: rgb(88, 88, 88);
- border-radius: 50%;
width: 1.75em;
height: 1.75em;
+ margin-inline-end: 12px;
+ border-radius: 50%;
+ background: var(--note-list-view-large-icon-background);
font-size: 1.2em;
+ color: var(--note-list-view-large-icon-color);
}
- .note-book-title {
- --link-hover-background: transparent;
+ h5 .ck-find-result {
+ background: var(--note-list-view-search-result-highlight-background);
+ color: var(--note-list-view-search-result-highlight-color);
+ font-weight: 600;
+ text-decoration: underline;
+ }
+}
+
+/* Note content preview */
+.nested-note-list .note-book-content {
+ display: none;
+ outline: 1px solid var(--note-list-view-content-background);
+ border-radius: 8px;
+ background-color: var(--note-list-view-content-background);
+ overflow: hidden;
+ user-select: text;
+ animation: note-preview-show .25s ease-out;
+ will-change: opacity;
+
+ &.note-book-content-ready {
+ display: block;
+ }
+
+ > .rendered-content > *:last-child {
+ margin-bottom: 0;
+ }
+
+ &.type-text {
+ padding: 8px 24px;
+
+ .ck-content > *:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ &.type-protectedSession {
+ padding: 20px;
+ }
+
+ &.type-image {
+ padding: 0;
+ }
+
+ &.type-pdf {
+ iframe {
+ height: 50vh;
+ }
+
+ .file-footer {
+ padding: 8px;
+ }
+ }
+
+ &.type-webView {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 50vh;
}
.ck-find-result {
- background: transparent;
- color: var(--quick-search-result-highlight-color);
- font-weight: 600;
- text-decoration: underline;
+ outline: 2px solid var(--note-list-view-content-search-result-highlight-background);
+ border-radius: 4px;
+ background: var(--note-list-view-content-search-result-highlight-background);
+ color: var(--note-list-view-content-search-result-highlight-color);
}
}
@@ -261,6 +269,8 @@
display: none;
}
+/* #endregion */
+
/* #region Grid view */
.note-list.grid-view .note-list-container {
display: flex;
From 9c2b01e3c94ebcd2b0679962e0d3121ecebdf18a Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 13:34:45 +0200
Subject: [PATCH 062/247] style/list view: tweak
---
apps/client/src/stylesheets/theme-next-dark.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/stylesheets/theme-next-dark.css b/apps/client/src/stylesheets/theme-next-dark.css
index e74180a230..eeac40a1fe 100644
--- a/apps/client/src/stylesheets/theme-next-dark.css
+++ b/apps/client/src/stylesheets/theme-next-dark.css
@@ -297,7 +297,7 @@
--note-list-view-search-result-highlight-color: var(--quick-search-result-highlight-color);
--note-list-view-content-background: rgba(0, 0, 0, .2);
--note-list-view-content-search-result-highlight-background: var(--quick-search-result-highlight-color);
- --note-list-view-content-search-result-highlight-color: white;
+ --note-list-view-content-search-result-highlight-color: black;
--calendar-coll-event-background-saturation: 25%;
--calendar-coll-event-background-lightness: 20%;
From 6b78bfecb4d7b0c0d7bff1639c09e4aab7f6dcde Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:34:48 +0200
Subject: [PATCH 063/247] refactor(badges/content): integrate run options using
additional options
---
.../widgets/layout/ActiveContentBadges.tsx | 85 +++++++------------
.../src/widgets/react/NotePropertyMenu.tsx | 7 +-
2 files changed, 34 insertions(+), 58 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 781f827553..473cae9a85 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -38,12 +38,41 @@ const typeMappings: Record{t("active_content_badges.menu_execute_now")}
-
>
)}
@@ -143,59 +171,6 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
);
}
-function ScriptRunOptions({ info, note }: { note: FNote, info: ActiveContentInfo }) {
- const [ run, setRun ] = useNoteLabel(note, "run");
-
- const options: {
- title: string;
- value: string | null;
- type: "both" | "backendScript" | "frontendScript";
- }[] = ([
- {
- title: t("active_content_badges.menu_run_disabled"),
- value: null,
- type: "both"
- },
- {
- title: t("active_content_badges.menu_run_backend_startup"),
- value: "backendStartup",
- type: "backendScript"
- },
- {
- title: t("active_content_badges.menu_run_daily"),
- value: "daily",
- type: "backendScript"
- },
- {
- title: t("active_content_badges.menu_run_hourly"),
- value: "hourly",
- type: "backendScript"
- },
- {
- title: t("active_content_badges.menu_run_frontend_startup"),
- value: "frontendStartup",
- type: "frontendScript"
- },
- {
- title: t("active_content_badges.menu_run_mobile_startup"),
- value: "mobileStartup",
- type: "frontendScript"
- }
- ] as const).filter(option => option.type === "both" || option.type === info.type);
-
- return (
-
- {options.map(({ title, value }) => (
- setRun(value)}
- checked={run ? run === value : value === null }
- >{title}
- ))}
-
- );
-}
-
function WidgetSwitcher({ note }: { note: FNote }) {
const [ widget, setWidget ] = useNoteLabelBoolean(note, "widget");
const [ disabledWidget, setDisabledWidget ] = useNoteLabelBoolean(note, "disabled:widget");
diff --git a/apps/client/src/widgets/react/NotePropertyMenu.tsx b/apps/client/src/widgets/react/NotePropertyMenu.tsx
index 5384c400a4..6a5bba1bf8 100644
--- a/apps/client/src/widgets/react/NotePropertyMenu.tsx
+++ b/apps/client/src/widgets/react/NotePropertyMenu.tsx
@@ -7,7 +7,7 @@ import FNote from "../../entities/fnote";
import NoteContextAwareWidget from "../note_context_aware_widget";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "./FormList";
import FormTextBox from "./FormTextBox";
-import { useNoteLabelBoolean, useNoteLabelWithDefault } from "./hooks";
+import { useNoteLabel, useNoteLabelBoolean, useNoteLabelWithDefault } from "./hooks";
import { ParentComponent } from "./react_utils";
export interface ClickContext {
@@ -153,13 +153,14 @@ function NumberPropertyView({ note, property }: { note: FNote, property: NumberP
}
function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) {
- const [ value, setValue ] = useNoteLabelWithDefault(note, property.bindToLabel, property.defaultValue ?? "");
+ const [ value, setValue ] = useNoteLabel(note, property.bindToLabel);
+ const valueWithDefault = value ?? property.defaultValue ?? null;
function renderItem(option: ComboBoxItem) {
return (
setValue(option.value)}
>
{option.label}
From ffd5ebbe79da92d37e530a19622770402861e91f Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:38:02 +0200
Subject: [PATCH 064/247] refactor(badges/content): integrate execute using
additional options
---
.../widgets/layout/ActiveContentBadges.tsx | 25 +++++++++----------
1 file changed, 12 insertions(+), 13 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 473cae9a85..edf93b2653 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -7,9 +7,9 @@ import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import { openInAppHelpFromUrl } from "../../services/utils";
import { BadgeWithDropdown } from "../react/Badge";
-import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
+import { FormDropdownDivider, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
-import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
+import { useNoteContext, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
import { BookProperty, ViewProperty } from "../react/NotePropertyMenu";
const NON_DANGEROUS_ACTIVE_CONTENT = [ "appCss", "appTheme" ];
@@ -22,6 +22,13 @@ interface ActiveContentInfo {
canToggleEnabled: boolean;
}
+const executeOption: BookProperty = {
+ type: "button",
+ icon: "bx bx-play",
+ label: t("active_content_badges.menu_execute_now"),
+ onClick: context => context.triggerCommand("runActiveNote")
+};
+
const typeMappings: Record
- {isExecutable && (
- <>
- {t("active_content_badges.menu_execute_now")}
-
- >
- )}
-
{(info.type === "frontendScript" || info.type === "widget") && (
<>
From b2ebaf111fc8764bc2f00554c1c0fc72b4ad56be Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 13:43:06 +0200
Subject: [PATCH 065/247] style/card: add legacy theme fallback
---
apps/client/src/widgets/react/Card.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/react/Card.css b/apps/client/src/widgets/react/Card.css
index 4a5b53f82a..49b3145f8d 100644
--- a/apps/client/src/widgets/react/Card.css
+++ b/apps/client/src/widgets/react/Card.css
@@ -10,7 +10,7 @@
.tn-card-section {
padding: var(--card-padding);
- border: 1px solid var(--card-border-color);
+ border: 1px solid var(--card-border-color, var(--main-border-color));
background: var(--card-background-color);
&:first-of-type {
From 178ac088b45bff21d1a92f96deca0ffcb6388bab Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:44:16 +0200
Subject: [PATCH 066/247] refactor(badges/content): integrate widget switcher
using additional options
---
.../widgets/layout/ActiveContentBadges.tsx | 52 ++++++++-----------
.../src/widgets/react/NotePropertyMenu.tsx | 8 +--
2 files changed, 26 insertions(+), 34 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index edf93b2653..0c874f72d5 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -9,7 +9,7 @@ import { openInAppHelpFromUrl } from "../../services/utils";
import { BadgeWithDropdown } from "../react/Badge";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
-import { useNoteContext, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
+import { useNoteContext, useTriliumEvent } from "../react/hooks";
import { BookProperty, ViewProperty } from "../react/NotePropertyMenu";
const NON_DANGEROUS_ACTIVE_CONTENT = [ "appCss", "appTheme" ];
@@ -78,14 +78,32 @@ const typeMappings: Record attributes.setLabel(note.noteId, "widget")
}
]
},
widget: {
icon: "bx bxs-widget",
- helpPage: "MgibgPcfeuGz"
+ helpPage: "MgibgPcfeuGz",
+ additionalOptions: [
+ {
+ type: "button",
+ label: t("active_content_badges.menu_change_to_frontend_script"),
+ icon: "bx bx-window",
+ onClick: ({ note }) => {
+ attributes.removeOwnedLabelByName(note, "widget");
+ attributes.removeOwnedLabelByName(note, "disabled:widget");
+ }
+ }
+ ]
},
appCss: {
icon: "bx bxs-file-css",
@@ -141,13 +159,6 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
icon={icon}
text={getTranslationForType(info.type)}
>
- {(info.type === "frontendScript" || info.type === "widget") && (
- <>
-
-
- >
- )}
-
{additionalOptions?.length && (
<>
{additionalOptions?.map((property, i) => (
@@ -170,27 +181,6 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
);
}
-function WidgetSwitcher({ note }: { note: FNote }) {
- const [ widget, setWidget ] = useNoteLabelBoolean(note, "widget");
- const [ disabledWidget, setDisabledWidget ] = useNoteLabelBoolean(note, "disabled:widget");
-
- return (widget || disabledWidget)
- ? {
- setWidget(false);
- setDisabledWidget(false);
- }}
- >{t("active_content_badges.menu_change_to_frontend_script")}
- : {
- setWidget(true);
- }}
- >{t("active_content_badges.menu_change_to_widget")} ;
-
-}
-
function getTranslationForType(type: ActiveContentInfo["type"]) {
switch (type) {
case "iconPack":
diff --git a/apps/client/src/widgets/react/NotePropertyMenu.tsx b/apps/client/src/widgets/react/NotePropertyMenu.tsx
index 6a5bba1bf8..cbc4985e68 100644
--- a/apps/client/src/widgets/react/NotePropertyMenu.tsx
+++ b/apps/client/src/widgets/react/NotePropertyMenu.tsx
@@ -58,7 +58,7 @@ interface ComboBoxGroup {
items: ComboBoxItem[];
}
-interface ComboBoxSeparator {
+interface Separator {
type: "separator"
}
@@ -71,11 +71,11 @@ export interface ComboBoxProperty {
* The default value is used when the label is not set.
*/
defaultValue?: string;
- options: (ComboBoxItem | ComboBoxSeparator | ComboBoxGroup)[];
+ options: (ComboBoxItem | Separator | ComboBoxGroup)[];
dropStart?: boolean;
}
-export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty | SplitButtonProperty;
+export type BookProperty = CheckBoxProperty | ButtonProperty | NumberProperty | ComboBoxProperty | SplitButtonProperty | Separator;
export function ViewProperty({ note, property }: { note: FNote, property: BookProperty }) {
switch (property.type) {
@@ -89,6 +89,8 @@ export function ViewProperty({ note, property }: { note: FNote, property: BookPr
return ;
case "combobox":
return ;
+ case "separator":
+ return ;
}
}
From afa9fe30631ae6fc5d933cfba7a1ac0b68e0a4e3 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:46:47 +0200
Subject: [PATCH 067/247] refactor(badges/content): integrate title into
definition
---
.../widgets/layout/ActiveContentBadges.tsx | 40 +++++++------------
1 file changed, 14 insertions(+), 26 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 0c874f72d5..023884c0d4 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -30,6 +30,7 @@ const executeOption: BookProperty = {
};
const typeMappings: Record = {
iconPack: {
+ title: t("active_content_badges.type_icon_pack"),
icon: "bx bx-package",
helpPage: "g1mlRoU8CsqC",
},
backendScript: {
+ title: t("active_content_badges.type_backend_script"),
icon: "bx bx-server",
helpPage: "SPirpZypehBG",
apiDocsPage: "MEtfsqa5VwNi",
@@ -63,6 +66,7 @@ const typeMappings: Record
{additionalOptions?.length && (
<>
@@ -181,35 +190,14 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
);
}
-function getTranslationForType(type: ActiveContentInfo["type"]) {
- switch (type) {
- case "iconPack":
- return t("active_content_badges.type_icon_pack");
- case "backendScript":
- return t("active_content_badges.type_backend_script");
- case "frontendScript":
- return t("active_content_badges.type_frontend_script");
- case "widget":
- return t("active_content_badges.type_widget");
- case "appCss":
- return t("active_content_badges.type_app_css");
- case "renderNote":
- return t("active_content_badges.type_render_note");
- case "webView":
- return t("active_content_badges.type_web_view");
- case "appTheme":
- return t("active_content_badges.type_app_theme");
- }
-}
-
function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentInfo }) {
- const typeTranslation = getTranslationForType(info.type);
+ const { title } = typeMappings[info.type];
return info && {
const attrs = note.getOwnedAttributes()
.filter(attr => {
From 4afbabb977004817fd8ec364df0eb4e757b1a0a1 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 13:51:13 +0200
Subject: [PATCH 068/247] style/list view: tweak
---
apps/client/src/widgets/collections/legacy/ListOrGridView.css | 1 +
1 file changed, 1 insertion(+)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 7a09e177f8..88aa719c11 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -213,6 +213,7 @@
background-color: var(--note-list-view-content-background);
overflow: hidden;
user-select: text;
+ font-size: .85rem;
animation: note-preview-show .25s ease-out;
will-change: opacity;
From 5a3c7355c13e55e929066837d0b8b07e960d448b Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:51:48 +0200
Subject: [PATCH 069/247] chore(badges/content): improve dropdown on mobile
---
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 023884c0d4..00099d0370 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -167,6 +167,10 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
className={clsx("active-content-badge", info.canToggleEnabled && !info.isEnabled && "disabled")}
icon={icon}
text={title}
+ dropdownOptions={{
+ dropdownContainerClassName: "mobile-bottom-menu",
+ mobileBackdrop: true
+ }}
>
{additionalOptions?.length && (
<>
From 48773636ca91d625dbbf1d4e09f59edb48dae3a9 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:54:01 +0200
Subject: [PATCH 070/247] feat(collections): improve properties dropdown on
mobile
---
apps/client/src/widgets/collections/geomap/index.css | 4 ++++
apps/client/src/widgets/note_bars/CollectionProperties.tsx | 2 ++
2 files changed, 6 insertions(+)
diff --git a/apps/client/src/widgets/collections/geomap/index.css b/apps/client/src/widgets/collections/geomap/index.css
index 4daaada2cb..a30842c9b6 100644
--- a/apps/client/src/widgets/collections/geomap/index.css
+++ b/apps/client/src/widgets/collections/geomap/index.css
@@ -11,6 +11,10 @@
}
}
+body.mobile .geo-view > .collection-properties {
+ z-index: 2500;
+}
+
.geo-map-container {
height: 100%;
overflow: hidden;
diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx
index 597769a36b..2ede18a94d 100644
--- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx
+++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx
@@ -83,6 +83,8 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption
{properties.map(property => (
From fea8de89c61db28d3b8adec039b61d3cd45ba095 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 14:08:50 +0200
Subject: [PATCH 071/247] style/list view: handle the title and attributes when
overflowing the container
---
.../src/widgets/collections/legacy/ListOrGridView.css | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 88aa719c11..4cee5d215b 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -153,6 +153,7 @@
.note-list-attributes {
flex-grow: 1;
+ margin-inline-start: 1em;
text-align: right;
font-size: .75em;
opacity: .75;
@@ -160,6 +161,16 @@
.nested-note-list-item-menu {
margin-inline-start: 8px;
+ flex-shrink: 0;
+ }
+}
+
+.nested-note-list:not(.search-results) h5 {
+ span.tn-icon + span,
+ .note-list-attributes {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
}
From e411e5f2cfaf567dede59e770d31a7f43a49dbc9 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 14:20:09 +0200
Subject: [PATCH 072/247] chore(client): fix typecheck
---
.../note_bars/CollectionProperties.tsx | 4 +-
.../src/widgets/react/NotePropertyMenu.tsx | 2 +-
.../ribbon/CollectionPropertiesTab.tsx | 158 +++++++++---------
3 files changed, 83 insertions(+), 81 deletions(-)
diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx
index 2ede18a94d..66bfb32c45 100644
--- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx
+++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx
@@ -86,8 +86,8 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption
dropdownContainerClassName="mobile-bottom-menu"
mobileBackdrop
>
- {properties.map(property => (
-
+ {properties.map((property, index) => (
+
))}
{properties.length > 0 && }
diff --git a/apps/client/src/widgets/react/NotePropertyMenu.tsx b/apps/client/src/widgets/react/NotePropertyMenu.tsx
index cbc4985e68..6d8070741d 100644
--- a/apps/client/src/widgets/react/NotePropertyMenu.tsx
+++ b/apps/client/src/widgets/react/NotePropertyMenu.tsx
@@ -53,7 +53,7 @@ export interface ComboBoxItem {
label: string;
}
-interface ComboBoxGroup {
+export interface ComboBoxGroup {
title: string;
items: ComboBoxItem[];
}
diff --git a/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx b/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx
index 4af8247a3f..e25f2650c9 100644
--- a/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx
+++ b/apps/client/src/widgets/ribbon/CollectionPropertiesTab.tsx
@@ -1,18 +1,20 @@
-import { useContext, useMemo } from "preact/hooks";
-import { t } from "../../services/i18n";
-import FormSelect, { FormSelectWithGroups } from "../react/FormSelect";
-import { TabContext } from "./ribbon-interface";
-import { mapToKeyValueArray } from "../../services/utils";
-import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
-import { bookPropertiesConfig, BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "./collection-properties-config";
-import Button, { SplitButton } from "../react/Button";
-import { ParentComponent } from "../react/react_utils";
-import FNote from "../../entities/fnote";
-import FormCheckbox from "../react/FormCheckbox";
-import FormTextBox from "../react/FormTextBox";
import { ComponentChildren } from "preact";
-import { ViewTypeOptions } from "../collections/interface";
+import { useContext, useMemo } from "preact/hooks";
+
+import FNote from "../../entities/fnote";
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
+import { t } from "../../services/i18n";
+import { mapToKeyValueArray } from "../../services/utils";
+import { ViewTypeOptions } from "../collections/interface";
+import Button, { SplitButton } from "../react/Button";
+import FormCheckbox from "../react/FormCheckbox";
+import FormSelect, { FormSelectWithGroups } from "../react/FormSelect";
+import FormTextBox from "../react/FormTextBox";
+import { useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
+import { BookProperty, ButtonProperty, CheckBoxProperty, ComboBoxGroup, ComboBoxItem, ComboBoxProperty, NumberProperty, SplitButtonProperty } from "../react/NotePropertyMenu";
+import { ParentComponent } from "../react/react_utils";
+import { bookPropertiesConfig } from "./collection-properties-config";
+import { TabContext } from "./ribbon-interface";
export const VIEW_TYPE_MAPPINGS: Record = {
grid: t("book_properties.grid"),
@@ -50,70 +52,70 @@ export function useViewType(note: FNote | null | undefined) {
}
function CollectionTypeSwitcher({ viewType, setViewType }: { viewType: string, setViewType: (newValue: string) => void }) {
- const collectionTypes = useMemo(() => mapToKeyValueArray(VIEW_TYPE_MAPPINGS), []);
+ const collectionTypes = useMemo(() => mapToKeyValueArray(VIEW_TYPE_MAPPINGS), []);
- return (
-
- {t("book_properties.view_type")}:
-
-
- )
+ return (
+
+ {t("book_properties.view_type")}:
+
+
+ );
}
-function BookProperties({ viewType, note, properties }: { viewType: ViewTypeOptions, note: FNote, properties: BookProperty[] }) {
- return (
- <>
- {properties.map(property => (
-
- {mapPropertyView({ note, property })}
-
- ))}
+function BookProperties({ note, properties }: { viewType: ViewTypeOptions, note: FNote, properties: BookProperty[] }) {
+ return (
+ <>
+ {properties.map((property, index) => (
+
+ {mapPropertyView({ note, property })}
+
+ ))}
-
- >
- )
+
+ >
+ );
}
function mapPropertyView({ note, property }: { note: FNote, property: BookProperty }) {
- switch (property.type) {
- case "button":
- return
- case "split-button":
- return
- case "checkbox":
- return
- case "number":
- return
- case "combobox":
- return
- }
+ switch (property.type) {
+ case "button":
+ return ;
+ case "split-button":
+ return ;
+ case "checkbox":
+ return ;
+ case "number":
+ return ;
+ case "combobox":
+ return ;
+ }
}
function ButtonPropertyView({ note, property }: { note: FNote, property: ButtonProperty }) {
- const parentComponent = useContext(ParentComponent);
+ const parentComponent = useContext(ParentComponent);
- return {
- if (!parentComponent) return;
- property.onClick({
- note,
- triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
- });
- }}
- />
+ return {
+ if (!parentComponent) return;
+ property.onClick({
+ note,
+ triggerCommand: parentComponent.triggerCommand.bind(parentComponent)
+ });
+ }}
+ />;
}
function SplitButtonPropertyView({ note, property }: { note: FNote, property: SplitButtonProperty }) {
@@ -131,18 +133,18 @@ function SplitButtonPropertyView({ note, property }: { note: FNote, property: Sp
onClick={() => clickContext && property.onClick(clickContext)}
>
{parentComponent && }
-
+ ;
}
function CheckboxPropertyView({ note, property }: { note: FNote, property: CheckBoxProperty }) {
- const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel);
+ const [ value, setValue ] = useNoteLabelBoolean(note, property.bindToLabel);
- return (
-
- )
+ return (
+
+ );
}
function NumberPropertyView({ note, property }: { note: FNote, property: NumberProperty }) {
@@ -160,7 +162,7 @@ function NumberPropertyView({ note, property }: { note: FNote, property: NumberP
disabled={disabled}
/>
- )
+ );
}
function ComboBoxPropertyView({ note, property }: { note: FNote, property: ComboBoxProperty }) {
@@ -169,12 +171,12 @@ function ComboBoxPropertyView({ note, property }: { note: FNote, property: Combo
return (
!("type" in i)) as (ComboBoxItem | ComboBoxGroup)[]}
keyProperty="value" titleProperty="label"
currentValue={value ?? property.defaultValue} onChange={setValue}
/>
- )
+ );
}
function LabelledEntry({ label, children }: { label: string, children: ComponentChildren }) {
@@ -186,5 +188,5 @@ function LabelledEntry({ label, children }: { label: string, children: Component
{children}
>
- )
+ );
}
From d044fce9c1192d691116e424ddd55ef1241dfe96 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 14:44:21 +0200
Subject: [PATCH 073/247] fix(mobile): remove redundant file properties footer
---
apps/client/src/layouts/mobile_layout.tsx | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/apps/client/src/layouts/mobile_layout.tsx b/apps/client/src/layouts/mobile_layout.tsx
index 8b162bced3..b56e766ac1 100644
--- a/apps/client/src/layouts/mobile_layout.tsx
+++ b/apps/client/src/layouts/mobile_layout.tsx
@@ -24,9 +24,7 @@ import NoteWrapperWidget from "../widgets/note_wrapper.js";
import NoteDetail from "../widgets/NoteDetail.jsx";
import QuickSearchWidget from "../widgets/quick_search.js";
import { useNoteContext } from "../widgets/react/hooks.jsx";
-import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
-import SearchDefinitionTab from "../widgets/ribbon/SearchDefinitionTab.jsx";
import SearchResult from "../widgets/search_result.jsx";
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
import { applyModals } from "./layout_commons.js";
@@ -78,7 +76,6 @@ export default class MobileLayout {
.child( )
.child( )
.child( )
- .child( )
)
.child( )
.child(new FindWidget())
@@ -102,13 +99,3 @@ export default class MobileLayout {
return rootContainer;
}
}
-
-function FilePropertiesWrapper() {
- const { note, ntxId } = useNoteContext();
-
- return (
-
- {note?.type === "file" && }
-
- );
-}
From 78f492861109752bb1ff6f9000f76dff37cac5d2 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 14:53:01 +0200
Subject: [PATCH 074/247] feat(mobile): reduce margins for empty tab
---
.../client/src/widgets/type_widgets/Empty.css | 30 ++++++++++---------
1 file changed, 16 insertions(+), 14 deletions(-)
diff --git a/apps/client/src/widgets/type_widgets/Empty.css b/apps/client/src/widgets/type_widgets/Empty.css
index 9f04afbcf4..40f9f02020 100644
--- a/apps/client/src/widgets/type_widgets/Empty.css
+++ b/apps/client/src/widgets/type_widgets/Empty.css
@@ -1,18 +1,20 @@
-.note-detail-empty {
- container-type: size;
- padding-top: 50px;
- min-width: 350px;
-}
+body.desktop {
+ .note-detail-empty {
+ container-type: size;
+ padding-top: 50px;
+ min-width: 350px;
+ }
-.note-detail-empty > * {
- margin-inline: auto;
- max-width: 850px;
- padding-inline: 50px;
-}
-
-@container (max-width: 600px) {
.note-detail-empty > * {
- padding-inline: 20px;
+ margin-inline: auto;
+ max-width: 850px;
+ padding-inline: 50px;
+ }
+
+ @container (max-width: 600px) {
+ .note-detail-empty > * {
+ padding-inline: 20px;
+ }
}
}
@@ -59,4 +61,4 @@
.workspace-icon {
text-align: center;
font-size: 500%;
-}
\ No newline at end of file
+}
From 2e6290c514f5b3d00640fc6a82bb63846d6100e1 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 14:55:01 +0200
Subject: [PATCH 075/247] feat(mobile): improve empty tab workspace switcher
---
.../client/src/widgets/type_widgets/Empty.css | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/widgets/type_widgets/Empty.css b/apps/client/src/widgets/type_widgets/Empty.css
index 40f9f02020..9385bef72e 100644
--- a/apps/client/src/widgets/type_widgets/Empty.css
+++ b/apps/client/src/widgets/type_widgets/Empty.css
@@ -26,10 +26,22 @@ body.desktop {
}
.workspace-notes .workspace-note {
- width: 130px;
text-align: center;
margin: 10px;
border: 1px transparent solid;
+
+ .workspace-icon {
+ text-align: center;
+ font-size: 250%;
+ }
+
+ @media (min-width: 992px) {
+ width: 130px;
+
+ .workspace-icon {
+ font-size: 500%;
+ }
+ }
}
.workspace-notes .workspace-note:hover {
@@ -57,8 +69,3 @@ body.desktop {
.empty-tab-search .input-clearer-button {
border-bottom-right-radius: 0;
}
-
-.workspace-icon {
- text-align: center;
- font-size: 500%;
-}
From a6b7761dfafad81f973c828704a7cc4b8872b881 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 14:58:28 +0200
Subject: [PATCH 076/247] fix(mobile): double-scroll bar for empty tab
---
apps/client/src/widgets/type_widgets/Empty.css | 2 --
1 file changed, 2 deletions(-)
diff --git a/apps/client/src/widgets/type_widgets/Empty.css b/apps/client/src/widgets/type_widgets/Empty.css
index 9385bef72e..d48ed2b9e1 100644
--- a/apps/client/src/widgets/type_widgets/Empty.css
+++ b/apps/client/src/widgets/type_widgets/Empty.css
@@ -51,8 +51,6 @@ body.desktop {
}
.note-detail-empty-results .aa-dropdown-menu {
- max-height: 50vh;
- overflow: scroll;
border: var(--bs-border-width) solid var(--bs-border-color);
border-top: 0;
}
From 13642235995000fd11abb6936850b7103c5f31a7 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:00:34 +0200
Subject: [PATCH 077/247] fix(mobile): horizontall scroll in empty tab due to
long attributes
---
apps/client/src/stylesheets/style.css | 1 +
1 file changed, 1 insertion(+)
diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css
index 0fc2a962e1..c3e2810fc8 100644
--- a/apps/client/src/stylesheets/style.css
+++ b/apps/client/src/stylesheets/style.css
@@ -942,6 +942,7 @@ table.promoted-attributes-in-tooltip th {
color: var(--muted-text-color);
opacity: 0.6;
line-height: 1;
+ word-wrap: break-word;
}
.aa-dropdown-menu .aa-suggestion p {
From 297dd41170cea78bb12978e7f3fdfe5858c83974 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:12:12 +0200
Subject: [PATCH 078/247] fix(mobile): incorrect tab hue on light theme
---
apps/client/src/stylesheets/theme-next-dark.css | 11 +++++++++++
apps/client/src/stylesheets/theme-next-light.css | 10 ++++++++++
.../client/src/widgets/mobile_widgets/TabSwitcher.css | 5 -----
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/apps/client/src/stylesheets/theme-next-dark.css b/apps/client/src/stylesheets/theme-next-dark.css
index cf88b7f2d2..99056aa2b6 100644
--- a/apps/client/src/stylesheets/theme-next-dark.css
+++ b/apps/client/src/stylesheets/theme-next-dark.css
@@ -338,6 +338,17 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 13.2%, 20.8%);
}
+.modal.tab-bar-modal .tabs .tab-card.with-hue {
+ background-color: hsl(var(--bg-hue), 8.8%, 11.2%);
+ border-color: hsl(var(--bg-hue), 9.4%, 25.1%);
+}
+
+.modal.tab-bar-modal .tabs .tab-card.active.with-hue {
+ background-color: hsl(var(--bg-hue), 8.8%, 11.2%);
+ border-color: hsl(var(--bg-hue), 9.4%, 25.1%);
+}
+
+
.use-note-color {
--custom-color: var(--dark-theme-custom-color);
}
diff --git a/apps/client/src/stylesheets/theme-next-light.css b/apps/client/src/stylesheets/theme-next-light.css
index 00678c6ae9..ac50453d09 100644
--- a/apps/client/src/stylesheets/theme-next-light.css
+++ b/apps/client/src/stylesheets/theme-next-light.css
@@ -313,6 +313,16 @@
--promoted-attribute-card-background-color: hsl(var(--custom-color-hue), 40%, 88%);
}
+.modal.tab-bar-modal .tabs .tab-card.with-hue {
+ background-color: hsl(var(--bg-hue), 56%, 96%);
+ border-color: hsl(var(--bg-hue), 33%, 41%);
+}
+
+.modal.tab-bar-modal .tabs .tab-card.active.with-hue {
+ background-color: hsl(var(--bg-hue), 86%, 96%);
+ border-color: hsl(var(--bg-hue), 33%, 41%);
+}
+
.note-split.with-hue,
.quick-edit-dialog-wrapper.with-hue {
--note-icon-custom-background-color: hsl(var(--custom-color-hue), 44.5%, 43.1%);
diff --git a/apps/client/src/widgets/mobile_widgets/TabSwitcher.css b/apps/client/src/widgets/mobile_widgets/TabSwitcher.css
index 8be65240b2..58b6c1fbc3 100644
--- a/apps/client/src/widgets/mobile_widgets/TabSwitcher.css
+++ b/apps/client/src/widgets/mobile_widgets/TabSwitcher.css
@@ -35,11 +35,6 @@
display: flex;
flex-direction: column;
- &.with-hue {
- background-color: hsl(var(--bg-hue), 8.8%, 11.2%);
- border-color: hsl(var(--bg-hue), 9.4%, 25.1%);
- }
-
&.active {
outline: 4px solid var(--more-accented-background-color);
background: var(--card-background-hover-color);
From 10b5d29107c964bcacd470532178ad03b5349d5c Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:16:23 +0200
Subject: [PATCH 079/247] fix(mobile): promoted attributes having a max-height
---
apps/client/src/widgets/PromotedAttributes.css | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/PromotedAttributes.css b/apps/client/src/widgets/PromotedAttributes.css
index 6e3c7795d1..ef15905192 100644
--- a/apps/client/src/widgets/PromotedAttributes.css
+++ b/apps/client/src/widgets/PromotedAttributes.css
@@ -16,6 +16,10 @@ body.mobile .promoted-attributes-widget {
display: table;
}
+body.experimental-feature-new-layout .promoted-attributes-container {
+ max-height: unset;
+}
+
.promoted-attribute-cell {
display: flex;
align-items: center;
@@ -94,4 +98,4 @@ body.mobile .promoted-attributes-widget {
background: rgba(0, 0, 0, 0.5);
transform: rotate(45deg);
pointer-events: none;
-}
\ No newline at end of file
+}
From 67acfaab62b0c4094c18ecccd12ccf472d031b50 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:17:08 +0200
Subject: [PATCH 080/247] fix(mobile): missing scroll padding
---
apps/client/src/layouts/mobile_layout.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/apps/client/src/layouts/mobile_layout.tsx b/apps/client/src/layouts/mobile_layout.tsx
index b56e766ac1..e194bb7271 100644
--- a/apps/client/src/layouts/mobile_layout.tsx
+++ b/apps/client/src/layouts/mobile_layout.tsx
@@ -25,6 +25,7 @@ import NoteDetail from "../widgets/NoteDetail.jsx";
import QuickSearchWidget from "../widgets/quick_search.js";
import { useNoteContext } from "../widgets/react/hooks.jsx";
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
+import ScrollPadding from "../widgets/scroll_padding";
import SearchResult from "../widgets/search_result.jsx";
import MobileEditorToolbar from "../widgets/type_widgets/text/mobile_editor_toolbar.jsx";
import { applyModals } from "./layout_commons.js";
@@ -76,6 +77,7 @@ export default class MobileLayout {
.child( )
.child( )
.child( )
+ .child( )
)
.child( )
.child(new FindWidget())
From e0766ad439e9fd129e1d0b06a46101363f4d66f1 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:25:28 +0200
Subject: [PATCH 081/247] feat(mobile): display grid view on two columns
---
apps/client/src/widgets/collections/legacy/ListOrGridView.css | 4 ++++
apps/client/src/widgets/containers/scrolling_container.css | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 1bfa389e5c..722e747ef0 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -128,6 +128,10 @@
border: 1px solid transparent;
}
+body.mobile .note-list.grid-view .note-book-card {
+ flex-basis: 150px;
+}
+
.note-list.grid-view .note-book-card {
max-height: 300px;
}
diff --git a/apps/client/src/widgets/containers/scrolling_container.css b/apps/client/src/widgets/containers/scrolling_container.css
index 0e590801c4..a3fae557a3 100644
--- a/apps/client/src/widgets/containers/scrolling_container.css
+++ b/apps/client/src/widgets/containers/scrolling_container.css
@@ -19,6 +19,10 @@
}
}
+body.mobile .scrolling-container {
+ --content-margin-inline: 8px;
+}
+
.note-split.type-code:not(.mime-text-x-sqlite) {
&> .scrolling-container {
background-color: var(--code-background-color);
From 9ed2894a0c3d83d2dff29485d45d5aff01677d3b Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:29:27 +0200
Subject: [PATCH 082/247] fix(mobile): badges not collapsing in quick edit
---
apps/client/src/widgets/dialogs/PopupEditor.css | 6 ++++++
apps/client/src/widgets/dialogs/PopupEditor.tsx | 6 ++----
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/apps/client/src/widgets/dialogs/PopupEditor.css b/apps/client/src/widgets/dialogs/PopupEditor.css
index 3357b00f3e..84f4c93197 100644
--- a/apps/client/src/widgets/dialogs/PopupEditor.css
+++ b/apps/client/src/widgets/dialogs/PopupEditor.css
@@ -32,6 +32,12 @@ body.mobile .modal.popup-editor-dialog .modal-dialog {
display: flex;
align-items: center;
margin-block: 0;
+
+ .note-icon-widget {
+ display: flex;
+ align-items: center;
+ margin-inline-start: 0;
+ }
}
.modal.popup-editor-dialog .modal-header .note-title-widget {
diff --git a/apps/client/src/widgets/dialogs/PopupEditor.tsx b/apps/client/src/widgets/dialogs/PopupEditor.tsx
index a7f3fde393..07363b2e91 100644
--- a/apps/client/src/widgets/dialogs/PopupEditor.tsx
+++ b/apps/client/src/widgets/dialogs/PopupEditor.tsx
@@ -67,10 +67,7 @@ export default function PopupEditor() {
-
- {isNewLayout && }
- >}
+ title={ }
customTitleBarButtons={[{
iconClassName: "bx-expand-alt",
title: t("popup-editor.maximize"),
@@ -123,6 +120,7 @@ export function TitleRow() {
+ {isNewLayout && }
);
}
From 5a77318a9ed0fc3cc781fb75aa773f063983ae40 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:33:03 +0200
Subject: [PATCH 083/247] chore(mobile): address requested changes
---
apps/client/src/stylesheets/theme-next-dark.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/stylesheets/theme-next-dark.css b/apps/client/src/stylesheets/theme-next-dark.css
index 99056aa2b6..6a089f0ed3 100644
--- a/apps/client/src/stylesheets/theme-next-dark.css
+++ b/apps/client/src/stylesheets/theme-next-dark.css
@@ -344,7 +344,7 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
}
.modal.tab-bar-modal .tabs .tab-card.active.with-hue {
- background-color: hsl(var(--bg-hue), 8.8%, 11.2%);
+ background-color: hsl(var(--bg-hue), 8.8%, 16.2%);
border-color: hsl(var(--bg-hue), 9.4%, 25.1%);
}
From d11fb38280592db5743e686609b8875885c03a1a Mon Sep 17 00:00:00 2001
From: Marcel
Date: Sun, 15 Feb 2026 10:21:28 +0100
Subject: [PATCH 084/247] Translated using Weblate (German)
Currently translated at 100.0% (389 of 389 strings)
Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/de/
---
apps/server/src/assets/translations/de/server.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/server/src/assets/translations/de/server.json b/apps/server/src/assets/translations/de/server.json
index 70b8decc10..301015d686 100644
--- a/apps/server/src/assets/translations/de/server.json
+++ b/apps/server/src/assets/translations/de/server.json
@@ -425,7 +425,7 @@
"error_title": "Fehler"
},
"share_theme": {
- "site-theme": "Webseite Stil",
+ "site-theme": "Webseiten Stil",
"search_placeholder": "Suche...",
"image_alt": "Artikel Bild",
"last-updated": "Zuletzt aktualisiert am {{- date}}",
From 964633f426a87a3ab49785816f323425cf8c55b1 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 15:36:58 +0200
Subject: [PATCH 085/247] refactor(client/web_view): fix lint & formatting
---
.../src/widgets/type_widgets/WebView.tsx | 59 ++++++++++---------
1 file changed, 31 insertions(+), 28 deletions(-)
diff --git a/apps/client/src/widgets/type_widgets/WebView.tsx b/apps/client/src/widgets/type_widgets/WebView.tsx
index 3b47a0cd15..40c5ef8655 100644
--- a/apps/client/src/widgets/type_widgets/WebView.tsx
+++ b/apps/client/src/widgets/type_widgets/WebView.tsx
@@ -1,13 +1,15 @@
+import "./WebView.css";
+
import { useCallback, useState } from "preact/hooks";
+
import FNote from "../../entities/fnote";
import { t } from "../../services/i18n";
+import toast from "../../services/toast";
import utils from "../../services/utils";
+import Button from "../react/Button";
+import FormGroup from "../react/FormGroup";
import { useNoteLabel } from "../react/hooks";
import { TypeWidgetProps } from "./type_widget";
-import "./WebView.css";
-import FormGroup from "../react/FormGroup";
-import toast from "../../services/toast";
-import Button from "../react/Button";
const isElectron = utils.isElectron();
@@ -22,47 +24,48 @@ export default function WebView({ note }: TypeWidgetProps) {
function WebViewContent({ src }: { src: string }) {
if (!isElectron) {
- return
- } else {
- return
+ return ;
}
+ return ;
+
}
function SetupWebView({note}: {note: FNote}) {
- const [srcLabel, setSrcLabel] = useNoteLabel(note, "webViewSrc");
- const [src, setSrc] = useState("");
+ const [ , setSrcLabel] = useNoteLabel(note, "webViewSrc");
+ const [ src, setSrc ] = useState("");
const submit = useCallback((url: string) => {
try {
// Validate URL
new URL(url);
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (ex) {
toast.showErrorTitleAndMessage(t("web_view_setup.invalid_url_title"),
- t("web_view_setup.invalid_url_message"));
+ t("web_view_setup.invalid_url_message"));
return;
}
setSrcLabel(url);
- }, [note]);
+ }, [ setSrcLabel ]);
return
-
-
+
+
+
+
+ ;
}
From 4b8d341e001c4474b700f98b6cfa638bee935263 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:02:51 +0200
Subject: [PATCH 086/247] feat(web_view): add a screen if web view is disabled
---
apps/client/src/services/attributes.ts | 40 ++++++++-
.../src/translations/en/translation.json | 4 +-
.../widgets/layout/ActiveContentBadges.tsx | 20 +----
.../src/widgets/type_widgets/WebView.css | 6 +-
.../src/widgets/type_widgets/WebView.tsx | 87 ++++++++++++++-----
packages/commons/src/lib/attribute_names.ts | 1 +
6 files changed, 114 insertions(+), 44 deletions(-)
diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts
index 2bff933244..fd8a8b0f10 100644
--- a/apps/client/src/services/attributes.ts
+++ b/apps/client/src/services/attributes.ts
@@ -168,6 +168,42 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
return false;
}
+/**
+ * Toggles whether a dangerous attribute is enabled or not. When an attribute is disabled, its name is prefixed with `disabled:`.
+ *
+ * Note that this work for non-dangerous attributes as well.
+ *
+ * @param note the note whose attribute to change.
+ * @param type the type of dangerous attribute (label or relation).
+ * @param name the name of the dangerous attribute.
+ * @param willEnable whether to enable or disable the attribute.
+ * @returns a promise that will resolve when the request to the server completes.
+ */
+async function toggleDangerousAttribute(note: FNote, type: "label" | "relation", name: string, willEnable: boolean) {
+ const attr = note.getOwnedAttribute(type, name) ?? note.getOwnedAttribute(type, `disabled:${name}`);
+ if (!attr) return;
+ const baseName = getNameWithoutDangerousPrefix(attr.name);
+ const newName = willEnable ? baseName : `disabled:${baseName}`;
+ if (newName === attr.name) return;
+
+ // We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
+ if (attr.type === "label") {
+ await setLabel(note.noteId, newName, attr.value);
+ } else {
+ await setRelation(note.noteId, newName, attr.value);
+ }
+ await removeAttributeById(note.noteId, attr.attributeId);
+}
+
+/**
+ * Returns the name of an attribute without the `disabled:` prefix, or the same name if it's not disabled.
+ * @param name the name of an attribute.
+ * @returns the name without the `disabled:` prefix.
+ */
+function getNameWithoutDangerousPrefix(name: string) {
+ return name.startsWith("disabled:") ? name.substring(9) : name;
+}
+
export default {
addLabel,
setLabel,
@@ -177,5 +213,7 @@ export default {
removeAttributeById,
removeOwnedLabelByName,
removeOwnedRelationByName,
- isAffecting
+ isAffecting,
+ toggleDangerousAttribute,
+ getNameWithoutDangerousPrefix
};
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 3656035497..7b38c0dd8d 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1075,7 +1075,9 @@
"url_placeholder": "Enter or paste the website address, for example https://triliumnotes.org",
"create_button": "Create Web View",
"invalid_url_title": "Invalid address",
- "invalid_url_message": "Insert a valid web address, for example https://triliumnotes.org."
+ "invalid_url_message": "Insert a valid web address, for example https://triliumnotes.org.",
+ "disabled_description": "This web view was imported from an external source. To help protect you from phishing or malicious content, it isn’t loading automatically. You can enable it if you trust the source.",
+ "disabled_button_enable": "Enable web view"
},
"backend_log": {
"refresh": "Refresh"
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 00099d0370..978fe7ef03 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -206,31 +206,15 @@ function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentI
const attrs = note.getOwnedAttributes()
.filter(attr => {
if (attr.isInheritable) return false;
- const baseName = getNameWithoutPrefix(attr.name);
+ const baseName = attributes.getNameWithoutDangerousPrefix(attr.name);
return DANGEROUS_ATTRIBUTES.some(item => item.name === baseName && item.type === attr.type);
});
- for (const attr of attrs) {
- const baseName = getNameWithoutPrefix(attr.name);
- const newName = willEnable ? baseName : `disabled:${baseName}`;
- if (newName === attr.name) continue;
-
- // We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
- if (attr.type === "label") {
- await attributes.setLabel(note.noteId, newName, attr.value);
- } else {
- await attributes.setRelation(note.noteId, newName, attr.value);
- }
- await attributes.removeAttributeById(note.noteId, attr.attributeId);
- }
+ await Promise.all(attrs.map(a => attributes.toggleDangerousAttribute(note, a.type, a.name, willEnable)));
}}
/>;
}
-function getNameWithoutPrefix(name: string) {
- return name.startsWith("disabled:") ? name.substring(9) : name;
-}
-
function useActiveContentInfo(note: FNote | null | undefined) {
const [ info, setInfo ] = useState(null);
diff --git a/apps/client/src/widgets/type_widgets/WebView.css b/apps/client/src/widgets/type_widgets/WebView.css
index c158e3ceae..ceeb5ba7af 100644
--- a/apps/client/src/widgets/type_widgets/WebView.css
+++ b/apps/client/src/widgets/type_widgets/WebView.css
@@ -32,4 +32,8 @@
width: 100%;
max-width: 600px;
}
-}
\ No newline at end of file
+
+ .tn-link {
+ margin-top: 1em;
+ }
+}
diff --git a/apps/client/src/widgets/type_widgets/WebView.tsx b/apps/client/src/widgets/type_widgets/WebView.tsx
index 40c5ef8655..aeb56d70b2 100644
--- a/apps/client/src/widgets/type_widgets/WebView.tsx
+++ b/apps/client/src/widgets/type_widgets/WebView.tsx
@@ -3,23 +3,32 @@ import "./WebView.css";
import { useCallback, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
+import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import toast from "../../services/toast";
-import utils from "../../services/utils";
+import utils, { openInAppHelpFromUrl } from "../../services/utils";
import Button from "../react/Button";
import FormGroup from "../react/FormGroup";
+import FormTextBox from "../react/FormTextBox";
import { useNoteLabel } from "../react/hooks";
+import LinkButton from "../react/LinkButton";
import { TypeWidgetProps } from "./type_widget";
const isElectron = utils.isElectron();
export default function WebView({ note }: TypeWidgetProps) {
const [ webViewSrc ] = useNoteLabel(note, "webViewSrc");
+ const [ disabledWebViewSrc ] = useNoteLabel(note, "disabled:webViewSrc");
- return (webViewSrc
- ?
- :
- );
+ if (disabledWebViewSrc) {
+ return ;
+ }
+
+ if (!webViewSrc) {
+ return ;
+ }
+
+ return ;
}
function WebViewContent({ src }: { src: string }) {
@@ -48,24 +57,56 @@ function SetupWebView({note}: {note: FNote}) {
setSrcLabel(url);
}, [ setSrcLabel ]);
- return
+ );
+}
+
+function DisabledWebView({ note, url }: { note: FNote, url: string }) {
+ return (
+
+
+
+
+
+
+
+
+ attributes.toggleDangerousAttribute(note, "label", "webViewSrc", true)}
+ primary
+ />
+
+ openInAppHelpFromUrl("1vHRoWCEjj0L")}
+ />
+
+
+ );
}
diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts
index da3b890c9c..d5a63de04a 100644
--- a/packages/commons/src/lib/attribute_names.ts
+++ b/packages/commons/src/lib/attribute_names.ts
@@ -61,6 +61,7 @@ type Labels = {
// Note-type specific
webViewSrc: string;
+ "disabled:webViewSrc": string;
readOnly: boolean;
mapType: string;
mapRootNoteId: string;
From a02f3c4440f6e98a19e7857a3ef4f85cc0491ff2 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:08:58 +0200
Subject: [PATCH 087/247] refactor(client): extract setup form to dedicated
component
---
.../src/widgets/type_widgets/WebView.css | 21 -----
.../src/widgets/type_widgets/WebView.tsx | 80 +++++++++----------
.../type_widgets/helpers/SetupForm.css | 20 +++++
.../type_widgets/helpers/SetupForm.tsx | 22 +++++
4 files changed, 80 insertions(+), 63 deletions(-)
create mode 100644 apps/client/src/widgets/type_widgets/helpers/SetupForm.css
create mode 100644 apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
diff --git a/apps/client/src/widgets/type_widgets/WebView.css b/apps/client/src/widgets/type_widgets/WebView.css
index ceeb5ba7af..42ac871cfb 100644
--- a/apps/client/src/widgets/type_widgets/WebView.css
+++ b/apps/client/src/widgets/type_widgets/WebView.css
@@ -16,24 +16,3 @@
width: 100%;
height: 100%;
}
-
-.web-view-setup-form {
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding-inline: 40px;
-
- .form-icon {
- margin-bottom: 12px;
- }
-
- .form-group {
- width: 100%;
- max-width: 600px;
- }
-
- .tn-link {
- margin-top: 1em;
- }
-}
diff --git a/apps/client/src/widgets/type_widgets/WebView.tsx b/apps/client/src/widgets/type_widgets/WebView.tsx
index aeb56d70b2..90db7000d2 100644
--- a/apps/client/src/widgets/type_widgets/WebView.tsx
+++ b/apps/client/src/widgets/type_widgets/WebView.tsx
@@ -12,6 +12,7 @@ import FormGroup from "../react/FormGroup";
import FormTextBox from "../react/FormTextBox";
import { useNoteLabel } from "../react/hooks";
import LinkButton from "../react/LinkButton";
+import SetupForm from "./helpers/SetupForm";
import { TypeWidgetProps } from "./type_widget";
const isElectron = utils.isElectron();
@@ -58,55 +59,50 @@ function SetupWebView({note}: {note: FNote}) {
}, [ setSrcLabel ]);
return (
-
-
submit(src)}>
-
-
-
- {setSrc((e.target as HTMLInputElement)?.value);}}
- />
-
-
- submit(src)}
+ >
+
+ {setSrc((e.target as HTMLInputElement)?.value);}}
/>
-
-
+
+
+
+
);
}
function DisabledWebView({ note, url }: { note: FNote, url: string }) {
return (
-
-
-
-
-
-
-
-
- attributes.toggleDangerousAttribute(note, "label", "webViewSrc", true)}
- primary
+
+
+
+
- openInAppHelpFromUrl("1vHRoWCEjj0L")}
- />
-
-
+ attributes.toggleDangerousAttribute(note, "label", "webViewSrc", true)}
+ primary
+ />
+
+ openInAppHelpFromUrl("1vHRoWCEjj0L")}
+ />
+
);
}
diff --git a/apps/client/src/widgets/type_widgets/helpers/SetupForm.css b/apps/client/src/widgets/type_widgets/helpers/SetupForm.css
new file mode 100644
index 0000000000..205f3a4cde
--- /dev/null
+++ b/apps/client/src/widgets/type_widgets/helpers/SetupForm.css
@@ -0,0 +1,20 @@
+.setup-form {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ padding-inline: 40px;
+
+ .form-icon {
+ margin-bottom: 12px;
+ }
+
+ .form-group {
+ width: 100%;
+ max-width: 600px;
+ }
+
+ .tn-link {
+ margin-top: 1em;
+ }
+}
diff --git a/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx b/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
new file mode 100644
index 0000000000..121b4bb692
--- /dev/null
+++ b/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
@@ -0,0 +1,22 @@
+import "./SetupForm.css";
+
+import clsx from "clsx";
+import { ComponentChildren } from "preact";
+
+interface SetupFormProps {
+ icon: string;
+ onSubmit?: () => void;
+ children: ComponentChildren;
+}
+
+export default function SetupForm({ icon, children, onSubmit }: SetupFormProps) {
+ return (
+
+
+
+
+ {children}
+
+
+ );
+}
From 2103be9d2850beeba005dac786d196cb5a7ff413 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:11:52 +0200
Subject: [PATCH 088/247] refactor(client): extract learn more into component
---
apps/client/src/translations/en/translation.json | 3 +++
apps/client/src/widgets/type_widgets/WebView.tsx | 13 ++++---------
.../src/widgets/type_widgets/helpers/SetupForm.tsx | 14 +++++++++++++-
3 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 7b38c0dd8d..341e3ed579 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2314,5 +2314,8 @@
"menu_change_to_widget": "Change to widget",
"menu_change_to_frontend_script": "Change to frontend script",
"menu_theme_base": "Theme base"
+ },
+ "setup_form": {
+ "more_info": "Learn more"
}
}
diff --git a/apps/client/src/widgets/type_widgets/WebView.tsx b/apps/client/src/widgets/type_widgets/WebView.tsx
index 90db7000d2..d61725d064 100644
--- a/apps/client/src/widgets/type_widgets/WebView.tsx
+++ b/apps/client/src/widgets/type_widgets/WebView.tsx
@@ -6,16 +6,16 @@ import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import toast from "../../services/toast";
-import utils, { openInAppHelpFromUrl } from "../../services/utils";
+import utils from "../../services/utils";
import Button from "../react/Button";
import FormGroup from "../react/FormGroup";
import FormTextBox from "../react/FormTextBox";
import { useNoteLabel } from "../react/hooks";
-import LinkButton from "../react/LinkButton";
import SetupForm from "./helpers/SetupForm";
import { TypeWidgetProps } from "./type_widget";
const isElectron = utils.isElectron();
+const HELP_PAGE = "1vHRoWCEjj0L";
export default function WebView({ note }: TypeWidgetProps) {
const [ webViewSrc ] = useNoteLabel(note, "webViewSrc");
@@ -60,7 +60,7 @@ function SetupWebView({note}: {note: FNote}) {
return (
submit(src)}
>
@@ -83,7 +83,7 @@ function SetupWebView({note}: {note: FNote}) {
function DisabledWebView({ note, url }: { note: FNote, url: string }) {
return (
-
+
attributes.toggleDangerousAttribute(note, "label", "webViewSrc", true)}
primary
/>
-
- openInAppHelpFromUrl("1vHRoWCEjj0L")}
- />
);
}
diff --git a/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx b/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
index 121b4bb692..69e491fb6c 100644
--- a/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
+++ b/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
@@ -3,19 +3,31 @@ import "./SetupForm.css";
import clsx from "clsx";
import { ComponentChildren } from "preact";
+import { t } from "../../../services/i18n";
+import { openInAppHelpFromUrl } from "../../../services/utils";
+import LinkButton from "../../react/LinkButton";
+
interface SetupFormProps {
icon: string;
onSubmit?: () => void;
children: ComponentChildren;
+ inAppHelpPage?: string;
}
-export default function SetupForm({ icon, children, onSubmit }: SetupFormProps) {
+export default function SetupForm({ icon, children, onSubmit, inAppHelpPage }: SetupFormProps) {
return (
{children}
+
+ {inAppHelpPage && (
+ openInAppHelpFromUrl("1vHRoWCEjj0L")}
+ />
+ )}
);
From 06a005aceca21e8cd5b8ff74f51d0662b41767e5 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:17:41 +0200
Subject: [PATCH 089/247] refactor(client/render_note): separate content from
setup
---
.../src/widgets/type_widgets/Render.tsx | 38 +++++++++++--------
packages/commons/src/lib/attribute_names.ts | 3 ++
2 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index f47e58f2de..1f9d61d218 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -1,23 +1,33 @@
import "./Render.css";
-import { useEffect, useRef, useState } from "preact/hooks";
+import { useEffect, useRef } from "preact/hooks";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import render from "../../services/render";
import Alert from "../react/Alert";
-import { useTriliumEvent } from "../react/hooks";
+import { useNoteRelation, useTriliumEvent } from "../react/hooks";
import RawHtml from "../react/RawHtml";
import { refToJQuerySelector } from "../react/react_utils";
import { TypeWidgetProps } from "./type_widget";
-export default function Render({ note, noteContext, ntxId }: TypeWidgetProps) {
+export default function Render(props: TypeWidgetProps) {
+ const { note } = props;
+ const [ renderNote ] = useNoteRelation(note, "renderNote");
+
+ if (!renderNote) {
+ return ;
+ }
+
+ return ;
+}
+
+function RenderContent({ note, noteContext, ntxId }: TypeWidgetProps) {
const contentRef = useRef(null);
- const [ renderNotesFound, setRenderNotesFound ] = useState(false);
function refresh() {
if (!contentRef) return;
- render.render(note, refToJQuerySelector(contentRef)).then(setRenderNotesFound);
+ render.render(note, refToJQuerySelector(contentRef));
}
useEffect(refresh, [ note ]);
@@ -47,16 +57,14 @@ export default function Render({ note, noteContext, ntxId }: TypeWidgetProps) {
resolve(refToJQuerySelector(contentRef));
});
- return (
- <>
- {!renderNotesFound && (
-
- {t("render.note_detail_render_help_1")}
-
-
- )}
+ return
;
+}
-
- >
+function SetupRenderContent() {
+ return (
+
+ {t("render.note_detail_render_help_1")}
+
+
);
}
diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts
index d5a63de04a..7a9534a5b7 100644
--- a/packages/commons/src/lib/attribute_names.ts
+++ b/packages/commons/src/lib/attribute_names.ts
@@ -78,6 +78,9 @@ type Relations = [
"searchScript",
"ancestor",
+ // Active content
+ "renderNote",
+
// Launcher-specific
"target",
"widget"
From 5550cb7b9597d95bd0a69d8140373e2c633c1a89 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:30:10 +0200
Subject: [PATCH 090/247] feat(client/render_note): basic setup for with note
picker
---
.../src/stylesheets/theme-next/forms.css | 4 ++--
.../src/translations/en/translation.json | 3 +--
.../src/widgets/type_widgets/Render.tsx | 24 ++++++++++++-------
.../type_widgets/helpers/SetupForm.tsx | 2 +-
4 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/apps/client/src/stylesheets/theme-next/forms.css b/apps/client/src/stylesheets/theme-next/forms.css
index bccf7ab6d4..2fc8a39dbd 100644
--- a/apps/client/src/stylesheets/theme-next/forms.css
+++ b/apps/client/src/stylesheets/theme-next/forms.css
@@ -838,7 +838,7 @@ input[type="range"] {
text-align: center;
}
-.tn-centered-form input,
+.tn-centered-form .input-group,
.tn-centered-form button {
margin-top: 12px;
-}
\ No newline at end of file
+}
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 341e3ed579..f5926aa4b1 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1067,8 +1067,7 @@
"click_on_canvas_to_place_new_note": "Click on canvas to place new note"
},
"render": {
- "note_detail_render_help_1": "This help note is shown because this note of type Render HTML doesn't have required relation to function properly.",
- "note_detail_render_help_2": "Render HTML note type is used for scripting . In short, you have a HTML code note (optionally with some JavaScript) and this note will render it. To make it work, you need to define a relation called \"renderNote\" pointing to the HTML note to render."
+ "setup_title": "Display custom HTML or Preact JSX inside this note"
},
"web_view_setup": {
"title": "Create a live view of a webpage directly into Trilium",
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index 1f9d61d218..1f05820008 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -5,10 +5,11 @@ import { useEffect, useRef } from "preact/hooks";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import render from "../../services/render";
-import Alert from "../react/Alert";
+import FormGroup from "../react/FormGroup";
import { useNoteRelation, useTriliumEvent } from "../react/hooks";
-import RawHtml from "../react/RawHtml";
+import NoteAutocomplete from "../react/NoteAutocomplete";
import { refToJQuerySelector } from "../react/react_utils";
+import SetupForm from "./helpers/SetupForm";
import { TypeWidgetProps } from "./type_widget";
export default function Render(props: TypeWidgetProps) {
@@ -16,7 +17,7 @@ export default function Render(props: TypeWidgetProps) {
const [ renderNote ] = useNoteRelation(note, "renderNote");
if (!renderNote) {
- return ;
+ return ;
}
return ;
@@ -60,11 +61,18 @@ function RenderContent({ note, noteContext, ntxId }: TypeWidgetProps) {
return
;
}
-function SetupRenderContent() {
+function SetupRenderContent({ note }: TypeWidgetProps) {
return (
-
- {t("render.note_detail_render_help_1")}
-
-
+
+
+ {
+ if (!noteId) return;
+ attributes.setRelation(note.noteId, "renderNote", noteId);
+ }} />
+
+
);
}
diff --git a/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx b/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
index 69e491fb6c..771b8de612 100644
--- a/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
+++ b/apps/client/src/widgets/type_widgets/helpers/SetupForm.tsx
@@ -25,7 +25,7 @@ export default function SetupForm({ icon, children, onSubmit, inAppHelpPage }: S
{inAppHelpPage && (
openInAppHelpFromUrl("1vHRoWCEjj0L")}
+ onClick={() => openInAppHelpFromUrl(inAppHelpPage)}
/>
)}
From 1ba498c0e39993ec7667f8671c35e4cc6146eca9 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:38:09 +0200
Subject: [PATCH 091/247] feat(client/render_note): create sample note with
preact
---
.../src/translations/en/translation.json | 4 ++-
.../src/widgets/type_widgets/Render.tsx | 25 +++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index f5926aa4b1..ac5f3256ed 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1067,7 +1067,9 @@
"click_on_canvas_to_place_new_note": "Click on canvas to place new note"
},
"render": {
- "setup_title": "Display custom HTML or Preact JSX inside this note"
+ "setup_title": "Display custom HTML or Preact JSX inside this note",
+ "setup_create_sample": "Create sample note with Preact",
+ "setup_sample_created": "A sample note was created as a child note."
},
"web_view_setup": {
"title": "Create a live view of a webpage directly into Trilium",
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index 1f05820008..7dc524e216 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -4,7 +4,10 @@ import { useEffect, useRef } from "preact/hooks";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
+import note_create from "../../services/note_create";
import render from "../../services/render";
+import toast from "../../services/toast";
+import Button from "../react/Button";
import FormGroup from "../react/FormGroup";
import { useNoteRelation, useTriliumEvent } from "../react/hooks";
import NoteAutocomplete from "../react/NoteAutocomplete";
@@ -12,6 +15,12 @@ import { refToJQuerySelector } from "../react/react_utils";
import SetupForm from "./helpers/SetupForm";
import { TypeWidgetProps } from "./type_widget";
+const PREACT_SAMPLE = /*js*/`\
+export default function() {
+ return Hello world.
;
+}
+`;
+
export default function Render(props: TypeWidgetProps) {
const { note } = props;
const [ renderNote ] = useNoteRelation(note, "renderNote");
@@ -73,6 +82,22 @@ function SetupRenderContent({ note }: TypeWidgetProps) {
attributes.setRelation(note.noteId, "renderNote", noteId);
}} />
+
+ {
+ const { note: codeNote } = await note_create.createNote(note.noteId, {
+ type: "code",
+ mime: "text/jsx",
+ content: PREACT_SAMPLE,
+ activate: false
+ });
+ if (!codeNote) return;
+ await attributes.setRelation(note.noteId, "renderNote", codeNote.noteId);
+ toast.showMessage(t("render.setup_sample_created"));
+ }}
+ />
);
}
From 197b769838cd014249370082bc04258efa4ea915 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:46:19 +0200
Subject: [PATCH 092/247] feat(client/render_note): create sample note with
HTML
---
.../src/translations/en/translation.json | 3 +-
.../src/widgets/type_widgets/Render.tsx | 51 ++++++++++++-------
2 files changed, 36 insertions(+), 18 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index ac5f3256ed..b90f79da4b 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1068,7 +1068,8 @@
},
"render": {
"setup_title": "Display custom HTML or Preact JSX inside this note",
- "setup_create_sample": "Create sample note with Preact",
+ "setup_create_sample_preact": "Create sample note with Preact",
+ "setup_create_sample_html": "Create sample note with HTML",
"setup_sample_created": "A sample note was created as a child note."
},
"web_view_setup": {
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index 7dc524e216..88fc75cd75 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -2,13 +2,15 @@ import "./Render.css";
import { useEffect, useRef } from "preact/hooks";
+import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import note_create from "../../services/note_create";
import render from "../../services/render";
import toast from "../../services/toast";
-import Button from "../react/Button";
+import { SplitButton } from "../react/Button";
import FormGroup from "../react/FormGroup";
+import { FormListItem } from "../react/FormList";
import { useNoteRelation, useTriliumEvent } from "../react/hooks";
import NoteAutocomplete from "../react/NoteAutocomplete";
import { refToJQuerySelector } from "../react/react_utils";
@@ -21,6 +23,10 @@ export default function() {
}
`;
+const HTML_SAMPLE = /*html*/`\
+Hello world.
+`;
+
export default function Render(props: TypeWidgetProps) {
const { note } = props;
const [ renderNote ] = useNoteRelation(note, "renderNote");
@@ -79,25 +85,36 @@ function SetupRenderContent({ note }: TypeWidgetProps) {
{
if (!noteId) return;
- attributes.setRelation(note.noteId, "renderNote", noteId);
+ setRenderNote(note, noteId);
}} />
- {
- const { note: codeNote } = await note_create.createNote(note.noteId, {
- type: "code",
- mime: "text/jsx",
- content: PREACT_SAMPLE,
- activate: false
- });
- if (!codeNote) return;
- await attributes.setRelation(note.noteId, "renderNote", codeNote.noteId);
- toast.showMessage(t("render.setup_sample_created"));
- }}
- />
+ setupSampleNote(note, "text/jsx", PREACT_SAMPLE)}
+ >
+ setupSampleNote(note, "text/html", HTML_SAMPLE)}
+ >{t("render.setup_create_sample_html")}
+
);
}
+
+async function setRenderNote(note: FNote, targetNoteUrl: string) {
+ await attributes.setRelation(note.noteId, "renderNote", targetNoteUrl);
+}
+
+async function setupSampleNote(parentNote: FNote, mime: string, content: string) {
+ const { note: codeNote } = await note_create.createNote(parentNote.noteId, {
+ type: "code",
+ mime,
+ content,
+ activate: false
+ });
+ if (!codeNote) return;
+ await setRenderNote(parentNote, codeNote.noteId);
+ toast.showMessage(t("render.setup_sample_created"));
+}
From ec8b0a3801e00984c0b0e0c47e50c0f57d6d128d Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:54:34 +0200
Subject: [PATCH 093/247] feat(client/render_note): warning for disabled render
note
---
apps/client/src/services/attributes.ts | 29 ++++++++++++-------
.../src/translations/en/translation.json | 4 ++-
.../src/widgets/type_widgets/Render.tsx | 28 ++++++++++++++++--
.../type_widgets/helpers/SetupForm.css | 2 ++
packages/commons/src/lib/attribute_names.ts | 1 +
5 files changed, 50 insertions(+), 14 deletions(-)
diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts
index fd8a8b0f10..0ead073567 100644
--- a/apps/client/src/services/attributes.ts
+++ b/apps/client/src/services/attributes.ts
@@ -173,6 +173,8 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
*
* Note that this work for non-dangerous attributes as well.
*
+ * If there are multiple attributes with the same name, all of them will be toggled at the same time.
+ *
* @param note the note whose attribute to change.
* @param type the type of dangerous attribute (label or relation).
* @param name the name of the dangerous attribute.
@@ -180,19 +182,24 @@ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefin
* @returns a promise that will resolve when the request to the server completes.
*/
async function toggleDangerousAttribute(note: FNote, type: "label" | "relation", name: string, willEnable: boolean) {
- const attr = note.getOwnedAttribute(type, name) ?? note.getOwnedAttribute(type, `disabled:${name}`);
- if (!attr) return;
- const baseName = getNameWithoutDangerousPrefix(attr.name);
- const newName = willEnable ? baseName : `disabled:${baseName}`;
- if (newName === attr.name) return;
+ const attrs = [
+ ...note.getOwnedAttributes(type, name),
+ ...note.getOwnedAttributes(type, `disabled:${name}`)
+ ];
- // We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
- if (attr.type === "label") {
- await setLabel(note.noteId, newName, attr.value);
- } else {
- await setRelation(note.noteId, newName, attr.value);
+ for (const attr of attrs) {
+ const baseName = getNameWithoutDangerousPrefix(attr.name);
+ const newName = willEnable ? baseName : `disabled:${baseName}`;
+ if (newName === attr.name) return;
+
+ // We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
+ if (attr.type === "label") {
+ await setLabel(note.noteId, newName, attr.value);
+ } else {
+ await setRelation(note.noteId, newName, attr.value);
+ }
+ await removeAttributeById(note.noteId, attr.attributeId);
}
- await removeAttributeById(note.noteId, attr.attributeId);
}
/**
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index b90f79da4b..d2892456de 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1070,7 +1070,9 @@
"setup_title": "Display custom HTML or Preact JSX inside this note",
"setup_create_sample_preact": "Create sample note with Preact",
"setup_create_sample_html": "Create sample note with HTML",
- "setup_sample_created": "A sample note was created as a child note."
+ "setup_sample_created": "A sample note was created as a child note.",
+ "disabled_description": "This render notes comes from an external source. To protect you from malicious content, it is not enabled by default. Make sure you trust the source before enabling it.",
+ "disabled_button_enable": "Enable render note"
},
"web_view_setup": {
"title": "Create a live view of a webpage directly into Trilium",
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index 88fc75cd75..563a2342c6 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -8,7 +8,7 @@ import { t } from "../../services/i18n";
import note_create from "../../services/note_create";
import render from "../../services/render";
import toast from "../../services/toast";
-import { SplitButton } from "../react/Button";
+import Button, { SplitButton } from "../react/Button";
import FormGroup from "../react/FormGroup";
import { FormListItem } from "../react/FormList";
import { useNoteRelation, useTriliumEvent } from "../react/hooks";
@@ -17,6 +17,8 @@ import { refToJQuerySelector } from "../react/react_utils";
import SetupForm from "./helpers/SetupForm";
import { TypeWidgetProps } from "./type_widget";
+const HELP_PAGE = "HcABDtFCkbFN";
+
const PREACT_SAMPLE = /*js*/`\
export default function() {
return Hello world.
;
@@ -30,6 +32,11 @@ const HTML_SAMPLE = /*html*/`\
export default function Render(props: TypeWidgetProps) {
const { note } = props;
const [ renderNote ] = useNoteRelation(note, "renderNote");
+ const [ disabledRenderNote ] = useNoteRelation(note, "disabled:renderNote");
+
+ if (disabledRenderNote) {
+ return ;
+ }
if (!renderNote) {
return ;
@@ -76,11 +83,28 @@ function RenderContent({ note, noteContext, ntxId }: TypeWidgetProps) {
return
;
}
+function DisabledRender({ note }: TypeWidgetProps) {
+ return (
+
+ {t("render.disabled_description")}
+ attributes.toggleDangerousAttribute(note, "relation", "renderNote", true)}
+ primary
+ />
+
+ );
+}
+
function SetupRenderContent({ note }: TypeWidgetProps) {
return (
{
diff --git a/apps/client/src/widgets/type_widgets/helpers/SetupForm.css b/apps/client/src/widgets/type_widgets/helpers/SetupForm.css
index 205f3a4cde..1bd04b30c7 100644
--- a/apps/client/src/widgets/type_widgets/helpers/SetupForm.css
+++ b/apps/client/src/widgets/type_widgets/helpers/SetupForm.css
@@ -1,6 +1,8 @@
.setup-form {
height: 100%;
display: flex;
+ max-width: 600px;
+ margin: auto;
flex-direction: column;
justify-content: center;
padding-inline: 40px;
diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts
index 7a9534a5b7..10d7538875 100644
--- a/packages/commons/src/lib/attribute_names.ts
+++ b/packages/commons/src/lib/attribute_names.ts
@@ -80,6 +80,7 @@ type Relations = [
// Active content
"renderNote",
+ "disabled:renderNote",
// Launcher-specific
"target",
From 8b3e3c2c3a5df717a94bdbe0f36064dcc3581762 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 16:56:37 +0200
Subject: [PATCH 094/247] chore(react): not reacting properly to deleted
relations
---
apps/client/src/widgets/react/hooks.tsx | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx
index 8eb2cc0efd..c41e1e1a1c 100644
--- a/apps/client/src/widgets/react/hooks.tsx
+++ b/apps/client/src/widgets/react/hooks.tsx
@@ -551,7 +551,12 @@ export function useNoteRelation(note: FNote | undefined | null, relationName: Re
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
for (const attr of loadResults.getAttributeRows()) {
if (attr.type === "relation" && attr.name === relationName && attributes.isAffecting(attr, note)) {
- setRelationValue(attr.value ?? null);
+ if (!attr.isDeleted) {
+ setRelationValue(attr.value ?? null);
+ } else {
+ setRelationValue(null);
+ }
+ break;
}
}
});
@@ -601,6 +606,7 @@ export function useNoteLabel(note: FNote | undefined | null, labelName: FilterLa
} else {
setLabelValue(null);
}
+ break;
}
}
});
From a6e596a5e39c3efb08291027016c8facd92f192a Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 17:04:31 +0200
Subject: [PATCH 095/247] feat(client/render_note): display rendering errors
---
apps/client/src/services/render.ts | 16 +++++++++-------
.../src/widgets/type_widgets/Render.css | 9 ++++-----
.../src/widgets/type_widgets/Render.tsx | 19 ++++++++++++++++---
3 files changed, 29 insertions(+), 15 deletions(-)
diff --git a/apps/client/src/services/render.ts b/apps/client/src/services/render.ts
index adfd8a4949..f09d26532d 100644
--- a/apps/client/src/services/render.ts
+++ b/apps/client/src/services/render.ts
@@ -6,7 +6,7 @@ import bundleService, { type Bundle } from "./bundle.js";
import froca from "./froca.js";
import server from "./server.js";
-async function render(note: FNote, $el: JQuery) {
+async function render(note: FNote, $el: JQuery, onError?: (e: unknown) => void) {
const relations = note.getRelations("renderNote");
const renderNoteIds = relations.map((rel) => rel.value).filter((noteId) => noteId);
@@ -21,12 +21,14 @@ async function render(note: FNote, $el: JQuery) {
$scriptContainer.append(bundle.html);
// async so that scripts cannot block trilium execution
- bundleService.executeBundle(bundle, note, $scriptContainer).then(result => {
- // Render JSX
- if (bundle.html === "") {
- renderIfJsx(bundle, result, $el);
- }
- });
+ bundleService.executeBundle(bundle, note, $scriptContainer)
+ .catch(onError)
+ .then(result => {
+ // Render JSX
+ if (bundle.html === "") {
+ renderIfJsx(bundle, result, $el).catch(onError);
+ }
+ });
}
return renderNoteIds.length > 0;
diff --git a/apps/client/src/widgets/type_widgets/Render.css b/apps/client/src/widgets/type_widgets/Render.css
index 2595da0d84..7597b47cd2 100644
--- a/apps/client/src/widgets/type_widgets/Render.css
+++ b/apps/client/src/widgets/type_widgets/Render.css
@@ -1,8 +1,7 @@
.note-detail-render {
position: relative;
-}
-.note-detail-render .note-detail-render-help {
- margin: 50px;
- padding: 20px;
-}
\ No newline at end of file
+ &>.admonition {
+ margin: 1em;
+ }
+}
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index 563a2342c6..4d4fd4cd8e 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -1,6 +1,6 @@
import "./Render.css";
-import { useEffect, useRef } from "preact/hooks";
+import { useEffect, useRef, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
@@ -8,6 +8,8 @@ import { t } from "../../services/i18n";
import note_create from "../../services/note_create";
import render from "../../services/render";
import toast from "../../services/toast";
+import { getErrorMessage } from "../../services/utils";
+import Admonition from "../react/Admonition";
import Button, { SplitButton } from "../react/Button";
import FormGroup from "../react/FormGroup";
import { FormListItem } from "../react/FormList";
@@ -47,10 +49,12 @@ export default function Render(props: TypeWidgetProps) {
function RenderContent({ note, noteContext, ntxId }: TypeWidgetProps) {
const contentRef = useRef(null);
+ const [ error, setError ] = useState(null);
function refresh() {
if (!contentRef) return;
- render.render(note, refToJQuerySelector(contentRef));
+ setError(null);
+ render.render(note, refToJQuerySelector(contentRef), setError);
}
useEffect(refresh, [ note ]);
@@ -80,7 +84,16 @@ function RenderContent({ note, noteContext, ntxId }: TypeWidgetProps) {
resolve(refToJQuerySelector(contentRef));
});
- return
;
+ return (
+ <>
+ {error && (
+
+ {getErrorMessage(error)}
+
+ )}
+
+ >
+ );
}
function DisabledRender({ note }: TypeWidgetProps) {
From 7a534c3ea76029998ea301738956b15e744a4440 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 17:06:23 +0200
Subject: [PATCH 096/247] chore(client/active_badges): disable toggle for web
view & render note
---
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 978fe7ef03..0ffad6c1fe 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -231,11 +231,9 @@ function useActiveContentInfo(note: FNote | null | undefined) {
if (note.type === "render") {
type = "renderNote";
isEnabled = note.hasRelation("renderNote");
- canToggleEnabled = note.hasRelation("renderNote") || note.hasRelation("disabled:renderNote");
} else if (note.type === "webView") {
type = "webView";
isEnabled = note.hasLabel("webViewSrc");
- canToggleEnabled = note.hasLabelOrDisabled("webViewSrc");
} else if (note.type === "code" && note.mime === "application/javascript;env=backend") {
type = "backendScript";
for (const backendLabel of [ "run", "customRequestHandler", "customResourceProvider" ]) {
From b0a3d5427654a0d70cd91a0039a21a2dc2234678 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 17:15:51 +0200
Subject: [PATCH 097/247] chore(client): address requested changes
---
apps/client/src/services/attributes.ts | 2 +-
.../src/widgets/layout/ActiveContentBadges.tsx | 12 ++++--------
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts
index 0ead073567..77558c9464 100644
--- a/apps/client/src/services/attributes.ts
+++ b/apps/client/src/services/attributes.ts
@@ -190,7 +190,7 @@ async function toggleDangerousAttribute(note: FNote, type: "label" | "relation",
for (const attr of attrs) {
const baseName = getNameWithoutDangerousPrefix(attr.name);
const newName = willEnable ? baseName : `disabled:${baseName}`;
- if (newName === attr.name) return;
+ if (newName === attr.name) continue;
// We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
if (attr.type === "label") {
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 0ffad6c1fe..3049e46243 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -203,14 +203,10 @@ function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentI
switchOffTooltip={t("active_content_badges.toggle_tooltip_disable_tooltip", { type: title })}
switchOnTooltip={t("active_content_badges.toggle_tooltip_enable_tooltip", { type: title })}
onChange={async (willEnable) => {
- const attrs = note.getOwnedAttributes()
- .filter(attr => {
- if (attr.isInheritable) return false;
- const baseName = attributes.getNameWithoutDangerousPrefix(attr.name);
- return DANGEROUS_ATTRIBUTES.some(item => item.name === baseName && item.type === attr.type);
- });
-
- await Promise.all(attrs.map(a => attributes.toggleDangerousAttribute(note, a.type, a.name, willEnable)));
+ await Promise.all(note.getOwnedAttributes()
+ .map(attr => ({ name: attributes.getNameWithoutDangerousPrefix(attr.name), type: attr.type }))
+ .filter(({ name, type }) => DANGEROUS_ATTRIBUTES.some(item => item.name === name && item.type === type))
+ .map(({ name, type }) => attributes.toggleDangerousAttribute(note, type, name, willEnable)));
}}
/>;
}
From 3db2c910e072b2ba509638c3d24730b14abad849 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 17:32:34 +0200
Subject: [PATCH 098/247] fix(client/active_badges): wrong enable + missing
toggle for icon pack & custom CSS
---
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 3 +++
1 file changed, 3 insertions(+)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 3049e46243..fcccd00f29 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -251,10 +251,13 @@ function useActiveContentInfo(note: FNote | null | undefined) {
for (const labelToCheck of activeContentLabels) {
if (note.hasLabel(labelToCheck)) {
type = labelToCheck;
+ isEnabled = true;
+ canToggleEnabled = true;
break;
} else if (note.hasLabel(`disabled:${labelToCheck}`)) {
type = labelToCheck;
isEnabled = false;
+ canToggleEnabled = true;
break;
}
}
From 9b2876a8ff82f8b25a7c0e4753e7f1d7781764d6 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 17:47:28 +0200
Subject: [PATCH 099/247] docs(user): mention custom tiles & hiding labels for
geomap
---
.../Active content.html | 200 +++---
.../User Guide/Collections/Geo Map.html | 633 +++++++++---------
.../User Guide/Note Types/Render Note.html | 32 +-
.../User Guide/Scripting/Backend scripts.html | 20 +-
.../Scripting/Backend scripts/Events.html | 256 ++++---
.../User Guide/Scripting/Frontend Basics.html | 85 ++-
.../Frontend Basics/Custom Widgets.html | 153 +++--
.../Frontend Basics/Launch Bar Widgets.html | 17 +-
.../Developer Guide/Documentation.md | 2 +-
docs/User Guide/!!!meta.json | 502 +++++++-------
docs/User Guide/User Guide.md | 2 +-
.../User Guide/Collections/Geo Map.md | 31 +-
12 files changed, 974 insertions(+), 959 deletions(-)
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Active content.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Active content.html
index f45db3c93d..cc2294d9ef 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Active content.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Active content.html
@@ -5,116 +5,109 @@
Active content problem of safety, especially when this active content
comes from a third-party such as if it is downloaded from a website and
then imported into Trilium.
-When importing .zip
- archives into Trilium, safe mode is active by default which will
- try to prevent untrusted code from executing. For example, a custom widget needs
- the #widget label in
- order to function; safe import works by renaming that label to #disabled:widget.
+When importing .zip archives into Trilium, safe mode is
+ active by default which will try to prevent untrusted code from executing.
+ For example, a custom widget needs the
+ #widget label in order to function;
+ safe import works by renaming that label to #disabled:widget.
Safe mode
Sometimes active content can cause issues with the UI or the server, preventing
- it from functioning properly. Safe mode allows
+ it from functioning properly. Safe mode allows
starting Trilium in such a way that active content is not loaded by default
at start-up, allowing the user to fix the problematic scripts or widgets.
Types of active content
These are the types of active content in Trilium, along with a few examples
of what untrusted content of that type could cause:
-
-
-
-
-
-
-
-
-
-
- Name
- Disabled on a safe import
-
- Description
- Potential risks of untrusted code
-
-
-
-
- Front-end scripts
-
- Yes
- Allow running arbitrary code on the client (UI) of Trilium, which can
- alter the user interface.
- A malicious script can execute server-side code, access un-encrypted notes
- or change their contents.
-
-
- Custom Widgets
-
- Yes
- Can add new UI features to Trilium, for example by adding a new section
- in the Right Sidebar .
- The UI can be altered in such a way that it can be used to extract sensitive
- information or it can simply cause the application to crash.
-
-
- Backend scripts
-
- Yes
- Can run custom code on the server of Trilium (Node.js environment), with
- full access to the notes and the database.
- Has access to all the unencrypted notes, but with full access to the database
- it can completely destroy the data. It also has access to execute other
- applications or alter the files and folders on the server).
-
-
- Web View
-
- Yes
- Displays a website inside a note.
- Can point to a phishing website which can collect the data (for example
- on a log in page).
-
-
- Render Note
-
- Yes
- Renders custom content inside a note, such as a dashboard or a new editor
- that is not officially supported by Trilium.
- Can affect the UI similar to front-end scripts or custom widgets since
- the scripts are not completely encapsulated, or they can act similar to
- a web view where they can collect data entered by the user.
-
-
- Custom app-wide CSS
-
- No
- Can alter the layout and style of the UI using CSS, applied regardless
- of theme.
- Generally less problematic than the rest of active content, but a badly
- written CSS can affect the layout of the application, requiring the use
- of Safe mode to
- be able to use the application.
-
-
- Custom themes
-
- No
- Can change the style of the entire UI.
- Similar to custom app-wide CSS.
-
-
- Icon Packs
-
- No
- Introduces new icons that can be used for notes.
- Generally are more contained and less prone to cause issues, but they
- can cause performance issues (for example if the icon pack has millions
- of icons in it).
-
-
-
-
+
+
+
+ Name
+ Disabled on a safe import
+
+ Description
+ Potential risks of untrusted code
+
+
+
+
+ Front-end scripts
+
+ Yes
+ Allow running arbitrary code on the client (UI) of Trilium, which can
+ alter the user interface.
+ A malicious script can execute server-side code, access un-encrypted notes
+ or change their contents.
+
+
+ Custom Widgets
+
+ Yes
+ Can add new UI features to Trilium, for example by adding a new section
+ in the Right Sidebar .
+ The UI can be altered in such a way that it can be used to extract sensitive
+ information or it can simply cause the application to crash.
+
+
+ Backend scripts
+
+ Yes
+ Can run custom code on the server of Trilium (Node.js environment), with
+ full access to the notes and the database.
+ Has access to all the unencrypted notes, but with full access to the database
+ it can completely destroy the data. It also has access to execute other
+ applications or alter the files and folders on the server).
+
+
+ Web View
+
+ Yes
+ Displays a website inside a note.
+ Can point to a phishing website which can collect the data (for example
+ on a log in page).
+
+
+ Render Note
+
+ Yes
+ Renders custom content inside a note, such as a dashboard or a new editor
+ that is not officially supported by Trilium.
+ Can affect the UI similar to front-end scripts or custom widgets since
+ the scripts are not completely encapsulated, or they can act similar to
+ a web view where they can collect data entered by the user.
+
+
+ Custom app-wide CSS
+
+ No
+ Can alter the layout and style of the UI using CSS, applied regardless
+ of theme.
+ Generally less problematic than the rest of active content, but a badly
+ written CSS can affect the layout of the application, requiring the use
+ of Safe mode to
+ be able to use the application.
+
+
+ Custom themes
+
+ No
+ Can change the style of the entire UI.
+ Similar to custom app-wide CSS.
+
+
+ Icon Packs
+
+ No
+ Introduces new icons that can be used for notes.
+ Generally are more contained and less prone to cause issues, but they
+ can cause performance issues (for example if the icon pack has millions
+ of icons in it).
+
+
+
+
Active content badge
-Starting with v0.102.0, on the New Layout a
+
Starting with v0.102.0, on the New Layout a
badge will be displayed near the note title, indicating that an active
content is detected. Clicking the badge will reveal a menu with various
options related to that content type, for example to open the documentation
@@ -122,5 +115,4 @@ style="width:100%;">
For some active content types, such as backend scripts with custom triggering
conditions a toggle button will appear. This makes it possible to easily
disable scripts or widgets, but also to re-enable them if an import was
- made with safe mode active.
-
\ No newline at end of file
+ made with safe mode active.
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Geo Map.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Geo Map.html
index cfee0ce25f..e86e58f98a 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Geo Map.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Collections/Geo Map.html
@@ -1,9 +1,8 @@
Creating a new geo map
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
-
- Right click on any note on the note tree and select Insert child note → Geo Map (beta) .
-
-
- 2
-
-
-
-
-
- By default the map will be empty and will show the entire world.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+ Right click on any note on the note tree and select Insert child note → Geo Map (beta) .
+
+
+ 2
+
+
+
+
+
+ By default the map will be empty and will show the entire world.
+
+
+
+
Repositioning the map
Click and drag the map in order to move across the map.
@@ -55,59 +55,60 @@
restored when visiting again the note.
Adding a marker using the map
Adding a new note using the plus button
-
-
-
-
-
-
-
-
-
-
- 1
- To create a marker, first navigate to the desired point on the map. Then
- press the
- button in the Floating buttons (top-right)
- area.
-
- If the button is not visible, make sure the button section is visible
- by pressing the chevron button (
- ) in the top-right of the map.
-
-
-
- 2
-
-
-
- Once pressed, the map will enter in the insert mode, as illustrated by
- the notification.
-
- Simply click the point on the map where to place the marker, or the Escape
- key to cancel.
-
-
- 3
-
-
-
- Enter the name of the marker/note to be created.
-
-
- 4
-
-
-
- Once confirmed, the marker will show up on the map and it will also be
- displayed as a child note of the map.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ To create a marker, first navigate to the desired point on the map. Then
+ press the
+ button in the Floating buttons (top-right)
+ area.
+
+ If the button is not visible, make sure the button section is visible
+ by pressing the chevron button (
+ ) in the top-right of the map.
+
+
+
+ 2
+
+
+
+ Once pressed, the map will enter in the insert mode, as illustrated by
+ the notification.
+
+ Simply click the point on the map where to place the marker, or the Escape
+ key to cancel.
+
+
+ 3
+
+
+
+ Enter the name of the marker/note to be created.
+
+
+ 4
+
+
+
+ Once confirmed, the marker will show up on the map and it will also be
+ displayed as a child note of the map.
+
+
+
+
Adding a new note using the contextual menu
Right click anywhere on the map, where to place the newly created marker
@@ -120,15 +121,18 @@
Adding an existing note on note from the note tree
Select the desired note in the Note Tree .
- Hold the mouse on the note and drag it to the map to the desired location.
- The map should be updated with the new marker.
+ Hold the mouse on the note and drag it to the map to the desired location.
+ The map should be updated with the new marker.
This works for:
Notes that are not part of the geo map, case in which a clone will
be created.
Notes that are a child of the geo map but not yet positioned on the map.
- Notes that are a child of the geo map and also positioned, case in which
+ Notes that are a child of the geo map and also positioned, case in which
the marker will be relocated to the new position.
@@ -138,8 +142,10 @@
How the location of the markers is stored
The location of a marker is stored in the #geolocation attribute
of the child notes:
-
+
+
+
This value can be added manually if needed. The value of the attribute
is made up of the latitude and longitude separated by a comma.
Repositioning markers
@@ -160,7 +166,8 @@ width="1288" height="278">
Middle-clicking the marker will open the note in a new tab.
Right-clicking the marker will open a contextual menu (as described below).
- If the map is in read-only mode, clicking on a marker will open a
+ If the map is in read-only mode, clicking on a marker will open a
Quick edit popup for the corresponding note.
@@ -206,211 +213,239 @@ width="1288" height="278">
The value of the attribute is made up of the latitude and longitude separated
by a comma.
Adding from Google Maps
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
-
- Go to Google Maps on the web and look for a desired location, right click
- on it and a context menu will show up.
-
- Simply click on the first item displaying the coordinates and they will
- be copied to clipboard.
-
- Then paste the value inside the text box into the #geolocation attribute
- of a child note of the map (don't forget to surround the value with a
- "character).
-
-
- 2
-
-
-
-
-
- In Trilium, create a child note under the map.
-
-
- 3
-
-
-
-
-
- And then go to Owned Attributes and type #geolocation=",
- then paste from the clipboard as-is and then add the ending " character.
- Press Enter to confirm and the map should now be updated to contain the
- new note.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+ Go to Google Maps on the web and look for a desired location, right click
+ on it and a context menu will show up.
+
+ Simply click on the first item displaying the coordinates and they will
+ be copied to clipboard.
+
+ Then paste the value inside the text box into the #geolocation attribute
+ of a child note of the map (don't forget to surround the value with a
+ "character).
+
+
+ 2
+
+
+
+
+
+ In Trilium, create a child note under the map.
+
+
+ 3
+
+
+
+
+
+ And then go to Owned Attributes and type #geolocation=",
+ then paste from the clipboard as-is and then add the ending " character.
+ Press Enter to confirm and the map should now be updated to contain the
+ new note.
+
+
+
+
Adding from OpenStreetMap
Similarly to the Google Maps approach:
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
- Go to any location on openstreetmap.org and right click to bring up the
- context menu. Select the “Show address” item.
-
-
- 2
-
-
-
- The address will be visible in the top-left of the screen, in the place
- of the search bar.
-
- Select the coordinates and copy them into the clipboard.
-
-
- 3
-
-
-
- Simply paste the value inside the text box into the #geolocation attribute
- of a child note of the map and then it should be displayed on the map.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+ Go to any location on openstreetmap.org and right click to bring up the
+ context menu. Select the “Show address” item.
+
+
+ 2
+
+
+
+ The address will be visible in the top-left of the screen, in the place
+ of the search bar.
+
+ Select the coordinates and copy them into the clipboard.
+
+
+ 3
+
+
+
+ Simply paste the value inside the text box into the #geolocation attribute
+ of a child note of the map and then it should be displayed on the map.
+
+
+
+
Adding GPS tracks (.gpx)
Trilium has basic support for displaying GPS tracks on the geo map.
-
-
-
-
-
-
-
-
-
-
- 1
-
-
-
-
-
- To add a track, simply drag & drop a .gpx file inside the geo map
- in the note tree.
-
-
- 2
-
-
-
-
-
- In order for the file to be recognized as a GPS track, it needs to show
- up as application/gpx+xml in the File type field.
-
-
- 3
-
-
-
-
-
- When going back to the map, the track should now be visible.
-
- The start and end points of the track are indicated by the two blue markers.
-
-
-
-
- The starting point of the track will be displayed as a marker, with the
- name of the note underneath. The start marker will also respect the icon
- and the color of the note. The end marker
- is displayed with a distinct icon.
- If the GPX contains waypoints, they will also be displayed. If they have
- a name, it is displayed when hovering over it with the mouse.
-
-Read-only mode
-When a map is in read-only all editing features will be disabled such
- as:
-
- The add button in the Floating buttons .
- Dragging markers.
- Editing from the contextual menu (removing locations or adding new items).
-
-To enable read-only mode simply press the Lock icon from the
- Floating buttons . To disable it, press the button again.
-Configuration
-Map Style
-The styling of the map can be adjusted in the Collection Properties tab
- in the Ribbon or
- manually via the #map:style attribute.
-The geo map comes with two different types of styles:
-
- Raster styles
-
- For these styles the map is represented as a grid of images at different
- zoom levels. This is the traditional way OpenStreetMap used to work.
- Zoom is slightly restricted.
- Currently, the only raster theme is the original OpenStreetMap style.
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+
+ To add a track, simply drag & drop a .gpx file inside the geo map
+ in the note tree.
+
+
+ 2
+
+
+
+
+
+ In order for the file to be recognized as a GPS track, it needs to show
+ up as application/gpx+xml in the File type field.
+
+
+ 3
+
+
+
+
+
+ When going back to the map, the track should now be visible.
+
+ The start and end points of the track are indicated by the two blue markers.
+
+
+
+
+
+ The starting point of the track will be displayed as a marker, with the
+ name of the note underneath. The start marker will also respect the icon
+ and the color of the note. The end marker
+ is displayed with a distinct icon.
+ If the GPX contains waypoints, they will also be displayed. If they have
+ a name, it is displayed when hovering over it with the mouse.
+
+ Read-only mode
+ When a map is in read-only all editing features will be disabled such
+ as:
+
+ The add button in the Floating buttons .
+ Dragging markers.
+ Editing from the contextual menu (removing locations or adding new items).
+
+ To enable read-only mode simply press the Lock icon from the
+ Floating buttons . To disable it, press the button again.
+ Configuration
+ Map Style
+ The styling of the map can be adjusted in the Collection Properties (above
+ the map, by pressing on the Gear icon) or manually via the #map:style attribute.
+ The geo map comes with two different types of styles:
+
+ Raster styles
+
+ For these styles the map is represented as a grid of images at different
+ zoom levels. This is the traditional way OpenStreetMap used to work.
+ Zoom is slightly restricted.
+ Currently, the only raster theme is the original OpenStreetMap style.
-
- Vector styles
-
- Vector styles are not represented as images, but as geometrical shapes.
- This makes the rendering much smoother, especially when zooming and looking
- at the building edges, for example.
- The map can be zoomed in much further.
- These come both in a light and a dark version.
- The vector styles come from VersaTiles ,
- a free and open-source project providing map tiles based on OpenStreetMap.
-
-
-
-
-Scale
-Activating this option via the Ribbon or
- manually via #map:scale will display an indicator
- in the bottom-left of the scale of the map.
-Troubleshooting
-
-
-
-
-Grid-like artifacts on the map
-This occurs if the application is not at 100% zoom which causes the pixels
- of the map to not render correctly due to fractional scaling. The only
- possible solution is to set the UI zoom at 100% (default keyboard shortcut
- is Ctrl +0 ).
\ No newline at end of file
+
+ Vector styles
+
+ Vector styles are not represented as images, but as geometrical shapes.
+ This makes the rendering much smoother, especially when zooming and looking
+ at the building edges, for example.
+ The map can be zoomed in much further.
+ These come both in a light and a dark version.
+ The vector styles come from VersaTiles ,
+ a free and open-source project providing map tiles based on OpenStreetMap.
+
+
+
+ Custom map style / tiles
+ Starting with v0.102.0 it is possible to use custom tile sets, but only
+ in raster format.
+ To do so, manually set the #map:style
+ label to the URL of the tile set. For example, to use Esri.NatGeoWorldMap,
+ set the value to https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}.
+
+
+ For a list of tile sets, see the Leaflet Providers preview page.
+ Select a desired tile set and just copy the URL from the Plain JavaScript example.
+
+ Custom vector map support is planned, but not yet implemented.
+ Other options
+ The following options can be configured either via the Collection properties
+ pane above the map, by clicking on the settings (Gear icon). Alternatively,
+ each of these options also have a corresponding label that
+ can be set manually.
+
+
+ Scale, which illustrates the scale of the map in kilometers and miles
+ in the bottom-left of the map.
+
+
+ The name of the markers is displayed by default underneath the pin on
+ the map. Since v0.102.0, it is possible to hide these labels which increases
+ the performance and decreases clutter when there are many markers on the
+ map.
+
+
+ Troubleshooting
+
+
+
+ Grid-like artifacts on the map
+ This occurs if the application is not at 100% zoom which causes the pixels
+ of the map to not render correctly due to fractional scaling. The only
+ possible solution is to set the UI zoom at 100% (default keyboard shortcut
+ is Ctrl +0 ).
\ No newline at end of file
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 1bac949e3e..7e6b8cddbe 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
@@ -2,7 +2,7 @@
-Render Note is a special case of front-end scripting which
+
Render Note is a special case of front-end scripting which
allows rendering custom content inside a note. This makes it possible to
create custom dashboards, or to use a custom note editor.
The content can either be a vanilla HTML, or Preact JSX.
@@ -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 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());
</>
);
}
-
- 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.
+
+
+ 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.
+
Refreshing the note
It's possible to refresh the note via:
Examples
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html
index 80cbebbfeb..8691c4c054 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html
@@ -1,11 +1,11 @@
-Unlike front-end scripts which
- run on the client / browser-side, back-end scripts run directly on the
- Node.js environment of the Trilium server.
+Unlike front-end scripts which run on the
+ client / browser-side, back-end scripts run directly on the Node.js environment
+ of the Trilium server.
Back-end scripts can be used both on a Server Installation (where
- it will run on the device the server is running on), or on the
- Desktop Installation (where it will run on the PC).
+ href="#root/_help_WOcw2SLH6tbX">Server Installation (where it will run
+ on the device the server is running on), or on the Desktop Installation (where it will
+ run on the PC).
Advantages of backend scripts
The benefit of backend scripts is that they can be pretty powerful, for
example to have access to the underlying system, for example it can read
@@ -14,7 +14,7 @@
access to the notes since the information about them is already loaded
in memory. Whereas on the client, notes have to be manually loaded first.
Creating a backend script
-Create a new Code note
+
Create a new Code note
and select the language JS backend .
Running backend scripts
Backend scripts can be either run manually (via the Execute button on
@@ -22,8 +22,8 @@
In addition, scripts can be run automatically when the server starts up,
on a fixed time interval or when a certain event occurs (such as an attribute
being modified). For more information, see the dedicated Events page.
+ href="#root/_help_GPERMystNGTB">Events page.
Script API
Trilium exposes a set of APIs that can be directly consumed by scripts,
under the api object. For a reference of
- this API, see Backend API .
\ No newline at end of file
+ this API, see Backend API .
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html
index 3e54232a5d..de4beda67f 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts/Events.html
@@ -5,139 +5,137 @@
Global events are attached to the script note via label. Simply create
e.g. "run" label with some of these values and script note will be executed
once the event occurs.
-
-
-
-
- Label
- Description
-
-
-
-
- run
-
-
- Defines on which events script should run. Possible values are:
-
- backendStartup - when Trilium backend starts
- up
- hourly - run once an hour. You can use
- additional label runAtHour to specify at
- which hour, on the back-end.
- daily - run once a day, on the back-end
-
-
-
-
- runOnInstance
-
- Specifies that the script should only run on a particular Trilium instance .
-
-
- runAtHour
-
- On which hour should this run. Should be used together with #run=hourly.
- Can be defined multiple times for more runs during the day.
-
-
-
-
+
+
+
+ Label
+ Description
+
+
+
+
+ run
+
+
+ Defines on which events script should run. Possible values are:
+
+ backendStartup - when Trilium backend starts
+ up
+ hourly - run once an hour. You can use
+ additional label runAtHour to specify at
+ which hour, on the back-end.
+ daily - run once a day, on the back-end
+
+
+
+
+ runOnInstance
+
+ Specifies that the script should only run on a particular Trilium instance .
+
+
+ runAtHour
+
+ On which hour should this run. Should be used together with #run=hourly.
+ Can be defined multiple times for more runs during the day.
+
+
+
+
Entity events
Other events are bound to some entity, these are defined as relations -
meaning that script is triggered only if note has this script attached
to it through relations (or it can inherit it).
-
-
-
-
- Relation
- Trigger condition
- Origin entity (see below)
-
-
-
-
- runOnNoteCreation
-
- executes when note is created on backend. Use this relation if you want
- to run the script for all notes created under a specific subtree. In that
- case, create it on the subtree root note and make it inheritable. A new
- note created within the subtree (any depth) will trigger the script.
- The BNote that got created.
-
-
- runOnChildNoteCreation
-
- executes when new note is created under the note where this relation is
- defined
- The BNote of the child that got created.
-
-
- runOnNoteTitleChange
-
- executes when note title is changed (includes note creation as well)
- The BNote of the note whose title got changed.
-
-
- runOnNoteContentChange
-
- executes when note content is changed (includes note creation as well).
- The BNote of the note whose content got
- changed.
-
-
- runOnNoteChange
-
- executes when note is changed (includes note creation as well). Does not
- include content changes
- The BNote of the note that got changed.
-
-
- runOnNoteDeletion
-
- executes when note is being deleted
- The BNote of the note that got (soft) deleted.
-
-
- runOnBranchCreation
-
- executes when a branch is created. Branch is a link between parent note
- and child note and is created e.g. when cloning or moving note.
- The BBranch that got created.
-
-
- runOnBranchChange
-
- executes when a branch is updated. (since v0.62)
- The BBranch that got changed.
-
-
- runOnBranchDeletion
-
- executes when a branch is deleted. Branch is a link between parent note
- and child note and is deleted e.g. when moving note (old branch/link is
- deleted).
- The BBranch that got (soft) deleted.
-
-
- runOnAttributeCreation
-
- executes when new attribute is created for the note which defines this
- relation
- The BAttribute that got created.
-
-
- runOnAttributeChange
-
- executes when the attribute is changed of a note which defines this relation.
- This is triggered also when the attribute is deleted
- The BAttribute that got changed.
-
-
-
-
+
+
+
+ Relation
+ Trigger condition
+ Origin entity (see below)
+
+
+
+
+ runOnNoteCreation
+
+ executes when note is created on backend. Use this relation if you want
+ to run the script for all notes created under a specific subtree. In that
+ case, create it on the subtree root note and make it inheritable. A new
+ note created within the subtree (any depth) will trigger the script.
+ The BNote that got created.
+
+
+ runOnChildNoteCreation
+
+ executes when new note is created under the note where this relation is
+ defined
+ The BNote of the child that got created.
+
+
+ runOnNoteTitleChange
+
+ executes when note title is changed (includes note creation as well)
+ The BNote of the note whose title got changed.
+
+
+ runOnNoteContentChange
+
+ executes when note content is changed (includes note creation as well).
+ The BNote of the note whose content got
+ changed.
+
+
+ runOnNoteChange
+
+ executes when note is changed (includes note creation as well). Does not
+ include content changes
+ The BNote of the note that got changed.
+
+
+ runOnNoteDeletion
+
+ executes when note is being deleted
+ The BNote of the note that got (soft) deleted.
+
+
+ runOnBranchCreation
+
+ executes when a branch is created. Branch is a link between parent note
+ and child note and is created e.g. when cloning or moving note.
+ The BBranch that got created.
+
+
+ runOnBranchChange
+
+ executes when a branch is updated. (since v0.62)
+ The BBranch that got changed.
+
+
+ runOnBranchDeletion
+
+ executes when a branch is deleted. Branch is a link between parent note
+ and child note and is deleted e.g. when moving note (old branch/link is
+ deleted).
+ The BBranch that got (soft) deleted.
+
+
+ runOnAttributeCreation
+
+ executes when new attribute is created for the note which defines this
+ relation
+ The BAttribute that got created.
+
+
+ runOnAttributeChange
+
+ executes when the attribute is changed of a note which defines this relation.
+ This is triggered also when the attribute is deleted
+ The BAttribute that got changed.
+
+
+
+
Origin entity
When a script is run by an event such as the ones described above,
Front-end scripts are custom JavaScript notes that are run on the client
(browser environment)
There are four flavors of front-end scripts:
-
-
-
-
-
-
-
-
- Regular scripts
- These are run with the current app and note context. These can be run
- either manually or automatically on start-up.
-
-
- Custom Widgets
-
- These can introduce new UI elements in various positions, such as near
- the Note Tree ,
- content area or even the Right Sidebar .
-
-
- Launch Bar Widgets
-
- Similar to Custom Widgets ,
- but dedicated to the Launch Bar .
- These can simply introduce new buttons or graphical elements to the bar.
-
-
- Render Note
-
- This allows rendering custom content inside a note, using either HTML
- or Preact JSX.
-
-
-
-
+
+
+
+
+
+
+
+
+
+ Regular scripts
+ These are run with the current app and note context. These can be run
+ either manually or automatically on start-up.
+
+
+ Custom Widgets
+
+ These can introduce new UI elements in various positions, such as near
+ the Note Tree ,
+ content area or even the Right Sidebar .
+
+
+ Launch Bar Widgets
+
+ Similar to Custom Widgets ,
+ but dedicated to the Launch Bar .
+ These can simply introduce new buttons or graphical elements to the bar.
+
+
+ Render Note
+
+ This allows rendering custom content inside a note, using either HTML
+ or Preact JSX.
+
+
+
For more advanced behaviors that do not require a user interface (e.g.
- batch modifying notes), see Backend scripts .
+ batch modifying notes), see Backend scripts .
Scripts
Scripts don't have any special requirements. They can be run manually
using the Execute button on the code note or they can be run automatically;
- to do so, set the run label to
+ to do so, set the run label to
either:
-For more information, see Custom Widgets .
+For more information, see Custom Widgets .
Script API
The front-end API of Trilium is available to all scripts running in the
front-end context as global variable api.
- For a reference of the API, see Frontend API .
+ For a reference of the API, see Frontend API .
Tutorial
For more information on building widgets, take a look at Widget Basics .
\ No newline at end of file
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html
index 416c15c107..dc99ba8553 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html
@@ -20,15 +20,14 @@
Creating a custom widget
Create a Code note.
- Set the language to:
+ Set the language to:
JavaScript (frontend) for legacy widgets using jQuery.
JSX for Preact widgets. You might need to go to Options → Code to enable
the language first.
-
- Apply the #widget label .
+
+ Apply the #widget label .
Getting started with a simple example
Let's start by creating a widget that shows a message near the content
@@ -62,81 +61,79 @@ export default defineWidget({
should appear underneath the content area.
Widget location (parent widget)
A widget can be placed in one of the following sections of the applications:
-
-
-
-
-
-
-
-
-
-
- Value for parentWidget
-
- Description
- Sample widget
- Special requirements
-
-
-
-
- left-pane
-
- Appears within the same pane that holds the Note Tree .
- Same as above, with only a different parentWidget.
- None.
-
-
- center-pane
-
- In the content area. If a split is open, the widget will span all of the
- splits.
- See example above.
- None.
-
-
- note-detail-pane
-
-
- In the content area, inside the note detail area. If a split is open,
- the widget will be contained inside the split.
- This is ideal if the widget is note-specific.
-
- Note context aware widget
-
-
-
- The widget must export a class and not an
- instance of the class (e.g. no new) because
- it needs to be multiplied for each note, so that splits work correctly.
- Since the class is exported instead of an
- instance, the parentWidget getter must be
- static, otherwise the widget is ignored.
-
-
-
-
- right-pane
-
- In the Right Sidebar ,
- as a dedicated section.
- Right pane widget
-
-
-
- Although not mandatory, it's best to use a RightPanelWidget instead
- of a BasicWidget or a NoteContextAwareWidget.
-
-
-
-
+
+
+
+
+
+
+
+
+
+ Value for parentWidget
+
+ Description
+ Sample widget
+ Special requirements
+
+
+
+
+ left-pane
+
+ Appears within the same pane that holds the Note Tree .
+ Same as above, with only a different parentWidget.
+ None.
+
+
+ center-pane
+
+ In the content area. If a split is open, the widget will span all of the
+ splits.
+ See example above.
+ None.
+
+
+ note-detail-pane
+
+
+ In the content area, inside the note detail area. If a split is open,
+ the widget will be contained inside the split.
+ This is ideal if the widget is note-specific.
+
+ Note context aware widget
+
+
+
+ The widget must export a class and not an
+ instance of the class (e.g. no new) because
+ it needs to be multiplied for each note, so that splits work correctly.
+ Since the class is exported instead of an
+ instance, the parentWidget getter must be
+ static, otherwise the widget is ignored.
+
+
+
+
+ right-pane
+
+ In the Right Sidebar ,
+ as a dedicated section.
+ Right pane widget
+
+
+
+ Although not mandatory, it's best to use a RightPanelWidget instead
+ of a BasicWidget or a NoteContextAwareWidget.
+
+
+
+
-
To position the widget somewhere else, just change the value passed to
get parentWidget()for legacy widgets or the parent field
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html
index c2b91ba4ed..acf5a50e35 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets.html
@@ -20,16 +20,11 @@
Don't set #widget, as that attribute is
reserved for Custom Widgets .
- In the Global menu ,
+ In the Global menu ,
select Configure launchbar .
- In the Visible Launchers section, select Add a custom widget .
- Give the newly created launcher a name (and optionally a name).
- In the Promoted Attributes section,
- modify the widget field to point to the newly created note.
- Refresh the
- UI.
+ In the Visible Launchers section, select Add a custom widget .
+ Give the newly created launcher a name (and optionally a name).
+ In the Promoted Attributes section,
+ modify the widget field to point to the newly created note.
+ Refresh the UI.
\ 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 6631466e38..8e669ac866 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 817f450e42..f9136647f9 100644
--- a/docs/User Guide/!!!meta.json
+++ b/docs/User Guide/!!!meta.json
@@ -6459,6 +6459,97 @@
"type": "text",
"mime": "text/html",
"attributes": [
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "mHbBMPDPkVV5",
+ "isInheritable": false,
+ "position": 10
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "MgibgPcfeuGz",
+ "isInheritable": false,
+ "position": 20
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HI6GBBIduIgv",
+ "isInheritable": false,
+ "position": 30
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "64ZTlUPgEPtW",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "yIhgI5H7A2Sm",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "RnaPdbciOfeq",
+ "isInheritable": false,
+ "position": 60
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "SPirpZypehBG",
+ "isInheritable": false,
+ "position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "1vHRoWCEjj0L",
+ "isInheritable": false,
+ "position": 80
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HcABDtFCkbFN",
+ "isInheritable": false,
+ "position": 90
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "AlhDUqhENtH7",
+ "isInheritable": false,
+ "position": 100
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "pKK96zzmvBGf",
+ "isInheritable": false,
+ "position": 110
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "gOKqSJgXLcIj",
+ "isInheritable": false,
+ "position": 120
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "IjZS7iK5EXtb",
+ "isInheritable": false,
+ "position": 130
+ },
{
"type": "label",
"name": "iconClass",
@@ -6472,97 +6563,6 @@
"value": "active-content",
"isInheritable": false,
"position": 40
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "MgibgPcfeuGz",
- "isInheritable": false,
- "position": 50
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "1vHRoWCEjj0L",
- "isInheritable": false,
- "position": 70
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "yIhgI5H7A2Sm",
- "isInheritable": false,
- "position": 100
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "HcABDtFCkbFN",
- "isInheritable": false,
- "position": 110
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "RnaPdbciOfeq",
- "isInheritable": false,
- "position": 120
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "SPirpZypehBG",
- "isInheritable": false,
- "position": 130
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "mHbBMPDPkVV5",
- "isInheritable": false,
- "position": 150
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "AlhDUqhENtH7",
- "isInheritable": false,
- "position": 160
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "pKK96zzmvBGf",
- "isInheritable": false,
- "position": 170
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "gOKqSJgXLcIj",
- "isInheritable": false,
- "position": 180
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "64ZTlUPgEPtW",
- "isInheritable": false,
- "position": 190
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "HI6GBBIduIgv",
- "isInheritable": false,
- "position": 200
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "IjZS7iK5EXtb",
- "isInheritable": false,
- "position": 210
}
],
"format": "markdown",
@@ -9943,7 +9943,7 @@
{
"type": "relation",
"name": "internalLink",
- "value": "CdNpE2pqjmI6",
+ "value": "yIhgI5H7A2Sm",
"isInheritable": false,
"position": 10
},
@@ -9971,31 +9971,38 @@
{
"type": "relation",
"name": "internalLink",
- "value": "XpOYSgsLkTJy",
+ "value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
- "value": "A9Oc6YKKc65v",
+ "value": "XpOYSgsLkTJy",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
- "value": "R7abl2fc6Mxi",
+ "value": "A9Oc6YKKc65v",
"isInheritable": false,
"position": 70
},
{
"type": "relation",
"name": "internalLink",
- "value": "6tZeKvSHEUiB",
+ "value": "R7abl2fc6Mxi",
"isInheritable": false,
"position": 80
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "6tZeKvSHEUiB",
+ "isInheritable": false,
+ "position": 90
+ },
{
"type": "label",
"name": "iconClass",
@@ -10009,13 +10016,6 @@
"value": "render-note",
"isInheritable": false,
"position": 70
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "yIhgI5H7A2Sm",
- "isInheritable": false,
- "position": 90
}
],
"format": "markdown",
@@ -11175,20 +11175,6 @@
"type": "text",
"mime": "text/html",
"attributes": [
- {
- "type": "relation",
- "name": "internalLink",
- "value": "zEY4DaJG4YT5",
- "isInheritable": false,
- "position": 10
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "OFXdgB2nNk1F",
- "isInheritable": false,
- "position": 20
- },
{
"type": "relation",
"name": "internalLink",
@@ -11238,13 +11224,6 @@
"isInheritable": false,
"position": 90
},
- {
- "type": "relation",
- "name": "internalLink",
- "value": "BlN9DFI679QC",
- "isInheritable": false,
- "position": 100
- },
{
"type": "label",
"name": "iconClass",
@@ -11258,6 +11237,13 @@
"value": "geomap",
"isInheritable": false,
"position": 90
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HI6GBBIduIgv",
+ "isInheritable": false,
+ "position": 110
}
],
"format": "markdown",
@@ -16248,10 +16234,94 @@
{
"type": "relation",
"name": "internalLink",
- "value": "SynTBQiBsdYJ",
+ "value": "MgibgPcfeuGz",
+ "isInheritable": false,
+ "position": 10
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "oPVyFC7WL2Lp",
+ "isInheritable": false,
+ "position": 20
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "RnaPdbciOfeq",
"isInheritable": false,
"position": 30
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "4Gn3psZKsfSm",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "xYmIYSP6wE3F",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HcABDtFCkbFN",
+ "isInheritable": false,
+ "position": 60
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "SPirpZypehBG",
+ "isInheritable": false,
+ "position": 70
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "HI6GBBIduIgv",
+ "isInheritable": false,
+ "position": 80
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "GPERMystNGTB",
+ "isInheritable": false,
+ "position": 90
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "GhurYZjh8e1V",
+ "isInheritable": false,
+ "position": 100
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "M8IppdwVHSjG",
+ "isInheritable": false,
+ "position": 110
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "Q2z6av6JZVWm",
+ "isInheritable": false,
+ "position": 120
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "SynTBQiBsdYJ",
+ "isInheritable": false,
+ "position": 130
+ },
{
"type": "label",
"name": "shareAlias",
@@ -16265,90 +16335,6 @@
"value": "bx bx-window",
"isInheritable": false,
"position": 40
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "Q2z6av6JZVWm",
- "isInheritable": false,
- "position": 50
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "GhurYZjh8e1V",
- "isInheritable": false,
- "position": 60
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "M8IppdwVHSjG",
- "isInheritable": false,
- "position": 70
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "MgibgPcfeuGz",
- "isInheritable": false,
- "position": 80
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "SPirpZypehBG",
- "isInheritable": false,
- "position": 90
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "RnaPdbciOfeq",
- "isInheritable": false,
- "position": 100
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "oPVyFC7WL2Lp",
- "isInheritable": false,
- "position": 110
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "4Gn3psZKsfSm",
- "isInheritable": false,
- "position": 120
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "xYmIYSP6wE3F",
- "isInheritable": false,
- "position": 130
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "HcABDtFCkbFN",
- "isInheritable": false,
- "position": 140
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "HI6GBBIduIgv",
- "isInheritable": false,
- "position": 150
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "GPERMystNGTB",
- "isInheritable": false,
- "position": 160
}
],
"format": "markdown",
@@ -16980,6 +16966,13 @@
"isInheritable": false,
"position": 60
},
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "s8alTXmpFR61",
+ "isInheritable": false,
+ "position": 70
+ },
{
"type": "label",
"name": "iconClass",
@@ -16993,13 +16986,6 @@
"value": "launch-bar-widgets",
"isInheritable": false,
"position": 40
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "s8alTXmpFR61",
- "isInheritable": false,
- "position": 70
}
],
"format": "markdown",
@@ -17444,6 +17430,48 @@
"type": "text",
"mime": "text/html",
"attributes": [
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "yIhgI5H7A2Sm",
+ "isInheritable": false,
+ "position": 10
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "WOcw2SLH6tbX",
+ "isInheritable": false,
+ "position": 20
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "poXkQfguuA0U",
+ "isInheritable": false,
+ "position": 30
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "6f9hih2hXXZk",
+ "isInheritable": false,
+ "position": 40
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "GPERMystNGTB",
+ "isInheritable": false,
+ "position": 50
+ },
+ {
+ "type": "relation",
+ "name": "internalLink",
+ "value": "MEtfsqa5VwNi",
+ "isInheritable": false,
+ "position": 60
+ },
{
"type": "label",
"name": "shareAlias",
@@ -17457,48 +17485,6 @@
"value": "bx bx-server",
"isInheritable": false,
"position": 40
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "yIhgI5H7A2Sm",
- "isInheritable": false,
- "position": 50
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "WOcw2SLH6tbX",
- "isInheritable": false,
- "position": 60
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "poXkQfguuA0U",
- "isInheritable": false,
- "position": 70
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "MEtfsqa5VwNi",
- "isInheritable": false,
- "position": 80
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "6f9hih2hXXZk",
- "isInheritable": false,
- "position": 90
- },
- {
- "type": "relation",
- "name": "internalLink",
- "value": "GPERMystNGTB",
- "isInheritable": false,
- "position": 100
}
],
"format": "markdown",
diff --git a/docs/User Guide/User Guide.md b/docs/User Guide/User Guide.md
index d02f4ecc38..ce5357b26e 100644
--- a/docs/User Guide/User Guide.md
+++ b/docs/User Guide/User Guide.md
@@ -15,7 +15,7 @@ Trilium is an open-source solution for note-taking and organizing a personal kno
* Desktop Installation
* Server Installation
-* [missing note] or [missing note]
+* Frontend API or [missing note]
* [ETAPI reference](User%20Guide/Advanced%20Usage/ETAPI%20\(REST%20API\)/API%20Reference.dat)
## External links
diff --git a/docs/User Guide/User Guide/Collections/Geo Map.md b/docs/User Guide/User Guide/Collections/Geo Map.md
index f79e99ebf7..91d583b193 100644
--- a/docs/User Guide/User Guide/Collections/Geo Map.md
+++ b/docs/User Guide/User Guide/Collections/Geo Map.md
@@ -1,6 +1,6 @@
# Geo Map
> [!IMPORTANT]
-> Attributes Promoted Attributes Attributes Starting with Trilium v0.97.0, the geo map has been converted from a standalone [note type](../Note%20Types.md) to a type of view for the Note List .
+> Starting with Trilium v0.97.0, the geo map has been converted from a standalone [note type](../Note%20Types.md) to a type of view for the Note List .
@@ -26,8 +26,8 @@ The position on the map and the zoom are saved inside the map note and restored
| | | |
| --- | --- | --- |
-| 1 | To create a marker, first navigate to the desired point on the map. Then press the  button in the [Floating buttons](../Basic%20Concepts%20and%20Features/UI%20Elements/Floating%20buttons.md) (top-right) area. If the button is not visible, make sure the button section is visible by pressing the chevron button () in the top-right of the map. | |
-| 2 | | Once pressed, the map will enter in the insert mode, as illustrated by the notification. Simply click the point on the map where to place the marker, or the Escape key to cancel. |
+| 1 | To create a marker, first navigate to the desired point on the map. Then press the  button in the [Floating buttons](../Basic%20Concepts%20and%20Features/UI%20Elements/Floating%20buttons.md) (top-right) area. If the button is not visible, make sure the button section is visible by pressing the chevron button () in the top-right of the map. | |
+| 2 | | Once pressed, the map will enter in the insert mode, as illustrated by the notification. Simply click the point on the map where to place the marker, or the Escape key to cancel. |
| 3 | | Enter the name of the marker/note to be created. |
| 4 | | Once confirmed, the marker will show up on the map and it will also be displayed as a child note of the map. |
@@ -109,7 +109,7 @@ The value of the attribute is made up of the latitude and longitude separated by
| | | |
| --- | --- | --- |
-| 1 | | Go to Google Maps on the web and look for a desired location, right click on it and a context menu will show up. Simply click on the first item displaying the coordinates and they will be copied to clipboard. Then paste the value inside the text box into the `#geolocation` attribute of a child note of the map (don't forget to surround the value with a `"` character). |
+| 1 | | Go to Google Maps on the web and look for a desired location, right click on it and a context menu will show up. Simply click on the first item displaying the coordinates and they will be copied to clipboard. Then paste the value inside the text box into the `#geolocation` attribute of a child note of the map (don't forget to surround the value with a `"` character). |
| 2 | | In Trilium, create a child note under the map. |
| 3 | | And then go to Owned Attributes and type `#geolocation="`, then paste from the clipboard as-is and then add the ending `"` character. Press Enter to confirm and the map should now be updated to contain the new note. |
@@ -120,7 +120,7 @@ Similarly to the Google Maps approach:
| | | |
| --- | --- | --- |
| 1 | | Go to any location on openstreetmap.org and right click to bring up the context menu. Select the “Show address” item. |
-| 2 | | The address will be visible in the top-left of the screen, in the place of the search bar. Select the coordinates and copy them into the clipboard. |
+| 2 | | The address will be visible in the top-left of the screen, in the place of the search bar. Select the coordinates and copy them into the clipboard. |
| 3 | | Simply paste the value inside the text box into the `#geolocation` attribute of a child note of the map and then it should be displayed on the map. |
## Adding GPS tracks (.gpx)
@@ -131,7 +131,7 @@ Trilium has basic support for displaying GPS tracks on the geo map.
| --- | --- | --- |
| 1 | | To add a track, simply drag & drop a .gpx file inside the geo map in the note tree. |
| 2 | | In order for the file to be recognized as a GPS track, it needs to show up as `application/gpx+xml` in the _File type_ field. |
-| 3 | | When going back to the map, the track should now be visible. The start and end points of the track are indicated by the two blue markers. |
+| 3 | | When going back to the map, the track should now be visible. The start and end points of the track are indicated by the two blue markers. |
> [!NOTE]
> The starting point of the track will be displayed as a marker, with the name of the note underneath. The start marker will also respect the icon and the `color` of the note. The end marker is displayed with a distinct icon.
@@ -152,7 +152,7 @@ To enable read-only mode simply press the _Lock_ icon from the Ribbon or manually via the `#map:style` attribute.
+The styling of the map can be adjusted in the _Collection Properties_ (above the map, by pressing on the Gear icon) or manually via the `#map:style` attribute.
The geo map comes with two different types of styles:
@@ -166,12 +166,23 @@ The geo map comes with two different types of styles:
* These come both in a light and a dark version.
* The vector styles come from [VersaTiles](https://versatiles.org/), a free and open-source project providing map tiles based on OpenStreetMap.
+### Custom map style / tiles
+
+Starting with v0.102.0 it is possible to use custom tile sets, but only in raster format.
+
+To do so, manually set the `#map:style` [label](../Advanced%20Usage/Attributes/Labels.md) to the URL of the tile set. For example, to use Esri.NatGeoWorldMap, set the value to [`https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}`.](https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}.)
+
> [!NOTE]
-> Currently it is not possible to use a custom map style.
+> For a list of tile sets, see the [Leaflet Providers preview](https://leaflet-extras.github.io/leaflet-providers/preview/) page. Select a desired tile set and just copy the URL from the _Plain JavaScript_ example.
-### Scale
+Custom vector map support is planned, but not yet implemented.
-Activating this option via the Ribbon or manually via `#map:scale` will display an indicator in the bottom-left of the scale of the map.
+### Other options
+
+The following options can be configured either via the Collection properties pane above the map, by clicking on the settings (Gear icon). Alternatively, each of these options also have a corresponding [label](../Advanced%20Usage/Attributes/Labels.md) that can be set manually.
+
+* Scale, which illustrates the scale of the map in kilometers and miles in the bottom-left of the map.
+* The name of the markers is displayed by default underneath the pin on the map. Since v0.102.0, it is possible to hide these labels which increases the performance and decreases clutter when there are many markers on the map.
## Troubleshooting
From e3172ebf1ccb6f939a90ab602ef9d7d5712a7935 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 17:51:06 +0200
Subject: [PATCH 100/247] fix(client): unreadable disabled item
---
apps/client/src/stylesheets/style.css | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css
index c3e2810fc8..2a6d66ed6c 100644
--- a/apps/client/src/stylesheets/style.css
+++ b/apps/client/src/stylesheets/style.css
@@ -153,6 +153,11 @@ textarea,
background: var(--input-background-color);
}
+.form-control:disabled {
+ background-color: var(--input-background-color);
+ opacity: 0.6;
+}
+
.form-control:focus {
color: var(--input-text-color);
background: var(--input-background-color);
From 6ffbe196674ddcbb1e86aaa06530932b12c1f8a6 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:07:35 +0200
Subject: [PATCH 101/247] client/list view: add support for tinted notes
---
apps/client/src/stylesheets/theme-next-dark.css | 6 ++++--
apps/client/src/stylesheets/theme-next-light.css | 6 ++++--
.../widgets/collections/legacy/ListOrGridView.css | 12 ++++++++++--
.../widgets/collections/legacy/ListOrGridView.tsx | 9 ++++++---
4 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/apps/client/src/stylesheets/theme-next-dark.css b/apps/client/src/stylesheets/theme-next-dark.css
index eeac40a1fe..43fdeda2d7 100644
--- a/apps/client/src/stylesheets/theme-next-dark.css
+++ b/apps/client/src/stylesheets/theme-next-dark.css
@@ -312,7 +312,8 @@
* Dark color scheme tweaks
*/
-#left-pane .fancytree-node.tinted {
+#left-pane .fancytree-node.tinted,
+.nested-note-list-item.use-note-color {
--custom-color: var(--dark-theme-custom-color);
/* The background color of the active item in the note tree.
@@ -351,7 +352,8 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
}
.note-split.with-hue,
-.quick-edit-dialog-wrapper.with-hue {
+.quick-edit-dialog-wrapper.with-hue,
+.nested-note-list-item.with-hue {
--note-icon-custom-background-color: hsl(var(--custom-color-hue), 15.8%, 30.9%);
--note-icon-custom-color: hsl(var(--custom-color-hue), 100%, 76.5%);
--note-icon-hover-custom-background-color: hsl(var(--custom-color-hue), 28.3%, 36.7%);
diff --git a/apps/client/src/stylesheets/theme-next-light.css b/apps/client/src/stylesheets/theme-next-light.css
index 70883f2368..d9f267d6f3 100644
--- a/apps/client/src/stylesheets/theme-next-light.css
+++ b/apps/client/src/stylesheets/theme-next-light.css
@@ -306,7 +306,8 @@
--calendar-coll-today-background-color: #00000006;
}
-#left-pane .fancytree-node.tinted {
+#left-pane .fancytree-node.tinted,
+.nested-note-list-item.use-note-color {
--custom-color: var(--light-theme-custom-color);
/* The background color of the active item in the note tree.
@@ -322,7 +323,8 @@
}
.note-split.with-hue,
-.quick-edit-dialog-wrapper.with-hue {
+.quick-edit-dialog-wrapper.with-hue,
+.nested-note-list-item.with-hue {
--note-icon-custom-background-color: hsl(var(--custom-color-hue), 44.5%, 43.1%);
--note-icon-custom-color: hsl(var(--custom-color-hue), 91.3%, 91%);
--note-icon-hover-custom-background-color: hsl(var(--custom-color-hue), 55.1%, 50.2%);
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 4cee5d215b..7e36c445f0 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -165,6 +165,14 @@
}
}
+.nested-note-list-item.use-note-color {
+ span.tn-icon + span,
+ .nested-note-list:not(.search-results) & .tn-icon,
+ .rendered-note-attributes {
+ color: var(--custom-color);
+ }
+}
+
.nested-note-list:not(.search-results) h5 {
span.tn-icon + span,
.note-list-attributes {
@@ -203,9 +211,9 @@
height: 1.75em;
margin-inline-end: 12px;
border-radius: 50%;
- background: var(--note-list-view-large-icon-background);
+ background: var(--note-icon-custom-background-color, var(--note-list-view-large-icon-background));
font-size: 1.2em;
- color: var(--note-list-view-large-icon-color);
+ color: var(--note-icon-custom-color, var(--note-list-view-large-icon-color));
}
h5 .ck-find-result {
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
index be244cbbe7..3f92f8ed62 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
@@ -116,16 +116,19 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan
includeArchived={includeArchived} />
>
}
-
+
return (
-
+
setExpanded(!isExpanded)}/>
From 3df03a551c8c66a134b4aee984ca9a3e91f9e1c4 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:15:07 +0200
Subject: [PATCH 102/247] client/list view: add support for archived items
---
.../collections/legacy/ListOrGridView.css | 22 ++++++++++++++-----
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 7e36c445f0..18f91c1819 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -119,12 +119,15 @@
}
/* List item */
-.nested-note-list-item h5 {
- display: flex;
- align-items: center;
- font-size: 1em;
- font-weight: normal;
- margin: 0;
+.nested-note-list-item {
+
+ h5 {
+ display: flex;
+ align-items: center;
+ font-size: 1em;
+ font-weight: normal;
+ margin: 0;
+ }
.note-expander {
margin-inline-end: 4px;
@@ -163,6 +166,13 @@
margin-inline-start: 8px;
flex-shrink: 0;
}
+
+ &.archived {
+ .note-book-title,
+ .tn-icon {
+ opacity: .5;
+ }
+ }
}
.nested-note-list-item.use-note-color {
From 108ca5afb5279ef764934159e645e6e5bf3ab585 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:16:46 +0200
Subject: [PATCH 103/247] client/list view: add support for archived items
---
apps/client/src/widgets/collections/legacy/ListOrGridView.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 18f91c1819..eea3222a45 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -170,7 +170,7 @@
&.archived {
.note-book-title,
.tn-icon {
- opacity: .5;
+ opacity: .75;
}
}
}
From 900bfdff9d8fab4250ad28e18b916832084d087e Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:18:50 +0200
Subject: [PATCH 104/247] style/list view: refactor
---
.../widgets/collections/legacy/ListOrGridView.css | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index eea3222a45..12199ad3bc 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -173,13 +173,13 @@
opacity: .75;
}
}
-}
-.nested-note-list-item.use-note-color {
- span.tn-icon + span,
- .nested-note-list:not(.search-results) & .tn-icon,
- .rendered-note-attributes {
- color: var(--custom-color);
+ &.use-note-color {
+ span.tn-icon + span,
+ .nested-note-list:not(.search-results) & .tn-icon,
+ .rendered-note-attributes {
+ color: var(--custom-color);
+ }
}
}
From f645d9d721cb11465069931a413ff816f9923514 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:20:45 +0200
Subject: [PATCH 105/247] style/list view: tweak
---
apps/client/src/widgets/collections/legacy/ListOrGridView.css | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 12199ad3bc..618a79030e 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -168,9 +168,9 @@
}
&.archived {
- .note-book-title,
+ span.tn-icon + span,
.tn-icon {
- opacity: .75;
+ opacity: .6;
}
}
From 10cf1a371eb84e3d1cc57678b4b08beaabd8e9f6 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:34:00 +0200
Subject: [PATCH 106/247] style/card: improve
---
apps/client/src/widgets/react/Card.css | 9 +++++----
apps/client/src/widgets/react/Card.tsx | 7 ++++---
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/widgets/react/Card.css b/apps/client/src/widgets/react/Card.css
index 49b3145f8d..4afa49b898 100644
--- a/apps/client/src/widgets/react/Card.css
+++ b/apps/client/src/widgets/react/Card.css
@@ -1,6 +1,7 @@
.tn-card {
--card-border-radius: 8px;
- --card-padding: 8px 16px;
+ --card-padding-block: 8px;
+ --card-padding-inline: 16px;
--card-section-gap: 3px;
--card-nested-section-indent: 30px;
@@ -9,7 +10,7 @@
gap: var(--card-section-gap);
.tn-card-section {
- padding: var(--card-padding);
+ padding: var(--card-padding-block) var(--card-padding-inline);
border: 1px solid var(--card-border-color, var(--main-border-color));
background: var(--card-background-color);
@@ -24,11 +25,11 @@
}
&.tn-card-section-nested {
- padding-left: calc(var(--card-nested-section-indent) * var(--tn-card-section-nesting-level));
+ padding-left: calc(var(--card-padding-inline) + var(--card-nested-section-indent) * var(--tn-card-section-nesting-level));
background-color: color-mix(in srgb, var(--card-background-color) calc(100% / (var(--tn-card-section-nesting-level) + 1)) , transparent);
}
- &.tn-action:hover {
+ &.tn-card-section-highlight-on-hover:hover {
background-color: var(--card-background-hover-color);
transition: background-color .2s ease-out;
}
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index c717ee5433..141cd0119f 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -17,7 +17,8 @@ export function Card(props: {children: ComponentChildren} & CardProps) {
interface CardSectionProps {
className?: string;
subSections?: JSX.Element | JSX.Element[];
- childrenVisible?: boolean;
+ subSectionsVisible?: boolean;
+ highlightOnHover?: boolean;
hasAction?: boolean;
onAction?: () => void;
}
@@ -29,14 +30,14 @@ export function CardSection(props: {children: ComponentChildren} & CardSectionPr
return <>
0,
- "tn-action": props.hasAction
+ "tn-card-section-highlight-on-hover": props.highlightOnHover || props.hasAction
})}
style={{"--tn-card-section-nesting-level": nestingLevel}}
onClick={props.onAction}>
{props.children}
- {props.childrenVisible &&
+ {props.subSectionsVisible &&
{props.subSections}
From 80b488deec28fbea51acff35036cd97964ccb90f Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:34:54 +0200
Subject: [PATCH 107/247] style/list view: tweak indentation size
---
.../src/widgets/collections/legacy/ListOrGridView.css | 6 +++++-
.../src/widgets/collections/legacy/ListOrGridView.tsx | 4 ++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index 618a79030e..a1586135dc 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -115,7 +115,11 @@
}
:root .nested-note-list {
- --card-nested-section-indent: 40px;
+ --card-nested-section-indent: 25px;
+}
+
+:root .nested-note-list.search-results {
+ --card-nested-section-indent: 32px;
}
/* List item */
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
index 3f92f8ed62..d024e8616b 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
@@ -124,8 +124,8 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan
"archived": note.isArchived
})}
subSections={subSections}
- childrenVisible={isExpanded}
- hasAction
+ subSectionsVisible={isExpanded}
+ highlightOnHover
data-note-id={note.noteId}
>
From b6582536878d506bf9ef0ce40e949eb025f0d02b Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:55:04 +0200
Subject: [PATCH 108/247] style/card: refactor
---
apps/client/src/widgets/react/Card.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/react/Card.css b/apps/client/src/widgets/react/Card.css
index 4afa49b898..ce6bf6dc61 100644
--- a/apps/client/src/widgets/react/Card.css
+++ b/apps/client/src/widgets/react/Card.css
@@ -1,10 +1,12 @@
-.tn-card {
+:where(.tn-card) {
--card-border-radius: 8px;
--card-padding-block: 8px;
--card-padding-inline: 16px;
--card-section-gap: 3px;
--card-nested-section-indent: 30px;
+}
+.tn-card {
display: flex;
flex-direction: column;
gap: var(--card-section-gap);
From 7e6daf5b3613db8b15cb90e72609a982959fd8f0 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 18:55:23 +0200
Subject: [PATCH 109/247] style/list view: refactor
---
.../src/widgets/collections/legacy/ListOrGridView.css | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index a1586135dc..b2251185ed 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -114,12 +114,12 @@
}
}
-:root .nested-note-list {
+.nested-note-list {
--card-nested-section-indent: 25px;
-}
-:root .nested-note-list.search-results {
- --card-nested-section-indent: 32px;
+ &.search-results {
+ --card-nested-section-indent: 32px;
+ }
}
/* List item */
From 483c57029aebeb6b6c933564ca61734f531fd18a Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 19:10:31 +0200
Subject: [PATCH 110/247] client/list view: extract event handler
---
.../src/widgets/collections/legacy/ListOrGridView.tsx | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
index d024e8616b..f4750c6ef5 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
@@ -19,6 +19,7 @@ import { JSX } from "preact/jsx-runtime";
import { clsx } from "clsx";
import ActionButton from "../../react/ActionButton";
import linkContextMenuService from "../../../menus/link_context_menu";
+import { TargetedMouseEvent } from "preact";
export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens }: ViewModeProps<{}>) {
const expandDepth = useExpansionDepth(note);
@@ -140,7 +141,7 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan
{linkContextMenuService.openContextMenu(notePath, e); e.stopPropagation()}}
+ onClick={(e) => openNoteMenu(notePath, e)}
/>
@@ -274,3 +275,8 @@ function useExpansionDepth(note: FNote) {
return parseInt(expandDepth, 10);
}
+
+function openNoteMenu(notePath, e: TargetedMouseEvent) {
+ linkContextMenuService.openContextMenu(notePath, e);
+ e.stopPropagation()
+}
From d1a3bceaa69a3a2e21dfc02904affee2f0201dfd Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 19:15:58 +0200
Subject: [PATCH 111/247] client/list view: refactor
---
apps/client/src/widgets/collections/legacy/ListOrGridView.css | 1 -
apps/client/src/widgets/collections/legacy/ListOrGridView.tsx | 3 +--
apps/client/src/widgets/react/Card.tsx | 3 +--
3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index b2251185ed..ea9a4e2e4b 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -124,7 +124,6 @@
/* List item */
.nested-note-list-item {
-
h5 {
display: flex;
align-items: center;
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
index f4750c6ef5..cdde8a6087 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx
@@ -225,8 +225,7 @@ export function NoteContent({ note, trim, noChildrenList, highlightedTokens, inc
});
}, [ note, highlightedTokens ]);
- return
-
;
+ return
;
}
function NoteChildren({ note, parentNote, highlightedTokens, currentLevel, expandDepth, includeArchived }: {
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index 141cd0119f..26b52040be 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -19,7 +19,6 @@ interface CardSectionProps {
subSections?: JSX.Element | JSX.Element[];
subSectionsVisible?: boolean;
highlightOnHover?: boolean;
- hasAction?: boolean;
onAction?: () => void;
}
@@ -30,7 +29,7 @@ export function CardSection(props: {children: ComponentChildren} & CardSectionPr
return <>
0,
- "tn-card-section-highlight-on-hover": props.highlightOnHover || props.hasAction
+ "tn-card-section-highlight-on-hover": props.highlightOnHover || props.onAction
})}
style={{"--tn-card-section-nesting-level": nestingLevel}}
onClick={props.onAction}>
From e00150a87603e536c95d96ba1f6101476b73970e Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 19:46:08 +0200
Subject: [PATCH 112/247] ui/cards: do not include nesting level CSS variables
if not necessary
---
apps/client/src/widgets/react/Card.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index 26b52040be..7296433bf7 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -31,7 +31,7 @@ export function CardSection(props: {children: ComponentChildren} & CardSectionPr
"tn-card-section-nested": nestingLevel > 0,
"tn-card-section-highlight-on-hover": props.highlightOnHover || props.onAction
})}
- style={{"--tn-card-section-nesting-level": nestingLevel}}
+ style={{"--tn-card-section-nesting-level": (nestingLevel) ? nestingLevel : null}}
onClick={props.onAction}>
{props.children}
From 4ee9d45dfc4fdc869dc69f45dc865de16f5db368 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 20:06:01 +0200
Subject: [PATCH 113/247] ui/cards: implement card titles (heading)
---
apps/client/src/widgets/react/Card.css | 8 ++++++++
apps/client/src/widgets/react/Card.tsx | 10 +++++++---
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/react/Card.css b/apps/client/src/widgets/react/Card.css
index ce6bf6dc61..5bbe0f6ab4 100644
--- a/apps/client/src/widgets/react/Card.css
+++ b/apps/client/src/widgets/react/Card.css
@@ -6,6 +6,14 @@
--card-nested-section-indent: 30px;
}
+.tn-card-heading {
+ margin-bottom: 10px;
+ font-size: .75rem;
+ font-weight: 600;
+ letter-spacing: .4pt;
+ text-transform: uppercase;
+}
+
.tn-card {
display: flex;
flex-direction: column;
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index 7296433bf7..3a805ff731 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -6,12 +6,16 @@ import clsx from "clsx";
interface CardProps {
className?: string;
+ heading?: string;
}
export function Card(props: {children: ComponentChildren} & CardProps) {
- return
- {props.children}
-
;
+ return <>
+ {props.heading && {props.heading} }
+
+ {props.children}
+
+ >;
}
interface CardSectionProps {
From 48013dc264ff5dd8cf2bb829775e429c33f15f09 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 20:14:14 +0200
Subject: [PATCH 114/247] ui/cards: use a better HTML structure
---
apps/client/src/widgets/react/Card.css | 2 +-
apps/client/src/widgets/react/Card.tsx | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/apps/client/src/widgets/react/Card.css b/apps/client/src/widgets/react/Card.css
index 5bbe0f6ab4..1472dc135a 100644
--- a/apps/client/src/widgets/react/Card.css
+++ b/apps/client/src/widgets/react/Card.css
@@ -14,7 +14,7 @@
text-transform: uppercase;
}
-.tn-card {
+.tn-card-body {
display: flex;
flex-direction: column;
gap: var(--card-section-gap);
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index 3a805ff731..6f41a2e6f3 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -10,12 +10,12 @@ interface CardProps {
}
export function Card(props: {children: ComponentChildren} & CardProps) {
- return <>
+ return
{props.heading &&
{props.heading} }
-
+
{props.children}
- >;
+
;
}
interface CardSectionProps {
From 489a88b8da96d2e28a499770477775b3099e07c4 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 20:23:19 +0200
Subject: [PATCH 115/247] ui/cards: refactor
---
apps/client/src/widgets/react/Card.css | 1 -
apps/client/src/widgets/react/Card.tsx | 24 ++++++++++++++++--------
2 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/apps/client/src/widgets/react/Card.css b/apps/client/src/widgets/react/Card.css
index 1472dc135a..cabc4a0889 100644
--- a/apps/client/src/widgets/react/Card.css
+++ b/apps/client/src/widgets/react/Card.css
@@ -43,6 +43,5 @@
background-color: var(--card-background-hover-color);
transition: background-color .2s ease-out;
}
-
}
}
\ No newline at end of file
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index 6f41a2e6f3..01b40ee306 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -4,7 +4,9 @@ import { JSX } from "preact";
import { useContext } from "preact/hooks";
import clsx from "clsx";
-interface CardProps {
+// #region Card
+
+export interface CardProps {
className?: string;
heading?: string;
}
@@ -18,7 +20,11 @@ export function Card(props: {children: ComponentChildren} & CardProps) {
;
}
-interface CardSectionProps {
+// #endregion
+
+// #region Card Section
+
+export interface CardSectionProps {
className?: string;
subSections?: JSX.Element | JSX.Element[];
subSectionsVisible?: boolean;
@@ -26,6 +32,12 @@ interface CardSectionProps {
onAction?: () => void;
}
+interface CardSectionContextType {
+ nestingLevel: number;
+}
+
+const CardSectionContext = createContext(undefined);
+
export function CardSection(props: {children: ComponentChildren} & CardSectionProps) {
const parentContext = useContext(CardSectionContext);
const nestingLevel = (parentContext && parentContext.nestingLevel + 1) ?? 0;
@@ -40,7 +52,7 @@ export function CardSection(props: {children: ComponentChildren} & CardSectionPr
{props.children}
- {props.subSectionsVisible &&
+ {props.subSectionsVisible && props.subSections &&
{props.subSections}
@@ -48,8 +60,4 @@ export function CardSection(props: {children: ComponentChildren} & CardSectionPr
>;
}
-interface CardSectionContextType {
- nestingLevel: number;
-}
-
-export const CardSectionContext = createContext(undefined);
\ No newline at end of file
+// #endregion
\ No newline at end of file
From 7551d0e04437a3f5441b43d56d7eeec637e7c07d Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 20:32:01 +0200
Subject: [PATCH 116/247] ui/cards: fix attribute name
---
apps/client/src/widgets/react/Card.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/react/Card.tsx b/apps/client/src/widgets/react/Card.tsx
index 01b40ee306..224a151504 100644
--- a/apps/client/src/widgets/react/Card.tsx
+++ b/apps/client/src/widgets/react/Card.tsx
@@ -12,7 +12,7 @@ export interface CardProps {
}
export function Card(props: {children: ComponentChildren} & CardProps) {
- return
+ return
{props.heading &&
{props.heading} }
{props.children}
From b8bc85856b122e63c03c4c0e3a4fa1460c129ffb Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 21:12:42 +0200
Subject: [PATCH 117/247] ui/pager: add prev/next page navigation buttons
---
apps/client/src/translations/en/translation.json | 4 +++-
.../client/src/widgets/collections/Pagination.tsx | 15 +++++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index e2f00acec5..e97aba782b 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2191,7 +2191,9 @@
},
"pagination": {
"page_title": "Page of {{startIndex}} - {{endIndex}}",
- "total_notes": "{{count}} notes"
+ "total_notes": "{{count}} notes",
+ "prev_page": "Previous page",
+ "next_page": "Next page"
},
"collections": {
"rendering_error": "Unable to show content due to an error."
diff --git a/apps/client/src/widgets/collections/Pagination.tsx b/apps/client/src/widgets/collections/Pagination.tsx
index 6b74964a64..f85a188a6f 100644
--- a/apps/client/src/widgets/collections/Pagination.tsx
+++ b/apps/client/src/widgets/collections/Pagination.tsx
@@ -4,6 +4,7 @@ import FNote from "../../entities/fnote";
import froca from "../../services/froca";
import { useNoteLabelInt } from "../react/hooks";
import { t } from "../../services/i18n";
+import ActionButton from "../react/ActionButton";
interface PaginationContext {
page: number;
@@ -50,7 +51,21 @@ export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit
From d8806eaa042216fdf456ff0bbf99548241078c5b Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 21:31:58 +0200
Subject: [PATCH 118/247] ui/pager: replace the page number links with buttons
---
.../src/widgets/collections/Pagination.tsx | 25 +++++++------------
1 file changed, 9 insertions(+), 16 deletions(-)
diff --git a/apps/client/src/widgets/collections/Pagination.tsx b/apps/client/src/widgets/collections/Pagination.tsx
index f85a188a6f..282a6b36a5 100644
--- a/apps/client/src/widgets/collections/Pagination.tsx
+++ b/apps/client/src/widgets/collections/Pagination.tsx
@@ -5,6 +5,7 @@ import froca from "../../services/froca";
import { useNoteLabelInt } from "../react/hooks";
import { t } from "../../services/i18n";
import ActionButton from "../react/ActionButton";
+import Button from "../react/Button";
interface PaginationContext {
page: number;
@@ -27,22 +28,14 @@ export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit setPage(i)}
- >
- {i}
-
- ))
- } else {
- // Current page
- children.push({i} )
- }
-
- children.push(<>{" "} {" "}>);
+ children.push((
+ setPage(i)}
+ />
+ ))
} else if (lastPrinted) {
children.push(<>{"... "} {" "}>);
lastPrinted = false;
From 0b8cba78d5eecff1f3e7771b941a6c544dee40d1 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Sun, 15 Feb 2026 21:36:53 +0200
Subject: [PATCH 119/247] ui/pager: remove unused styles
---
apps/client/src/widgets/collections/NoteList.css | 11 -----------
.../src/widgets/collections/legacy/ListOrGridView.css | 4 ----
2 files changed, 15 deletions(-)
diff --git a/apps/client/src/widgets/collections/NoteList.css b/apps/client/src/widgets/collections/NoteList.css
index 449f45d97c..c7b0f63acb 100644
--- a/apps/client/src/widgets/collections/NoteList.css
+++ b/apps/client/src/widgets/collections/NoteList.css
@@ -19,14 +19,3 @@ body.prefers-centered-content .note-list-widget:not(.full-height) {
.note-list-widget video {
height: 100%;
}
-
-/* #region Pagination */
-.note-list-pager {
- font-size: 1rem;
-
- span.current-page {
- text-decoration: underline;
- font-weight: bold;
- }
-}
-/* #endregion */
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index d0b3a7e773..3231f554f5 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -100,10 +100,6 @@
overflow: auto;
}
-.note-list-pager {
- text-align: center;
-}
-
/* #region List view */
@keyframes note-preview-show {
From 70aa115933a6c669da2ba6fea4bab0347c8e572d Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 18:03:36 +0200
Subject: [PATCH 120/247] fix(collections): collection properties not shown if
there are no children
---
.../note_bars/CollectionProperties.css | 2 +-
apps/client/src/widgets/type_widgets/Book.tsx | 21 ++++++++++++-------
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.css b/apps/client/src/widgets/note_bars/CollectionProperties.css
index 99700f77a7..c8802a42a0 100644
--- a/apps/client/src/widgets/note_bars/CollectionProperties.css
+++ b/apps/client/src/widgets/note_bars/CollectionProperties.css
@@ -5,7 +5,7 @@
align-items: center;
width: 100%;
max-width: unset;
- font-size: 0.8em;
+ font-size: 0.8rem;
.dropdown-menu {
input.form-control {
diff --git a/apps/client/src/widgets/type_widgets/Book.tsx b/apps/client/src/widgets/type_widgets/Book.tsx
index 8dd1030c5d..6d07f5fbe3 100644
--- a/apps/client/src/widgets/type_widgets/Book.tsx
+++ b/apps/client/src/widgets/type_widgets/Book.tsx
@@ -1,11 +1,14 @@
+import "./Book.css";
+
+import { useEffect, useState } from "preact/hooks";
+
import { t } from "../../services/i18n";
+import { ViewTypeOptions } from "../collections/interface";
+import CollectionProperties from "../note_bars/CollectionProperties";
import Alert from "../react/Alert";
import { useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks";
import RawHtml from "../react/RawHtml";
import { TypeWidgetProps } from "./type_widget";
-import "./Book.css";
-import { useEffect, useState } from "preact/hooks";
-import { ViewTypeOptions } from "../collections/interface";
const VIEW_TYPES: ViewTypeOptions[] = [ "list", "grid", "presentation" ];
@@ -27,10 +30,14 @@ export default function Book({ note }: TypeWidgetProps) {
return (
<>
{shouldDisplayNoChildrenWarning && (
-
-
-
+ <>
+
+
+
+
+
+ >
)}
>
- )
+ );
}
From 23c50f34feb7d5342f9f40d4c315f1c9e872c5a2 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 18:14:02 +0200
Subject: [PATCH 121/247] fix(collections): list/grid collection properties
clipped when little results
---
apps/client/src/widgets/collections/NoteList.css | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/collections/NoteList.css b/apps/client/src/widgets/collections/NoteList.css
index 449f45d97c..198bc1bdaf 100644
--- a/apps/client/src/widgets/collections/NoteList.css
+++ b/apps/client/src/widgets/collections/NoteList.css
@@ -2,8 +2,12 @@
min-height: 0;
max-width: var(--max-content-width); /* Inherited from .note-split */
- overflow: auto;
+ overflow: visible;
contain: none !important;
+
+ &.full-height {
+ overflow: auto;
+ }
}
body.prefers-centered-content .note-list-widget:not(.full-height) {
From 81a572af25f1c23f2fab0cd7ebcb95e941376328 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 18:15:24 +0200
Subject: [PATCH 122/247] feat(collections): improve display of no children
---
apps/client/src/translations/en/translation.json | 2 +-
apps/client/src/widgets/NoteDetail.tsx | 9 +++++++++
apps/client/src/widgets/type_widgets/Book.tsx | 7 ++-----
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index e2f00acec5..b12ff472dc 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -1010,7 +1010,7 @@
"no_attachments": "This note has no attachments."
},
"book": {
- "no_children_help": "This collection doesn't have any child notes so there's nothing to display. See wiki for details.",
+ "no_children_help": "This collection doesn't have any child notes so there's nothing to display.",
"drag_locked_title": "Locked for editing",
"drag_locked_message": "Dragging not allowed since the collection is locked for editing."
},
diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx
index c49802a7a4..1b2cc8f8cc 100644
--- a/apps/client/src/widgets/NoteDetail.tsx
+++ b/apps/client/src/widgets/NoteDetail.tsx
@@ -1,6 +1,7 @@
import "./NoteDetail.css";
import clsx from "clsx";
+import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
import { isValidElement, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
@@ -355,6 +356,14 @@ export function checkFullHeight(noteContext: NoteContext | undefined, type: Exte
// https://github.com/zadam/trilium/issues/2522
const isBackendNote = noteContext?.noteId === "_backendLog";
const isFullHeightNoteType = type && TYPE_MAPPINGS[type].isFullHeight;
+
+ // Allow vertical centering when there are no results.
+ if (type === "book" &&
+ [ "grid", "list" ].includes(noteContext.note?.getLabelValue("viewType") ?? "grid") &&
+ !noteContext.note?.hasChildren()) {
+ return true;
+ }
+
return (!noteContext?.hasNoteList() && isFullHeightNoteType)
|| noteContext?.viewScope?.viewMode === "attachments"
|| isBackendNote;
diff --git a/apps/client/src/widgets/type_widgets/Book.tsx b/apps/client/src/widgets/type_widgets/Book.tsx
index 6d07f5fbe3..05a1e2f058 100644
--- a/apps/client/src/widgets/type_widgets/Book.tsx
+++ b/apps/client/src/widgets/type_widgets/Book.tsx
@@ -5,9 +5,8 @@ import { useEffect, useState } from "preact/hooks";
import { t } from "../../services/i18n";
import { ViewTypeOptions } from "../collections/interface";
import CollectionProperties from "../note_bars/CollectionProperties";
-import Alert from "../react/Alert";
import { useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks";
-import RawHtml from "../react/RawHtml";
+import NoItems from "../react/NoItems";
import { TypeWidgetProps } from "./type_widget";
const VIEW_TYPES: ViewTypeOptions[] = [ "list", "grid", "presentation" ];
@@ -33,9 +32,7 @@ export default function Book({ note }: TypeWidgetProps) {
<>
-
-
-
+
>
)}
>
From 08da8d73e00b602b4bef9a2b07aa0f9f35562954 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 18:16:15 +0200
Subject: [PATCH 123/247] fix(collections): collection properties still clipped
in list/grid view
---
apps/client/src/widgets/collections/legacy/ListOrGridView.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.css b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
index d0b3a7e773..0dca98403f 100644
--- a/apps/client/src/widgets/collections/legacy/ListOrGridView.css
+++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.css
@@ -1,5 +1,5 @@
.note-list {
- overflow: hidden;
+ overflow: visible;
position: relative;
height: 100%;
}
From 52db410c829f11fcc16505e727dc118e114f082c Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 18:18:36 +0200
Subject: [PATCH 124/247] fix(collections/map): shifting into collection
properties when switching styles
---
apps/client/src/widgets/collections/geomap/index.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/apps/client/src/widgets/collections/geomap/index.css b/apps/client/src/widgets/collections/geomap/index.css
index a30842c9b6..be16b13eaf 100644
--- a/apps/client/src/widgets/collections/geomap/index.css
+++ b/apps/client/src/widgets/collections/geomap/index.css
@@ -18,6 +18,10 @@ body.mobile .geo-view > .collection-properties {
.geo-map-container {
height: 100%;
overflow: hidden;
+
+ .maplibregl-canvas-container {
+ position: relative;
+ }
}
.leaflet-pane {
From 70a903f811abd4f80cedabbd0c98d36021e88def Mon Sep 17 00:00:00 2001
From: Hosted Weblate
Date: Sun, 15 Feb 2026 16:23:37 +0100
Subject: [PATCH 125/247] Update translation files
Updated by "Cleanup translation files" add-on in Weblate.
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/
---
apps/client/src/translations/cn/translation.json | 4 ----
apps/client/src/translations/de/translation.json | 4 ----
apps/client/src/translations/es/translation.json | 4 ----
apps/client/src/translations/fr/translation.json | 4 ----
apps/client/src/translations/ga/translation.json | 4 ----
apps/client/src/translations/it/translation.json | 4 ----
apps/client/src/translations/ja/translation.json | 4 ----
apps/client/src/translations/pl/translation.json | 4 ----
apps/client/src/translations/pt/translation.json | 4 ----
apps/client/src/translations/pt_br/translation.json | 4 ----
apps/client/src/translations/ro/translation.json | 4 ----
apps/client/src/translations/ru/translation.json | 4 ----
apps/client/src/translations/tw/translation.json | 4 ----
apps/client/src/translations/uk/translation.json | 4 ----
14 files changed, 56 deletions(-)
diff --git a/apps/client/src/translations/cn/translation.json b/apps/client/src/translations/cn/translation.json
index 1f748a99b3..628a9bb4af 100644
--- a/apps/client/src/translations/cn/translation.json
+++ b/apps/client/src/translations/cn/translation.json
@@ -1064,10 +1064,6 @@
"default_new_note_title": "新笔记",
"click_on_canvas_to_place_new_note": "点击画布以放置新笔记"
},
- "render": {
- "note_detail_render_help_1": "之所以显示此帮助说明,是因为这个类型为渲染 HTML 的笔记没有正常工作所需的关系。",
- "note_detail_render_help_2": "渲染 HTML 笔记类型用于编写脚本 。简而言之,您有一份 HTML 代码笔记(可包含一些 JavaScript),然后这个笔记会把页面渲染出来。要使其正常工作,您需要定义一个名为 \"renderNote\" 的关系 指向要渲染的 HTML 笔记。"
- },
"backend_log": {
"refresh": "刷新"
},
diff --git a/apps/client/src/translations/de/translation.json b/apps/client/src/translations/de/translation.json
index 0cfcb85b57..3ae48886c2 100644
--- a/apps/client/src/translations/de/translation.json
+++ b/apps/client/src/translations/de/translation.json
@@ -1063,10 +1063,6 @@
"default_new_note_title": "neue Notiz",
"click_on_canvas_to_place_new_note": "Klicke auf den Canvas, um eine neue Notiz zu platzieren"
},
- "render": {
- "note_detail_render_help_1": "Diese Hilfesnotiz wird angezeigt, da diese Notiz vom Typ „HTML rendern“ nicht über die erforderliche Beziehung verfügt, um ordnungsgemäß zu funktionieren.",
- "note_detail_render_help_2": "Render-HTML-Notiztyp wird benutzt für scripting . Kurzgesagt, du hast ein HTML-Code-Notiz (optional mit JavaScript) und diese Notiz rendert es. Damit es funktioniert, musst du eine a Beziehung namens \"renderNote\" zeigend auf die HTML-Notiz zum rendern definieren."
- },
"backend_log": {
"refresh": "Aktualisieren"
},
diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json
index 57323d6d0c..7ab3f90c83 100644
--- a/apps/client/src/translations/es/translation.json
+++ b/apps/client/src/translations/es/translation.json
@@ -1068,10 +1068,6 @@
"default_new_note_title": "nueva nota",
"click_on_canvas_to_place_new_note": "Haga clic en el lienzo para colocar una nueva nota"
},
- "render": {
- "note_detail_render_help_1": "Esta nota de ayuda se muestra porque esta nota de tipo Renderizar HTML no tiene la relación requerida para funcionar correctamente.",
- "note_detail_render_help_2": "El tipo de nota Render HTML es usado para scripting . De forma resumida, tiene una nota con código HTML (opcionalmente con algo de JavaScript) y esta nota la renderizará. Para que funcione, es necesario definir una relación llamada \"renderNote\" apuntando a la nota HTML nota a renderizar."
- },
"backend_log": {
"refresh": "Refrescar"
},
diff --git a/apps/client/src/translations/fr/translation.json b/apps/client/src/translations/fr/translation.json
index b1253d8d81..24bd81fc92 100644
--- a/apps/client/src/translations/fr/translation.json
+++ b/apps/client/src/translations/fr/translation.json
@@ -1053,10 +1053,6 @@
"default_new_note_title": "nouvelle note",
"click_on_canvas_to_place_new_note": "Cliquez sur le canevas pour placer une nouvelle note"
},
- "render": {
- "note_detail_render_help_1": "Cette note d'aide s'affiche car cette note de type Rendu HTML n'a pas la relation requise pour fonctionner correctement.",
- "note_detail_render_help_2": "Le type de note Rendu HTML est utilisé pour les scripts . En résumé, vous disposez d'une note de code HTML (éventuellement contenant JavaScript) et cette note affichera le rendu. Pour que cela fonctionne, vous devez définir une relation appelée \"renderNote\" pointant vers la note HTML à rendre."
- },
"backend_log": {
"refresh": "Rafraîchir"
},
diff --git a/apps/client/src/translations/ga/translation.json b/apps/client/src/translations/ga/translation.json
index 2f00a54ce6..cf404ad350 100644
--- a/apps/client/src/translations/ga/translation.json
+++ b/apps/client/src/translations/ga/translation.json
@@ -1072,10 +1072,6 @@
"default_new_note_title": "nóta nua",
"click_on_canvas_to_place_new_note": "Cliceáil ar chanbhás chun nóta nua a chur"
},
- "render": {
- "note_detail_render_help_1": "Taispeántar an nóta cabhrach seo mar nach bhfuil aon ghaol riachtanach ag an nóta seo den chineál Render HTML le go bhfeidhmeoidh sé i gceart.",
- "note_detail_render_help_2": "Úsáidtear cineál nóta HTML rindreála le haghaidh scriptithe . Go hachomair, tá nóta cóid HTML agat (le roinnt JavaScript más féidir) agus déanfaidh an nóta seo é a rindreáil. Chun go n-oibreoidh sé, ní mór duit gaol ar a dtugtar \"renderNote\" a shainiú ag pointeáil chuig an nóta HTML atá le rindreáil."
- },
"backend_log": {
"refresh": "Athnuachan"
},
diff --git a/apps/client/src/translations/it/translation.json b/apps/client/src/translations/it/translation.json
index d994317a82..088361d4ab 100644
--- a/apps/client/src/translations/it/translation.json
+++ b/apps/client/src/translations/it/translation.json
@@ -1582,10 +1582,6 @@
"default_new_note_title": "nuova nota",
"click_on_canvas_to_place_new_note": "Clicca sulla tela per inserire una nuova nota"
},
- "render": {
- "note_detail_render_help_1": "Questa nota di aiuto viene visualizzata perché questa nota di tipo Render HTML non ha la relazione richiesta per funzionare correttamente.",
- "note_detail_render_help_2": "Il tipo di nota HTML Render viene utilizzato per lo scripting . In breve, si ottiene una nota in codice HTML (opzionalmente con un po' di JavaScript) che verrà visualizzata. Per farla funzionare, è necessario definire una relazione denominata \"renderNote\" che punti alla nota HTML da visualizzare."
- },
"vacuum_database": {
"title": "Pulizia del database",
"description": "Questa operazione ricostruirà il database, generando in genere un file di dimensioni inferiori. In realtà, nessun dato verrà modificato.",
diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json
index abf330b0d3..b38724068e 100644
--- a/apps/client/src/translations/ja/translation.json
+++ b/apps/client/src/translations/ja/translation.json
@@ -1860,10 +1860,6 @@
"protecting-title": "保護の状態",
"unprotecting-title": "保護解除の状態"
},
- "render": {
- "note_detail_render_help_1": "このヘルプノートが表示されるのは、このノートの「HTML のレンダリング」タイプには、正常に機能するために必要なリレーションがないためです。",
- "note_detail_render_help_2": "レンダリングHTMLノートタイプは、スクリプティング に使用されます。簡単に言うと、HTMLコードノート(オプションでJavaScriptを含む)があり、このノートがそれをレンダリングします。これを動作させるには、レンダリングするHTMLノートを指す「renderNote」というリレーション を定義する必要があります。"
- },
"consistency_checks": {
"find_and_fix_button": "一貫性の問題を見つけて修正する",
"finding_and_fixing_message": "一貫性の問題を見つけて修正中…",
diff --git a/apps/client/src/translations/pl/translation.json b/apps/client/src/translations/pl/translation.json
index 3f8174ada8..a8f19da2d3 100644
--- a/apps/client/src/translations/pl/translation.json
+++ b/apps/client/src/translations/pl/translation.json
@@ -1432,10 +1432,6 @@
"default_new_note_title": "nowa notatka",
"click_on_canvas_to_place_new_note": "Kliknij na płótnie, aby umieścić nową notatkę"
},
- "render": {
- "note_detail_render_help_1": "Ta notatka pomocy jest wyświetlana, ponieważ ta notatka typu Render HTML nie ma wymaganej relacji do poprawnego działania.",
- "note_detail_render_help_2": "Typ notatki Render HTML jest używany do skryptowania . W skrócie, masz notatkę kodu HTML (opcjonalnie z JavaScript) i ta notatka ją wyrenderuje. Aby to zadziałało, musisz zdefiniować relację o nazwie \"renderNote\" wskazującą na notatkę HTML do wyrenderowania."
- },
"backend_log": {
"refresh": "Odśwież"
},
diff --git a/apps/client/src/translations/pt/translation.json b/apps/client/src/translations/pt/translation.json
index 510ec70de7..2d5069ef36 100644
--- a/apps/client/src/translations/pt/translation.json
+++ b/apps/client/src/translations/pt/translation.json
@@ -1064,10 +1064,6 @@
"default_new_note_title": "nova nota",
"click_on_canvas_to_place_new_note": "Clique no quadro para incluir uma nova nota"
},
- "render": {
- "note_detail_render_help_1": "Esta nota de ajuda é mostrada porque esta nota do tipo Renderizar HTML não possui a relação necessária para funcionar corretamente.",
- "note_detail_render_help_2": "O tipo de nota Renderizar HTML é usado para automação . Em suma, tem uma nota de código HTML (opcionalmente com algum JavaScript) e esta nota irá renderizá-la. Para fazê-lo funcionar, deve definir uma relação chamada \"renderNote\" que aponta para a nota HTML a ser renderizada."
- },
"backend_log": {
"refresh": "Recarregar"
},
diff --git a/apps/client/src/translations/pt_br/translation.json b/apps/client/src/translations/pt_br/translation.json
index 99b5f94eb5..ea67adebd0 100644
--- a/apps/client/src/translations/pt_br/translation.json
+++ b/apps/client/src/translations/pt_br/translation.json
@@ -1991,10 +1991,6 @@
"drag_locked_title": "Bloqueado para edição",
"drag_locked_message": "Arrastar não é permitido pois a coleção está bloqueada para edição."
},
- "render": {
- "note_detail_render_help_1": "Esta nota de ajuda é mostrada porque esta nota do tipo Renderizar HTML não possui a relação necessária para funcionar corretamente.",
- "note_detail_render_help_2": "O tipo de nota Renderizar HTML é usado para automação . Em suma, você tem uma nota de código HTML (opcionalmente com algum JavaScript) e esta nota irá renderizá-la. Para fazê-lo funcionar, você precisa definir uma relação chamada \"renderNote\" apontando para a nota HTML a ser renderizada."
- },
"etapi": {
"title": "ETAPI",
"description": "ETAPI é uma API REST usada para acessar a instância do Trilium programaticamente, sem interface gráfica.",
diff --git a/apps/client/src/translations/ro/translation.json b/apps/client/src/translations/ro/translation.json
index 8f6d8d0a6c..3807f39f1d 100644
--- a/apps/client/src/translations/ro/translation.json
+++ b/apps/client/src/translations/ro/translation.json
@@ -1094,10 +1094,6 @@
"rename_relation_from": "Redenumește relația din",
"to": "În"
},
- "render": {
- "note_detail_render_help_1": "Această notă informativă este afișată deoarece această notiță de tip „Randare HTML” nu are relația necesară pentru a funcționa corespunzător.",
- "note_detail_render_help_2": "Notița de tipul „Render HTML” este utilizată pentru scriptare . Pe scurt, se folosește o notiță de tip cod HTML (opțional cu niște JavaScript) și această notiță o va randa. Pentru a funcționa, trebuie definită o relație denumită „renderNote” ce indică notița HTML de randat."
- },
"revisions": {
"confirm_delete": "Doriți ștergerea acestei revizii?",
"confirm_delete_all": "Doriți ștergerea tuturor reviziilor acestei notițe?",
diff --git a/apps/client/src/translations/ru/translation.json b/apps/client/src/translations/ru/translation.json
index 5cd6f94ecb..802de4fe8d 100644
--- a/apps/client/src/translations/ru/translation.json
+++ b/apps/client/src/translations/ru/translation.json
@@ -2081,10 +2081,6 @@
"help-button": {
"title": "Открыть соответствующую страницу справки"
},
- "render": {
- "note_detail_render_help_2": "Тип заметки «Рендер HTML» используется для скриптинга . Если коротко, у вас есть заметка с HTML-кодом (возможно, с добавлением JavaScript), и эта заметка её отобразит. Для этого необходимо определить отношение с именем «renderNote», указывающее на HTML-заметку для отрисовки.",
- "note_detail_render_help_1": "Эта справочная заметка отображается, поскольку эта справка типа Render HTML не имеет необходимой связи для правильной работы."
- },
"file": {
"too_big": "В целях повышения производительности в режиме предварительного просмотра отображаются только первые {{maxNumChars}} символов файла. Загрузите файл и откройте его во внешнем браузере, чтобы увидеть всё содержимое.",
"file_preview_not_available": "Предварительный просмотр файла недоступен для этого файла."
diff --git a/apps/client/src/translations/tw/translation.json b/apps/client/src/translations/tw/translation.json
index 7205bf7177..9575cbf0f6 100644
--- a/apps/client/src/translations/tw/translation.json
+++ b/apps/client/src/translations/tw/translation.json
@@ -1063,10 +1063,6 @@
"default_new_note_title": "新筆記",
"click_on_canvas_to_place_new_note": "點擊畫布以放置新筆記"
},
- "render": {
- "note_detail_render_help_1": "之所以顯示此說明筆記,是因為該類型的渲染 HTML 沒有設定好必須的關聯。",
- "note_detail_render_help_2": "渲染筆記類型用於編寫 腳本 。簡單說就是您可以寫HTML程式碼(或者加上一些JavaScript程式碼), 然後這個筆記會把頁面渲染出來。要使其正常工作,您需要定義一個名為 \"renderNote\" 的 關聯 指向要呈現的 HTML 筆記。"
- },
"backend_log": {
"refresh": "重新整理"
},
diff --git a/apps/client/src/translations/uk/translation.json b/apps/client/src/translations/uk/translation.json
index 032fd70393..d152216274 100644
--- a/apps/client/src/translations/uk/translation.json
+++ b/apps/client/src/translations/uk/translation.json
@@ -1130,10 +1130,6 @@
"default_new_note_title": "нова нотатка",
"click_on_canvas_to_place_new_note": "Натисніть на полотно, щоб розмістити нову нотатку"
},
- "render": {
- "note_detail_render_help_1": "Ця довідка відображається, оскільки ця нотатка типу Render HTML не має необхідного зв'язку для належного функціонування.",
- "note_detail_render_help_2": "Тип нотатки Render HTML використовується для скриптів . Коротше кажучи, у вас є нотатка з HTML-кодом (за бажанням з деяким JavaScript), і ця нотатка її відобразить. Щоб це запрацювало, вам потрібно визначити відношення під назвою \"renderNote\", яке вказує на нотатку HTML для відображення."
- },
"backend_log": {
"refresh": "Оновити"
},
From 46a414e1559d094006c9f8acccf466e3768e4302 Mon Sep 17 00:00:00 2001
From: Ulices
Date: Sun, 15 Feb 2026 17:05:50 +0100
Subject: [PATCH 126/247] Translated using Weblate (Spanish)
Currently translated at 100.0% (1806 of 1806 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/
---
.../src/translations/es/translation.json | 39 ++++++++++++++++++-
1 file changed, 38 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json
index 7ab3f90c83..8194b4407f 100644
--- a/apps/client/src/translations/es/translation.json
+++ b/apps/client/src/translations/es/translation.json
@@ -2298,6 +2298,43 @@
"url_placeholder": "Ingresar o pegar la dirección del sitio web, por ejemplo https://triliumnotes.org",
"create_button": "Crear Vista Web",
"invalid_url_title": "Dirección inválida",
- "invalid_url_message": "Ingrese una dirección web válida, por ejemplo https://triliumnotes.org."
+ "invalid_url_message": "Ingrese una dirección web válida, por ejemplo https://triliumnotes.org.",
+ "disabled_description": "Esta vista web fue importada de una fuente externa. Para ayudarlo a protegerse del phishing o el contenido malicioso, no se está cargando automáticamente. Puede activarlo si confía en la fuente.",
+ "disabled_button_enable": "Habilita vista web"
+ },
+ "render": {
+ "setup_title": "Mostrar HTML personalizado o Preact JSX dentro de esta nota",
+ "setup_create_sample_preact": "Crear nota de muestra con Preact",
+ "setup_create_sample_html": "Crear nota de muestra con HTML",
+ "setup_sample_created": "Se creó una nota de muestra como subnota.",
+ "disabled_description": "Esta nota de renderización proviene de una fuente externa. Para protegerlo de contenido malicioso, no está habilitado por defecto. Asegúrese de confiar en la fuente antes de habilitarla.",
+ "disabled_button_enable": "Habilitar nota de renderización"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "Paquete de iconos",
+ "type_backend_script": "Script de backend",
+ "type_frontend_script": "Script de frontend",
+ "type_widget": "Widget",
+ "type_app_css": "CSS personalizado",
+ "type_render_note": "Nota de renderización",
+ "type_web_view": "Vista web",
+ "type_app_theme": "Tema personalizado",
+ "toggle_tooltip_enable_tooltip": "Haga clic para habilitar este {{type}}.",
+ "toggle_tooltip_disable_tooltip": "Haga clic para deshabilitar este {{type}}.",
+ "menu_docs": "Abrir documentación",
+ "menu_execute_now": "Ejecutar script ahora",
+ "menu_run": "Ejecutar automáticamente",
+ "menu_run_disabled": "Manualmente",
+ "menu_run_backend_startup": "Cuando el backend inicia",
+ "menu_run_hourly": "Cada hora",
+ "menu_run_daily": "Diariamente",
+ "menu_run_frontend_startup": "Cuando el frontend de escritorio inicia",
+ "menu_run_mobile_startup": "Cuando el frontend móvil inicia",
+ "menu_change_to_widget": "Cambiar a widget",
+ "menu_change_to_frontend_script": "Cambiar a script de frontend",
+ "menu_theme_base": "Tema base"
+ },
+ "setup_form": {
+ "more_info": "Para saber más"
}
}
From 41cd4bcef64624e7e241c162909eca1a2a0b0cf3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aindri=C3=BA=20Mac=20Giolla=20Eoin?=
Date: Sun, 15 Feb 2026 17:41:09 +0100
Subject: [PATCH 127/247] Translated using Weblate (Irish)
Currently translated at 100.0% (1807 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
---
.../src/translations/ga/translation.json | 42 ++++++++++++++++++-
1 file changed, 40 insertions(+), 2 deletions(-)
diff --git a/apps/client/src/translations/ga/translation.json b/apps/client/src/translations/ga/translation.json
index cf404ad350..94a4c7c009 100644
--- a/apps/client/src/translations/ga/translation.json
+++ b/apps/client/src/translations/ga/translation.json
@@ -2108,7 +2108,8 @@
"raster": "Raster",
"vector_light": "Veicteoir (Solas)",
"vector_dark": "Veicteoir (Dorcha)",
- "show-scale": "Taispeáin scála"
+ "show-scale": "Taispeáin scála",
+ "show-labels": "Taispeáin ainmneacha marcóirí"
},
"table_context_menu": {
"delete_row": "Scrios an tsraith"
@@ -2328,6 +2329,43 @@
"url_placeholder": "Cuir isteach nó greamaigh seoladh an tsuímh ghréasáin, mar shampla https://triliumnotes.org",
"create_button": "Cruthaigh Radharc Gréasáin",
"invalid_url_title": "Seoladh neamhbhailí",
- "invalid_url_message": "Cuir isteach seoladh gréasáin bailí, mar shampla https://triliumnotes.org."
+ "invalid_url_message": "Cuir isteach seoladh gréasáin bailí, mar shampla https://triliumnotes.org.",
+ "disabled_description": "Iompórtáladh an radharc gréasáin seo ó fhoinse sheachtrach. Chun cabhrú leat a chosaint ar ábhar fioscaireachta nó mailíseach, níl sé ag lódáil go huathoibríoch. Is féidir leat é a chumasú má tá muinín agat as an bhfoinse.",
+ "disabled_button_enable": "Cumasaigh radharc gréasáin"
+ },
+ "render": {
+ "setup_title": "Taispeáin HTML saincheaptha nó Preact JSX taobh istigh den nóta seo",
+ "setup_create_sample_preact": "Cruthaigh nóta samplach le Preact",
+ "setup_create_sample_html": "Cruthaigh nóta samplach le HTML",
+ "setup_sample_created": "Cruthaíodh nóta samplach mar nóta linbh.",
+ "disabled_description": "Tagann na nótaí rindreála seo ó fhoinse sheachtrach. Chun tú a chosaint ar ábhar mailíseach, níl sé cumasaithe de réir réamhshocraithe. Déan cinnte go bhfuil muinín agat as an bhfoinse sula gcumasaíonn tú é.",
+ "disabled_button_enable": "Cumasaigh nóta rindreála"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "Pacáiste deilbhín",
+ "type_backend_script": "Script chúltaca",
+ "type_frontend_script": "Script tosaigh",
+ "type_widget": "Giuirléid",
+ "type_app_css": "CSS saincheaptha",
+ "type_render_note": "Nóta rindreála",
+ "type_web_view": "Radharc gréasáin",
+ "type_app_theme": "Téama saincheaptha",
+ "toggle_tooltip_enable_tooltip": "Cliceáil chun an {{type}} seo a chumasú.",
+ "toggle_tooltip_disable_tooltip": "Cliceáil chun an {{type}} seo a dhíchumasú.",
+ "menu_docs": "Doiciméadú oscailte",
+ "menu_execute_now": "Rith an script anois",
+ "menu_run": "Rith go huathoibríoch",
+ "menu_run_disabled": "De láimh",
+ "menu_run_backend_startup": "Nuair a thosaíonn an cúltaca",
+ "menu_run_hourly": "Gach uair an chloig",
+ "menu_run_daily": "Laethúil",
+ "menu_run_frontend_startup": "Nuair a thosaíonn tosaigh an deisce",
+ "menu_run_mobile_startup": "Nuair a thosaíonn an taobhlíne soghluaiste",
+ "menu_change_to_widget": "Athraigh go giuirléid",
+ "menu_change_to_frontend_script": "Athraigh chuig an script tosaigh",
+ "menu_theme_base": "Bunús téama"
+ },
+ "setup_form": {
+ "more_info": "Foghlaim níos mó"
}
}
From 82576c9703add51a84e8873e90c0f31c008f63be Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Mon, 16 Feb 2026 01:51:32 +0200
Subject: [PATCH 128/247] ui/pager: rewrite the pagination button distribution
algorithm to better control the length
---
.../src/translations/en/translation.json | 1 -
.../src/widgets/collections/Pagination.tsx | 72 +++++++++++++------
2 files changed, 49 insertions(+), 24 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index e97aba782b..c1dcbf48f2 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2190,7 +2190,6 @@
"percentage": "%"
},
"pagination": {
- "page_title": "Page of {{startIndex}} - {{endIndex}}",
"total_notes": "{{count}} notes",
"prev_page": "Previous page",
"next_page": "Next page"
diff --git a/apps/client/src/widgets/collections/Pagination.tsx b/apps/client/src/widgets/collections/Pagination.tsx
index 282a6b36a5..5704715441 100644
--- a/apps/client/src/widgets/collections/Pagination.tsx
+++ b/apps/client/src/widgets/collections/Pagination.tsx
@@ -19,28 +19,7 @@ interface PaginationContext {
export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit) {
if (pageCount < 2) return;
- let lastPrinted = false;
- let children: ComponentChildren[] = [];
- for (let i = 1; i <= pageCount; i++) {
- if (pageCount < 20 || i <= 5 || pageCount - i <= 5 || Math.abs(page - i) <= 2) {
- lastPrinted = true;
-
- const startIndex = (i - 1) * pageSize + 1;
- const endIndex = Math.min(totalNotes, i * pageSize);
-
- children.push((
- setPage(i)}
- />
- ))
- } else if (lastPrinted) {
- children.push(<>{"... "} {" "}>);
- lastPrinted = false;
- }
- }
+ const children = createPageButtons(page, setPage, pageCount);
return (
)
}
+function createPageButtons(page: number, setPage: Dispatch>, pageCount: number): ComponentChildren[] {
+ const maxButtonCount = 9;
+ const maxLeftRightSegmentLength = 2;
+
+ // The left-side segment
+ const leftLength = Math.min(pageCount, maxLeftRightSegmentLength);
+ const leftStart = 1;
+
+ // The middle segment
+ const middleMaxLength = maxButtonCount - maxLeftRightSegmentLength * 2;
+ const middleLength = Math.min(pageCount - leftLength, middleMaxLength);
+ let middleStart = page - Math.floor(middleLength / 2);
+ middleStart = Math.max(middleStart, leftLength + 1);
+
+ // The right-side segment
+ const rightLength = Math.min(pageCount - (middleLength + leftLength), maxLeftRightSegmentLength);
+ const rightStart = pageCount - rightLength + 1;
+ middleStart = Math.min(middleStart, rightStart - middleLength);
+
+ return [
+ ...createSegment(leftStart, leftLength, page, setPage, false),
+ ...createSegment(middleStart, middleLength, page, setPage, (middleStart - leftLength > 1)),
+ ...createSegment(rightStart, rightLength, page, setPage, (rightStart - (middleStart + middleLength) > 1)),
+ ];
+}
+
+function createSegment(start: number, length: number, currentPage: number, setPage: Dispatch>, prependEllipsis: boolean): ComponentChildren[] {
+ const children: ComponentChildren[] = [];
+
+ if (prependEllipsis) {
+ children.push("...");
+ }
+
+ for (let i = 0; i < length; i++) {
+ const pageNum = start + i;
+
+ children.push((
+ setPage(pageNum)}
+ />
+ ));
+ }
+
+ return children;
+}
+
export function usePagination(note: FNote, noteIds: string[]): PaginationContext {
const [ page, setPage ] = useState(1);
const [ pageNotes, setPageNotes ] = useState();
From ea25477e3dac5fb85f3940ca61523085868ce788 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Mon, 16 Feb 2026 02:06:39 +0200
Subject: [PATCH 129/247] ui/pager: fix some issues
---
apps/client/src/widgets/collections/Pagination.tsx | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/widgets/collections/Pagination.tsx b/apps/client/src/widgets/collections/Pagination.tsx
index 5704715441..b152e59638 100644
--- a/apps/client/src/widgets/collections/Pagination.tsx
+++ b/apps/client/src/widgets/collections/Pagination.tsx
@@ -27,7 +27,7 @@ export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit {setPage(--page)}}
+ onClick={() => {setPage(page - 1)}}
/>
{children}
@@ -36,7 +36,7 @@ export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit
{setPage(++page)}}
+ onClick={() => {setPage(page + 1)}}
/>
({t("pagination.total_notes", { count: totalNotes })})
@@ -65,8 +65,9 @@ function createPageButtons(page: number, setPage: Dispatch
>
return [
...createSegment(leftStart, leftLength, page, setPage, false),
...createSegment(middleStart, middleLength, page, setPage, (middleStart - leftLength > 1)),
- ...createSegment(rightStart, rightLength, page, setPage, (rightStart - (middleStart + middleLength) > 1)),
+ ...createSegment(rightStart, rightLength, page, setPage, (rightStart - (middleStart + middleLength - 1) > 1)),
];
+
}
function createSegment(start: number, length: number, currentPage: number, setPage: Dispatch>, prependEllipsis: boolean): ComponentChildren[] {
From 9f11717b69e2428b647db893d40fa4786ffbb612 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 16 Feb 2026 02:36:40 +0000
Subject: [PATCH 130/247] fix(deps): update dependency mind-elixir to v5.8.1
---
apps/client/package.json | 2 +-
pnpm-lock.yaml | 21 +++++++++++----------
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/apps/client/package.json b/apps/client/package.json
index f0ce125f49..85a31b7a41 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -56,7 +56,7 @@
"mark.js": "8.11.1",
"marked": "17.0.2",
"mermaid": "11.12.2",
- "mind-elixir": "5.8.0",
+ "mind-elixir": "5.8.1",
"normalize.css": "8.0.1",
"panzoom": "9.4.3",
"preact": "10.28.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6a088a22ca..271b88775c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -208,7 +208,7 @@ importers:
version: 0.2.0(mermaid@11.12.2)
'@mind-elixir/node-menu':
specifier: 5.0.1
- version: 5.0.1(mind-elixir@5.8.0)
+ version: 5.0.1(mind-elixir@5.8.1)
'@popperjs/core':
specifier: 2.11.8
version: 2.11.8
@@ -300,8 +300,8 @@ importers:
specifier: 11.12.2
version: 11.12.2
mind-elixir:
- specifier: 5.8.0
- version: 5.8.0
+ specifier: 5.8.1
+ version: 5.8.1
normalize.css:
specifier: 8.0.1
version: 8.0.1
@@ -11052,8 +11052,8 @@ packages:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
- mind-elixir@5.8.0:
- resolution: {integrity: sha512-/UpfwZoiySbXoc6nUIK5DSo8HK5vKtW8tnQz63vuoCvuVU3xj1j2mfmMTnGgiV5NoAKxSyAD59RMBpJfD0kbPw==}
+ mind-elixir@5.8.1:
+ resolution: {integrity: sha512-CLJyE22fW0/jPaksqfdEHmS32Ni+Ekrd3cOt8I8lYOX/pqoMWDpV9OaI/ycTTQE4aMSIBu6tAgw50lTqnhI/OQ==}
mini-css-extract-plugin@2.9.4:
resolution: {integrity: sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==}
@@ -14081,6 +14081,7 @@ packages:
tar@7.5.7:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==}
engines: {node: '>=18'}
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
temp@0.9.4:
resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==}
@@ -16386,8 +16387,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
- transitivePeerDependencies:
- - supports-color
'@ckeditor/ckeditor5-editor-multi-root@47.4.0':
dependencies:
@@ -16897,6 +16896,8 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-restricted-editing@47.4.0':
dependencies:
@@ -19239,9 +19240,9 @@ snapshots:
'@microsoft/tsdoc@0.15.1': {}
- '@mind-elixir/node-menu@5.0.1(mind-elixir@5.8.0)':
+ '@mind-elixir/node-menu@5.0.1(mind-elixir@5.8.1)':
dependencies:
- mind-elixir: 5.8.0
+ mind-elixir: 5.8.1
'@mixmark-io/domino@2.2.0': {}
@@ -27876,7 +27877,7 @@ snapshots:
mimic-response@3.1.0: {}
- mind-elixir@5.8.0: {}
+ mind-elixir@5.8.1: {}
mini-css-extract-plugin@2.9.4(webpack@5.101.3(@swc/core@1.11.29(@swc/helpers@0.5.17))(esbuild@0.27.3)):
dependencies:
From ae224151a0f7deb9165487c003994ff3cdeb45c6 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 16 Feb 2026 12:47:46 +0000
Subject: [PATCH 131/247] fix(deps): update dependency i18next to v25.8.10
---
apps/client/package.json | 2 +-
apps/server/package.json | 2 +-
apps/website/package.json | 2 +-
pnpm-lock.yaml | 35 ++++++++++++++++++++++-------------
4 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/apps/client/package.json b/apps/client/package.json
index f0ce125f49..c4da55ca39 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -44,7 +44,7 @@
"draggabilly": "3.0.0",
"force-graph": "1.51.1",
"globals": "17.3.0",
- "i18next": "25.8.7",
+ "i18next": "25.8.10",
"i18next-http-backend": "3.0.2",
"jquery": "4.0.0",
"jquery.fancytree": "2.38.5",
diff --git a/apps/server/package.json b/apps/server/package.json
index 8609efd8d3..222074e6ce 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -99,7 +99,7 @@
"html2plaintext": "2.1.4",
"http-proxy-agent": "7.0.2",
"https-proxy-agent": "7.0.6",
- "i18next": "25.8.7",
+ "i18next": "25.8.10",
"i18next-fs-backend": "2.6.1",
"image-type": "6.0.0",
"ini": "6.0.0",
diff --git a/apps/website/package.json b/apps/website/package.json
index 23c55aec75..3d52c6cf4a 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -9,7 +9,7 @@
"preview": "pnpm build && vite preview"
},
"dependencies": {
- "i18next": "25.8.7",
+ "i18next": "25.8.10",
"i18next-http-backend": "3.0.2",
"preact": "10.28.3",
"preact-iso": "2.11.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6a088a22ca..10208c944d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -264,8 +264,8 @@ importers:
specifier: 17.3.0
version: 17.3.0
i18next:
- specifier: 25.8.7
- version: 25.8.7(typescript@5.9.3)
+ specifier: 25.8.10
+ version: 25.8.10(typescript@5.9.3)
i18next-http-backend:
specifier: 3.0.2
version: 3.0.2(encoding@0.1.13)
@@ -313,7 +313,7 @@ importers:
version: 10.28.3
react-i18next:
specifier: 16.5.4
- version: 16.5.4(i18next@25.8.7(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
+ version: 16.5.4(i18next@25.8.10(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
react-window:
specifier: 2.2.7
version: 2.2.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -728,8 +728,8 @@ importers:
specifier: 7.0.6
version: 7.0.6
i18next:
- specifier: 25.8.7
- version: 25.8.7(typescript@5.9.3)
+ specifier: 25.8.10
+ version: 25.8.10(typescript@5.9.3)
i18next-fs-backend:
specifier: 2.6.1
version: 2.6.1
@@ -849,8 +849,8 @@ importers:
apps/website:
dependencies:
i18next:
- specifier: 25.8.7
- version: 25.8.7(typescript@5.9.3)
+ specifier: 25.8.10
+ version: 25.8.10(typescript@5.9.3)
i18next-http-backend:
specifier: 3.0.2
version: 3.0.2(encoding@0.1.13)
@@ -865,7 +865,7 @@ importers:
version: 6.6.5(preact@10.28.3)
react-i18next:
specifier: 16.5.4
- version: 16.5.4(i18next@25.8.7(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
+ version: 16.5.4(i18next@25.8.10(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
devDependencies:
'@preact/preset-vite':
specifier: 2.10.3
@@ -9622,8 +9622,8 @@ packages:
i18next-http-backend@3.0.2:
resolution: {integrity: sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==}
- i18next@25.8.7:
- resolution: {integrity: sha512-ttxxc5+67S/0hhoeVdEgc1lRklZhdfcUSEPp1//uUG2NB88X3667gRsDar+ZWQFdysnOsnb32bcoMsa4mtzhkQ==}
+ i18next@25.8.10:
+ resolution: {integrity: sha512-CtPJLMAz1G8sxo+mIzfBjGgLxWs7d6WqIjlmmv9BTsOat4pJIfwZ8cm07n3kFS6bP9c6YwsYutYrwsEeJVBo2g==}
peerDependencies:
typescript: ^5
peerDependenciesMeta:
@@ -14081,6 +14081,7 @@ packages:
tar@7.5.7:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==}
engines: {node: '>=18'}
+ deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
temp@0.9.4:
resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==}
@@ -16167,6 +16168,8 @@ snapshots:
'@ckeditor/ckeditor5-core': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-code-block@47.4.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
dependencies:
@@ -16359,6 +16362,8 @@ snapshots:
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
es-toolkit: 1.39.5
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-editor-classic@47.4.0':
dependencies:
@@ -16897,6 +16902,8 @@ snapshots:
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
ckeditor5: 47.4.0
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-restricted-editing@47.4.0':
dependencies:
@@ -17094,6 +17101,8 @@ snapshots:
'@ckeditor/ckeditor5-icons': 47.4.0
'@ckeditor/ckeditor5-ui': 47.4.0
'@ckeditor/ckeditor5-utils': 47.4.0
+ transitivePeerDependencies:
+ - supports-color
'@ckeditor/ckeditor5-upload@47.4.0':
dependencies:
@@ -26195,7 +26204,7 @@ snapshots:
transitivePeerDependencies:
- encoding
- i18next@25.8.7(typescript@5.9.3):
+ i18next@25.8.10(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.4
optionalDependencies:
@@ -29638,11 +29647,11 @@ snapshots:
react: 19.2.4
scheduler: 0.27.0
- react-i18next@16.5.4(i18next@25.8.7(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
+ react-i18next@16.5.4(i18next@25.8.10(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.4
html-parse-stringify: 3.0.1
- i18next: 25.8.7(typescript@5.9.3)
+ i18next: 25.8.10(typescript@5.9.3)
react: 19.2.4
use-sync-external-store: 1.6.0(react@19.2.4)
optionalDependencies:
From df96c6b9fa841aa2e5576c258eb8cf5b7decb3eb Mon Sep 17 00:00:00 2001
From: green
Date: Mon, 16 Feb 2026 03:12:06 +0100
Subject: [PATCH 132/247] Translated using Weblate (Japanese)
Currently translated at 99.3% (1796 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
---
.../src/translations/ja/translation.json | 30 +++++++++++++++++--
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json
index b38724068e..3bd99204e8 100644
--- a/apps/client/src/translations/ja/translation.json
+++ b/apps/client/src/translations/ja/translation.json
@@ -2036,7 +2036,8 @@
"show-scale": "スケールを表示",
"raster": "Raster",
"vector_light": "Vector(ライト)",
- "vector_dark": "Vector (ダーク)"
+ "vector_dark": "Vector (ダーク)",
+ "show-labels": "マーカー名を表示"
},
"call_to_action": {
"next_theme_title": "新しいTriliumテーマをお試しください",
@@ -2093,7 +2094,7 @@
"no_attachments": "このノートには添付ファイルはありません。"
},
"book": {
- "no_children_help": "このコレクションには子ノートがないため、表示するものがありません。詳細はwiki をご覧ください。",
+ "no_children_help": "このコレクションには子ノートがないため、表示するものがありません。",
"drag_locked_title": "編集をロック中",
"drag_locked_message": "コレクションは編集がロックされているため、ドラッグは許可されていません。"
},
@@ -2268,6 +2269,29 @@
"url_placeholder": "Web サイトのアドレスを入力または貼り付けて下さい。 例: https://triliumnotes.org",
"create_button": "Web ビューを作成",
"invalid_url_title": "無効なアドレス",
- "invalid_url_message": "有効な Web アドレスを入力してください。 例: https://triliumnotes.org"
+ "invalid_url_message": "有効な Web アドレスを入力してください。 例: https://triliumnotes.org",
+ "disabled_description": "この Web ビューは外部ソースからインポートされました。フィッシングや悪意のあるコンテンツから保護するため、自動的には読み込まれません。ソースを信頼できる場合は、有効にすることができます。",
+ "disabled_button_enable": "Web ビューを有効"
+ },
+ "render": {
+ "setup_title": "このノート内にカスタム HTML または Preact JSX を表示",
+ "setup_create_sample_preact": "Preact でサンプルノートを作成",
+ "setup_create_sample_html": "HTML でサンプルノートを作成",
+ "setup_sample_created": "子ノートとしてサンプルノートが作成されました。",
+ "disabled_description": "このレンダリングノートは外部ソースから提供されています。悪意のあるコンテンツからユーザーを保護するため、デフォルトでは有効になっていません。有効にする前に、ソースが信頼できるかどうかをご確認ください。",
+ "disabled_button_enable": "レンダリングノートを有効"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "アイコンパック",
+ "type_backend_script": "バックエンドスクリプト",
+ "type_frontend_script": "フロントエンドスクリプト",
+ "type_widget": "ウィジェット",
+ "type_app_css": "カスタム CSS",
+ "type_render_note": "レンダリングノート",
+ "type_web_view": "Web ビュー",
+ "type_app_theme": "カスタムテーマ",
+ "toggle_tooltip_enable_tooltip": "この {{type}} を有効にするにはクリックしてください。",
+ "toggle_tooltip_disable_tooltip": "この {{type}} を無効にするにはクリックしてください。",
+ "menu_docs": "ドキュメントを開く"
}
}
From 309a55f276da35327b009381bfe432a444e423c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aindri=C3=BA=20Mac=20Giolla=20Eoin?=
Date: Mon, 16 Feb 2026 11:56:02 +0100
Subject: [PATCH 133/247] Translated using Weblate (Irish)
Currently translated at 100.0% (1807 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ga/
---
apps/client/src/translations/ga/translation.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/client/src/translations/ga/translation.json b/apps/client/src/translations/ga/translation.json
index 94a4c7c009..42ea836265 100644
--- a/apps/client/src/translations/ga/translation.json
+++ b/apps/client/src/translations/ga/translation.json
@@ -1016,7 +1016,7 @@
"no_attachments": "Níl aon cheangaltáin leis an nóta seo."
},
"book": {
- "no_children_help": "Níl aon nótaí faoi mhíbhuntáiste sa bhailiúchán seo mar sin níl aon rud le taispeáint. Féach ar an vicí le haghaidh tuilleadh sonraí.",
+ "no_children_help": "Níl aon nótaí faoi mhíbhuntáiste sa bhailiúchán seo mar sin níl aon rud le taispeáint.",
"drag_locked_title": "Glasáilte le haghaidh eagarthóireachta",
"drag_locked_message": "Ní cheadaítear tarraingt ós rud é go bhfuil an bailiúchán faoi ghlas le haghaidh eagarthóireachta."
},
From 58a41e58eec5b582e5acaead716c365c9bb1c504 Mon Sep 17 00:00:00 2001
From: Toto Yullian
Date: Mon, 16 Feb 2026 10:28:16 +0100
Subject: [PATCH 134/247] Translated using Weblate (Indonesian)
Currently translated at 71.5% (83 of 116 strings)
Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/id/
---
docs/README-id.md | 33 +++++++++++++++++----------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/docs/README-id.md b/docs/README-id.md
index 525e0d34ff..54d2ace045 100644
--- a/docs/README-id.md
+++ b/docs/README-id.md
@@ -153,26 +153,26 @@ Tidak ada langkah migrasi khusus untuk bermigrasi dari zadam/Trilium ke
TriliumNext/Trilium. Cukup [instal TriliumNext/Trilium](#-installation) seperti
biasa dan akan menggunakan basis data yang sudah ada.
-Versions up to and including
-[v0.90.4](https://github.com/TriliumNext/Trilium/releases/tag/v0.90.4) are
-compatible with the latest zadam/trilium version of
-[v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Any later
-versions of TriliumNext/Trilium have their sync versions incremented which
-prevents direct migration.
+Versi hingga
+[v0.90.4](https://github.com/TriliumNext/Trilium/releases/tag/v0.90.4)
+kompatibel dengan versi Zadam/Trilium terbaru yaitu
+[v0.63.7](https://github.com/zadam/trilium/releases/tag/v0.63.7). Versi
+TriliumNext/Trilium yang lebih baru memiliki versi sinkronisasi yang
+ditingkatkan sehingga mencegah migrasi langsung.
-## 💬 Discuss with us
+## 💬 Mari berdiskusi dengan kami
-Feel free to join our official conversations. We would love to hear what
-features, suggestions, or issues you may have!
+Jangan ragu untuk bergabung dalam percakapan resmi kami. Kami ingin sekali
+mendengar fitur, saran, atau masalah apa pun yang mungkin Anda miliki!
-- [Matrix](https://matrix.to/#/#triliumnext:matrix.org) (For synchronous
- discussions.)
- - The `General` Matrix room is also bridged to
+- [Matriks](https://matrix.to/#/#triliumnext:matrix.org) (Untuk diskusi
+ sinkron.)
+ - Ruang Matriks `Umum` juga terhubung ke
[XMPP](xmpp:discuss@trilium.thisgreat.party?join)
- [Diskusi Github](https://github.com/TriliumNext/Trilium/discussions) (Untuk
diskusi asinkron.)
-- [Github Issues](https://github.com/TriliumNext/Trilium/issues) (For bug
- reports and feature requests.)
+- [Masalah GitHub](https://github.com/TriliumNext/Trilium/issues) (Untuk laporan
+ bug dan permintaan fitur.)
## 🏗 Instalasi
@@ -206,8 +206,9 @@ Saat ini hanya Chrome & Firefox versi terbaru yang didukung (dan telah diuji).
### Ponsel
-To use TriliumNext on a mobile device, you can use a mobile web browser to
-access the mobile interface of a server installation (see below).
+Untuk menggunakan TriliumNext pada perangkat seluler, Anda dapat menggunakan
+peramban web seluler untuk mengakses antarmuka seluler dari instalasi server
+(lihat di bawah).
See issue https://github.com/TriliumNext/Trilium/issues/4962 for more
information on mobile app support.
From f653c86965c02c968cc12c3dbccbb1055db660b6 Mon Sep 17 00:00:00 2001
From: green
Date: Mon, 16 Feb 2026 03:13:06 +0100
Subject: [PATCH 135/247] Translated using Weblate (Japanese)
Currently translated at 100.0% (1807 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
---
apps/client/src/translations/ja/translation.json | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json
index 3bd99204e8..9d71f3b29e 100644
--- a/apps/client/src/translations/ja/translation.json
+++ b/apps/client/src/translations/ja/translation.json
@@ -2292,6 +2292,20 @@
"type_app_theme": "カスタムテーマ",
"toggle_tooltip_enable_tooltip": "この {{type}} を有効にするにはクリックしてください。",
"toggle_tooltip_disable_tooltip": "この {{type}} を無効にするにはクリックしてください。",
- "menu_docs": "ドキュメントを開く"
+ "menu_docs": "ドキュメントを開く",
+ "menu_execute_now": "今すぐスクリプトを実行",
+ "menu_run": "自動で実行",
+ "menu_run_disabled": "手動で実行",
+ "menu_run_backend_startup": "バックエンドの起動時",
+ "menu_run_hourly": "毎時",
+ "menu_run_daily": "毎日",
+ "menu_run_frontend_startup": "デスクトップ フロントエンドの起動時",
+ "menu_run_mobile_startup": "モバイル フロントエンドの起動時",
+ "menu_change_to_widget": "ウィジェットの変更",
+ "menu_change_to_frontend_script": "フロントエンドスクリプトの変更",
+ "menu_theme_base": "テーマベース"
+ },
+ "setup_form": {
+ "more_info": "さらに詳しく"
}
}
From 748e7cc1df09ce5ce71aff066482c651014f7ba6 Mon Sep 17 00:00:00 2001
From: Stas Kies
Date: Mon, 16 Feb 2026 17:03:04 +0100
Subject: [PATCH 136/247] Translated using Weblate (German)
Currently translated at 99.1% (1792 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/de/
---
.../src/translations/de/translation.json | 26 ++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/de/translation.json b/apps/client/src/translations/de/translation.json
index 3ae48886c2..0675a238a7 100644
--- a/apps/client/src/translations/de/translation.json
+++ b/apps/client/src/translations/de/translation.json
@@ -1007,7 +1007,7 @@
"no_attachments": "Diese Notiz enthält keine Anhänge."
},
"book": {
- "no_children_help": "Diese Notiz mit dem Notiztyp Buch besitzt keine Unternotizen, deshalb ist nichts zum Anzeigen vorhanden. Siehe Wiki für mehr Details.",
+ "no_children_help": "Diese Sammlung enthält keineUnternotizen, daher gibt es nichts anzuzeigen.",
"drag_locked_title": "Für Bearbeitung gesperrt",
"drag_locked_message": "Das Ziehen ist nicht möglich, da die Sammlung für die Bearbeitung gesperrt ist."
},
@@ -2086,7 +2086,8 @@
"raster": "Raster",
"vector_light": "Vektor (Hell)",
"vector_dark": "Vektor (Dunkel)",
- "show-scale": "Zeige Skalierung"
+ "show-scale": "Zeige Skalierung",
+ "show-labels": "Zeige Markierungsnamen"
},
"table_context_menu": {
"delete_row": "Zeile entfernen"
@@ -2283,6 +2284,25 @@
"url_placeholder": "Gib oder füge die Adresse der Webseite ein, zum Beispiel https://triliumnotes.org",
"create_button": "Erstelle Web Ansicht",
"invalid_url_title": "Ungültige Adresse",
- "invalid_url_message": "Füge eine valide Webadresse ein, zum Beispiel https://triliumnotes.org."
+ "invalid_url_message": "Füge eine valide Webadresse ein, zum Beispiel https://triliumnotes.org.",
+ "disabled_description": "Diese Webansicht wurde von einer externen Quelle importiert. Um Sie vor Phishing oder schädlichen Inhalten zu schützen, wird sie nicht automatisch geladen. Sie können sie aktivieren, wenn Sie der Quelle vertrauen.",
+ "disabled_button_enable": "Webansicht aktivieren"
+ },
+ "render": {
+ "setup_create_sample_html": "Eine Beispielnotiz mit HTML erstellen",
+ "setup_create_sample_preact": "Eine Beispielnotiz mit Preact erstellen",
+ "setup_title": "Benutzerdefiniertes HTML oder Preact JSX in dieser Notiz anzeigen",
+ "setup_sample_created": "Eine Beispielnotiz wurde als untergeordnete Notiz erstellt.",
+ "disabled_description": "Diese Rendering-Notizen stammen aus einer externen Quelle. Um Sie vor schädlichen Inhalten zu schützen, ist diese Funktion standardmäßig deaktiviert. Stellen Sie sicher, dass Sie der Quelle vertrauen, bevor Sie sie aktivieren.",
+ "disabled_button_enable": "Rendering-Notiz aktivieren"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "Icon-Paket",
+ "type_backend_script": "Backend-Skript",
+ "type_frontend_script": "Frontend-Skript",
+ "type_widget": "Widget",
+ "type_app_css": "Benutzerdefiniertes CSS",
+ "type_render_note": "Rendering-Notiz",
+ "type_web_view": "Webansicht"
}
}
From a26bec047f39187eb584e349f1d2a78520f78de7 Mon Sep 17 00:00:00 2001
From: "Francis C."
Date: Mon, 16 Feb 2026 04:56:44 +0100
Subject: [PATCH 137/247] Translated using Weblate (Chinese (Traditional Han
script))
Currently translated at 100.0% (1807 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hant/
---
.../src/translations/tw/translation.json | 56 +++++++++++++++++--
1 file changed, 52 insertions(+), 4 deletions(-)
diff --git a/apps/client/src/translations/tw/translation.json b/apps/client/src/translations/tw/translation.json
index 9575cbf0f6..41ccbf4965 100644
--- a/apps/client/src/translations/tw/translation.json
+++ b/apps/client/src/translations/tw/translation.json
@@ -1007,7 +1007,7 @@
"no_attachments": "此筆記沒有附件。"
},
"book": {
- "no_children_help": "此類型為書籍的筆記沒有任何子筆記,因此沒有內容可顯示。請參閱 wiki 以了解詳情。",
+ "no_children_help": "此集合沒有任何子筆記,因此沒有內容可顯示。",
"drag_locked_title": "鎖定編輯",
"drag_locked_message": "無法拖曳,因為此集合已被鎖定編輯。"
},
@@ -1373,7 +1373,8 @@
"description": "描述",
"reload_app": "重新載入應用以套用更改",
"set_all_to_default": "將所有快捷鍵重設為預設值",
- "confirm_reset": "您確定要將所有鍵盤快捷鍵重設為預設值嗎?"
+ "confirm_reset": "您確定要將所有鍵盤快捷鍵重設為預設值嗎?",
+ "no_results": "未找到符合 '{{filter}}' 的捷徑"
},
"spellcheck": {
"title": "拼寫檢查",
@@ -1577,7 +1578,9 @@
"print_report_collection_content_one": "集合中的 {{count}} 篇筆記無法列印,因為它們不被支援或受到保護。",
"print_report_collection_content_other": "",
"print_report_collection_details_button": "查看詳情",
- "print_report_collection_details_ignored_notes": "忽略的筆記"
+ "print_report_collection_details_ignored_notes": "忽略的筆記",
+ "print_report_error_title": "列印失敗",
+ "print_report_stack_trace": "堆棧追蹤"
},
"note_title": {
"placeholder": "請輸入筆記標題...",
@@ -2071,7 +2074,8 @@
"raster": "柵格",
"vector_light": "向量(淺色)",
"vector_dark": "向量(深色)",
- "show-scale": "顯示比例尺"
+ "show-scale": "顯示比例尺",
+ "show-labels": "顯示標記名稱"
},
"table_context_menu": {
"delete_row": "刪除列"
@@ -2274,5 +2278,49 @@
},
"bookmark_buttons": {
"bookmarks": "書籤"
+ },
+ "render": {
+ "setup_title": "在此筆記中顯示自訂 HTML 或 Preact JSX",
+ "setup_create_sample_preact": "使用 Preact 建立範例筆記",
+ "setup_create_sample_html": "使用 HTML 建立範例筆記",
+ "setup_sample_created": "已建立一個範例筆記作為子筆記。",
+ "disabled_description": "此渲染筆記來自外部來源。為保護您免受惡意內容侵害,此功能預設為停用狀態。啟用前請務必確認來源可信。",
+ "disabled_button_enable": "啟用渲染筆記"
+ },
+ "web_view_setup": {
+ "title": "將網頁直接匯入 Trilium 建立即時預覽",
+ "url_placeholder": "輸入或貼上網站網址,例如 https://triliumnotes.org",
+ "create_button": "建立網頁檢視",
+ "invalid_url_title": "無效地址",
+ "invalid_url_message": "請輸入有效的網址,例如 https://triliumnotes.org。",
+ "disabled_description": "此網頁檢視來自外部來源。為協助保護您免受網路釣魚或惡意內容侵害,內容不會自動載入。若您信任來源,可手動啟用此功能。",
+ "disabled_button_enable": "啟用網頁檢視"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "圖示包",
+ "type_backend_script": "後端腳本",
+ "type_frontend_script": "前端腳本",
+ "type_widget": "元件",
+ "type_app_css": "自訂 CSS",
+ "type_render_note": "渲染筆記",
+ "type_web_view": "網頁顯示",
+ "type_app_theme": "自訂主題",
+ "toggle_tooltip_enable_tooltip": "點擊以啟用此 {{type}}。",
+ "toggle_tooltip_disable_tooltip": "點擊以停用此 {{type}}。",
+ "menu_docs": "打開文件",
+ "menu_execute_now": "立即執行腳本",
+ "menu_run": "自動執行",
+ "menu_run_disabled": "手動",
+ "menu_run_backend_startup": "當後端啟動時",
+ "menu_run_hourly": "每小時",
+ "menu_run_daily": "每日",
+ "menu_run_frontend_startup": "當桌面前端啟動時",
+ "menu_run_mobile_startup": "當移動前端啟動時",
+ "menu_change_to_widget": "更改為元件",
+ "menu_change_to_frontend_script": "更改為前端腳本",
+ "menu_theme_base": "主題基底"
+ },
+ "setup_form": {
+ "more_info": "了解更多"
}
}
From 17c0071dd1886055eb0a20a9e91dde6142b94dd3 Mon Sep 17 00:00:00 2001
From: Toto Yullian
Date: Mon, 16 Feb 2026 10:26:45 +0100
Subject: [PATCH 138/247] Translated using Weblate (Indonesian)
Currently translated at 44.9% (71 of 158 strings)
Translation: Trilium Notes/Website
Translate-URL: https://hosted.weblate.org/projects/trilium/website/id/
---
apps/website/src/translations/id/translation.json | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/apps/website/src/translations/id/translation.json b/apps/website/src/translations/id/translation.json
index bc37c2534b..8f550f062e 100644
--- a/apps/website/src/translations/id/translation.json
+++ b/apps/website/src/translations/id/translation.json
@@ -62,7 +62,10 @@
"geomap_description": "Rencanakan liburan Anda atau tandai titik minat langsung pada peta geografis menggunakan penanda titik yang dapat disesuaikan. Tampilkan rekaman jalur GPX untuk melacak rencana perjalanan.",
"presentation_title": "Presentasi",
"presentation_description": "Atur informasi ke dalam slide dan presentasikan dalam layar penuh dengan transisi yang mulus. Slide juga dapat diekspor ke PDF agar mudah dibagikan.",
- "calendar_description": "Kelola acara pribadi atau profesional Anda menggunakan kalender, dengan dukungan untuk acara sepanjang hari dan beberapa hari. Lihat acara Anda sekilas dengan tampilan mingguan, bulanan, dan tahunan. Interaksi mudah untuk menambahkan atau menyeret acara."
+ "calendar_description": "Kelola acara pribadi atau profesional Anda menggunakan kalender, dengan dukungan untuk acara sepanjang hari dan beberapa hari. Lihat acara Anda sekilas dengan tampilan mingguan, bulanan, dan tahunan. Interaksi mudah untuk menambahkan atau menyeret acara.",
+ "title": "Koleksi",
+ "calendar_title": "Penanggalan",
+ "table_title": "Tabel"
},
"faq": {
"title": "Tanya Jawab",
@@ -78,6 +81,9 @@
"import_export_title": "Impor/ekspor",
"import_export_description": "Berinteraksi dengan mudah dengan aplikasi lain menggunakan format Markdown, ENEX, dan OML.",
"share_description": "Jika Anda memiliki server, server tersebut dapat digunakan untuk berbagi sebagian catatan Anda dengan orang lain.",
- "scripting_title": "Pembuatan skrip tingkat lanjut"
+ "scripting_title": "Pembuatan skrip tingkat lanjut",
+ "scripting_description": "Bangun integrasi Anda sendiri di dalam Trilium dengan widget khusus, atau logika sisi server.",
+ "api_title": "REST API",
+ "api_description": "Berinteraksi dengan Trilium secara terprogram menggunakan API REST bawaannya."
}
}
From 99efc73e93971a5a453456ca2db08578ba8688c0 Mon Sep 17 00:00:00 2001
From: Toto Yullian
Date: Mon, 16 Feb 2026 10:28:39 +0100
Subject: [PATCH 139/247] Translated using Weblate (Indonesian)
Currently translated at 3.9% (72 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/id/
---
apps/client/src/translations/id/translation.json | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/id/translation.json b/apps/client/src/translations/id/translation.json
index 8b9e90cabd..157f406053 100644
--- a/apps/client/src/translations/id/translation.json
+++ b/apps/client/src/translations/id/translation.json
@@ -72,7 +72,8 @@
"ok": "Oke",
"are_you_sure_remove_note": "Apakah anda yakin mau membuang catatan \"{{title}}\" dari peta relasi? ",
"if_you_dont_check": "Jika Anda tidak mencentang ini, catatan hanya akan dihapus dari peta relasi.",
- "also_delete_note": "Hapus juga catatannya"
+ "also_delete_note": "Hapus juga catatannya",
+ "confirmation": "Konfirmasi"
},
"delete_notes": {
"delete_notes_preview": "Hapus pratinjau catatan",
@@ -81,11 +82,19 @@
"erase_notes_description": "Penghapusan normal hanya menandai catatan sebagai dihapus dan dapat dipulihkan (melalui dialog versi revisi) dalam jangka waktu tertentu. Mencentang opsi ini akan menghapus catatan secara permanen seketika dan catatan tidak akan bisa dipulihkan kembali.",
"erase_notes_warning": "Hapus catatan secara permanen (tidak bisa dikembalikan), termasuk semua duplikat. Aksi akan memaksa aplikasi untuk mengulang kembali.",
"notes_to_be_deleted": "Catatan-catatan berikut akan dihapuskan ({{notesCount}})",
- "no_note_to_delete": "Tidak ada Catatan yang akan dihapus (hanya duplikat)."
+ "no_note_to_delete": "Tidak ada Catatan yang akan dihapus (hanya duplikat).",
+ "broken_relations_to_be_deleted": "Hubungan berikut akan diputus dan dihapus ({{ relationCount}})"
},
"clone_to": {
"clone_notes_to": "Duplikat catatan ke…",
"help_on_links": "Bantuan pada tautan",
- "notes_to_clone": "Catatan untuk kloning"
+ "notes_to_clone": "Catatan untuk kloning",
+ "target_parent_note": "Sasaran catatan utama",
+ "search_for_note_by_its_name": "cari catatan berdasarkan namanya",
+ "cloned_note_prefix_title": "Catatan yang dikloning akan ditampilkan diruntutan catatan dengan awalan yang diberikan",
+ "prefix_optional": "Awalan (opsional)",
+ "clone_to_selected_note": "Salin ke catatan yang dipilih",
+ "no_path_to_clone_to": "Tidak ada jalur untuk digandakan.",
+ "note_cloned": "Catatan \"{{clonedTitle}}\" telah digandakan ke dalam \"{{targetTitle}}\""
}
}
From 30ad5d531c771fe109a78ecb1f386feb8d89092a Mon Sep 17 00:00:00 2001
From: "Francis C."
Date: Mon, 16 Feb 2026 05:04:37 +0100
Subject: [PATCH 140/247] Translated using Weblate (Chinese (Simplified Han
script))
Currently translated at 100.0% (1807 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/zh_Hans/
---
.../src/translations/cn/translation.json | 44 +++++++++++++++++--
1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/cn/translation.json b/apps/client/src/translations/cn/translation.json
index 628a9bb4af..3be86878d3 100644
--- a/apps/client/src/translations/cn/translation.json
+++ b/apps/client/src/translations/cn/translation.json
@@ -1008,7 +1008,7 @@
"no_attachments": "此笔记没有附件。"
},
"book": {
- "no_children_help": "此类型为书籍的笔记没有任何子笔记,因此没有内容显示。请参阅 wiki 了解详情。",
+ "no_children_help": "此集合没有任何子笔记,因此没有内容显示。",
"drag_locked_title": "锁定编辑",
"drag_locked_message": "无法拖拽,因为集合已被锁定编辑。"
},
@@ -2069,7 +2069,8 @@
"raster": "栅格",
"vector_light": "矢量(浅色)",
"vector_dark": "矢量(深色)",
- "show-scale": "显示比例尺"
+ "show-scale": "显示比例尺",
+ "show-labels": "显示标记名称"
},
"table_context_menu": {
"delete_row": "删除行"
@@ -2268,6 +2269,43 @@
"url_placeholder": "输入或粘贴网站地址,例如 https://triliumnotes.org",
"create_button": "创建网页视图",
"invalid_url_title": "无效的地址",
- "invalid_url_message": "请输入有效的网址,例如 https://triliumnotes.org。"
+ "invalid_url_message": "请输入有效的网址,例如 https://triliumnotes.org。",
+ "disabled_description": "此网页视图来自外部来源。为保护您免受网络钓鱼或恶意内容侵害,该视图不会自动加载。若您信任该来源,可手动启用加载功能。",
+ "disabled_button_enable": "启用网页视图"
+ },
+ "render": {
+ "setup_title": "在此笔记中显示自定义 HTML 或 Preact JSX",
+ "setup_create_sample_preact": "使用 Preact 建立范例笔记",
+ "setup_create_sample_html": "使用 HTML 建立范例笔记",
+ "setup_sample_created": "已建立一个范例笔记作为子笔记。",
+ "disabled_description": "此渲染笔记来自外部来源。为保护您免受恶意内容侵害,该功能默认处于禁用状态。启用前请确保您信任该来源。",
+ "disabled_button_enable": "启用渲染笔记"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "图标包",
+ "type_backend_script": "后端脚本",
+ "type_frontend_script": "前端脚本",
+ "type_widget": "小部件",
+ "type_app_css": "自定义 CSS",
+ "type_render_note": "渲染笔记",
+ "type_web_view": "网页视图",
+ "type_app_theme": "自定义主题",
+ "toggle_tooltip_enable_tooltip": "点击以启用此 {{type}}。",
+ "toggle_tooltip_disable_tooltip": "点击以禁用此 {{type}}。",
+ "menu_docs": "打开文档",
+ "menu_execute_now": "立即执行脚本",
+ "menu_run": "自动执行",
+ "menu_run_disabled": "手动",
+ "menu_run_backend_startup": "当后端启动时",
+ "menu_run_hourly": "每小时",
+ "menu_run_daily": "每日",
+ "menu_run_frontend_startup": "当桌面前端启动时",
+ "menu_run_mobile_startup": "当移动前端启动时",
+ "menu_change_to_widget": "更改为小部件",
+ "menu_change_to_frontend_script": "更改为前端脚本",
+ "menu_theme_base": "主题基底"
+ },
+ "setup_form": {
+ "more_info": "了解更多"
}
}
From 1b1162e26e62cbc4ebb0a69bc3f55f575340b4f2 Mon Sep 17 00:00:00 2001
From: Giovi
Date: Mon, 16 Feb 2026 15:03:45 +0100
Subject: [PATCH 141/247] Translated using Weblate (Italian)
Currently translated at 100.0% (1807 of 1807 strings)
Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/it/
---
.../src/translations/it/translation.json | 59 ++++++++++++++++---
1 file changed, 50 insertions(+), 9 deletions(-)
diff --git a/apps/client/src/translations/it/translation.json b/apps/client/src/translations/it/translation.json
index 088361d4ab..835737d8db 100644
--- a/apps/client/src/translations/it/translation.json
+++ b/apps/client/src/translations/it/translation.json
@@ -167,8 +167,8 @@
"desktop-application": "Applicazione Desktop",
"native-title-bar": "Barra del titolo nativa",
"native-title-bar-description": "Su Windows e macOS, disattivare la barra del titolo nativa rende l'applicazione più compatta. Su Linux, attivarla si integra meglio con il resto del sistema.",
- "background-effects": "Abilita effetti di sfondo (solo Windows 11)",
- "background-effects-description": "L'effetto Mica aggiunge uno sfondo sfocato ed elegante alle finestre delle app, creando profondità e un aspetto moderno. La \"Barra del titolo nativa\" deve essere disattivata.",
+ "background-effects": "Abilita effetti di sfondo",
+ "background-effects-description": "Aggiunge uno sfondo sfocato ed elegante alle finestre dell'app, creando profondità e un look moderno. La \"barra del titolo nativa\" deve essere disabilitata.",
"restart-app-button": "Riavviare l'applicazione per visualizzare le modifiche"
},
"note_autocomplete": {
@@ -369,7 +369,8 @@
"description": "Descrizione",
"reload_app": "Ricarica l'app per applicare le modifiche",
"set_all_to_default": "Imposta tutte le scorciatoie sui valori predefiniti",
- "confirm_reset": "Vuoi davvero ripristinare tutte le scorciatoie da tastiera ai valori predefiniti?"
+ "confirm_reset": "Vuoi davvero ripristinare tutte le scorciatoie da tastiera ai valori predefiniti?",
+ "no_results": "Nessuna scorciatoia trovata corrispondente '{{filter}}'"
},
"shared_switch": {
"toggle-on-title": "Condividi la nota",
@@ -1327,7 +1328,7 @@
"button_title": "Esporta diagramma come SVG"
},
"relation_map_buttons": {
- "create_child_note_title": "Crea una nuova nota secondaria e aggiungila a questa mappa delle relazioni",
+ "create_child_note_title": "Crea una nota secondaria e aggiungila alla mappa",
"reset_pan_zoom_title": "Ripristina panoramica e zoom alle coordinate e all'ingrandimento iniziali",
"zoom_in_title": "Ingrandisci",
"zoom_out_title": "Rimpicciolisci"
@@ -1526,7 +1527,7 @@
"no_attachments": "Questa nota non ha allegati."
},
"book": {
- "no_children_help": "Questa raccolta non ha note secondarie, quindi non c'è nulla da visualizzare. Consulta la wiki per i dettagli.",
+ "no_children_help": "Questa raccolta non ha note secondarie, quindi non c'è nulla da visualizzare.",
"drag_locked_title": "Bloccato per la modifica",
"drag_locked_message": "Trascinamento non consentito poiché la raccolta è bloccata per la modifica."
},
@@ -1919,7 +1920,9 @@
"print_report_collection_content_many": "{{count}} le note nella raccolta non possono essere stampate perché non sono supportate o sono protette.",
"print_report_collection_content_other": "{{count}} le note nella raccolta non possono essere stampate perché non sono supportate o sono protette.",
"print_report_collection_details_button": "Vedi dettagli",
- "print_report_collection_details_ignored_notes": "Note ignorate"
+ "print_report_collection_details_ignored_notes": "Note ignorate",
+ "print_report_error_title": "Impossibile stampare",
+ "print_report_stack_trace": "Traccia dello stack"
},
"note_title": {
"placeholder": "scrivi qui il titolo della nota...",
@@ -2106,7 +2109,8 @@
"raster": "Trama",
"vector_light": "Vettore (Luce)",
"vector_dark": "Vettore (scuro)",
- "show-scale": "Mostra scala"
+ "show-scale": "Mostra scala",
+ "show-labels": "Mostra nomi dei marcatori"
},
"table_context_menu": {
"delete_row": "Elimina riga"
@@ -2139,7 +2143,7 @@
"next_theme_message": "Al momento stai utilizzando il tema legacy. Vuoi provare il nuovo tema?",
"next_theme_button": "Prova il nuovo tema",
"background_effects_title": "Gli effetti di sfondo sono ora stabili",
- "background_effects_message": "Sui dispositivi Windows, gli effetti di sfondo sono ora completamente stabili. Gli effetti di sfondo aggiungono un tocco di colore all'interfaccia utente sfocando lo sfondo retrostante. Questa tecnica è utilizzata anche in altre applicazioni come Esplora risorse di Windows.",
+ "background_effects_message": "Su dispositivi Windows e macOS, gli effetti di sfondo sono ora stabili. Gli effetti di sfondo aggiungono un tocco di colore all'interfaccia utente sfocando lo sfondo dietro di essa.",
"background_effects_button": "Abilita gli effetti di sfondo",
"dismiss": "Chiudi",
"new_layout_title": "Nuovo layout",
@@ -2283,7 +2287,9 @@
"url_placeholder": "Inserisci o incolla l'indirizzo del sito web, ad esempio https://triliumnotes.org",
"create_button": "Crea vista Web",
"invalid_url_title": "Indirizzo non valido",
- "invalid_url_message": "Inserisci un indirizzo web valido, ad esempio https://triliumnotes.org."
+ "invalid_url_message": "Inserisci un indirizzo web valido, ad esempio https://triliumnotes.org.",
+ "disabled_description": "Questa visualizzazione web è stata importata da una fonte esterna. Per proteggerti dal phishing o da contenuti dannosi, non viene caricata automaticamente. Puoi abilitarla se ritieni che la fonte sia affidabile.",
+ "disabled_button_enable": "Abilita visualizzazione web"
},
"platform_indicator": {
"available_on": "Disponibile su {{platform}}"
@@ -2296,5 +2302,40 @@
},
"bookmark_buttons": {
"bookmarks": "Segnalibri"
+ },
+ "render": {
+ "setup_title": "Visualizza HTML personalizzato o Preact JSX all'interno di questa nota",
+ "setup_create_sample_preact": "Crea una nota di esempio con Preact",
+ "setup_create_sample_html": "Crea una nota di esempio con HTML",
+ "setup_sample_created": "È stata creata una nota di esempio come nota secondaria.",
+ "disabled_description": "Queste note di rendering provengono da una fonte esterna. Per proteggerti da contenuti dannosi, non sono abilitate per impostazione predefinita. Assicurati di fidarti della fonte prima di abilitarle.",
+ "disabled_button_enable": "Abilita nota di rendering"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "Pacchetto icone",
+ "type_backend_script": "Script di backend",
+ "type_frontend_script": "Script frontend",
+ "type_widget": "Widget",
+ "type_app_css": "CSS personalizzato",
+ "type_render_note": "Nota di rendering",
+ "type_web_view": "Visualizzazione web",
+ "type_app_theme": "Tema personalizzato",
+ "toggle_tooltip_enable_tooltip": "Clicca per abilitare questa funzione {{type}}.",
+ "toggle_tooltip_disable_tooltip": "Clicca per disattivare questa funzione {{type}}.",
+ "menu_docs": "Documentazione aperta",
+ "menu_execute_now": "Esegui lo script ora",
+ "menu_run": "Esegui automaticamente",
+ "menu_run_disabled": "Manualmente",
+ "menu_run_backend_startup": "Quando il backend si avvia",
+ "menu_run_hourly": "Ogni ora",
+ "menu_run_daily": "Giornaliero",
+ "menu_run_frontend_startup": "Quando si avvia il frontend desktop",
+ "menu_run_mobile_startup": "Quando si avvia il frontend mobile",
+ "menu_change_to_widget": "Passa al widget",
+ "menu_change_to_frontend_script": "Modifica allo script frontend",
+ "menu_theme_base": "Tema base"
+ },
+ "setup_form": {
+ "more_info": "Per saperne di più"
}
}
From d64f2c72b70882f43ca45c761e40bbf3949979e5 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Mon, 16 Feb 2026 18:15:29 +0200
Subject: [PATCH 142/247] ui/buttons: add support for low profile style
---
apps/client/src/widgets/react/Button.tsx | 22 +++++++++++++++-------
1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/widgets/react/Button.tsx b/apps/client/src/widgets/react/Button.tsx
index ffdc6ddd2c..1aab2153e6 100644
--- a/apps/client/src/widgets/react/Button.tsx
+++ b/apps/client/src/widgets/react/Button.tsx
@@ -18,7 +18,7 @@ export interface ButtonProps {
keyboardShortcut?: string;
/** Called when the button is clicked. If not set, the button will submit the form (if any). */
onClick?: () => void;
- primary?: boolean;
+ kind?: "primary" | "secondary" | "lowProfile";
disabled?: boolean;
size?: "normal" | "small" | "micro";
style?: CSSProperties;
@@ -26,15 +26,23 @@ export interface ButtonProps {
title?: string;
}
-const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortcut, icon, primary, disabled, size, style, triggerCommand, ...restProps }: ButtonProps) => {
+const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortcut, icon, kind, disabled, size, style, triggerCommand, ...restProps }: ButtonProps) => {
// Memoize classes array to prevent recreation
const classes = useMemo(() => {
const classList: string[] = ["btn"];
- if (primary) {
- classList.push("btn-primary");
- } else {
- classList.push("btn-secondary");
+
+ switch(kind) {
+ case "primary":
+ classList.push("btn-primary");
+ break;
+ case "lowProfile":
+ classList.push("tn-low-profile");
+ break;
+ default:
+ classList.push("btn-secondary");
+ break;
}
+
if (className) {
classList.push(className);
}
@@ -44,7 +52,7 @@ const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortc
classList.push("btn-micro");
}
return classList.join(" ");
- }, [primary, className, size]);
+ }, [kind, className, size]);
// Memoize keyboard shortcut rendering
const shortcutElements = useMemo(() => {
From 49128345370e1ee80ed71d50d238e1add46c1f33 Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Mon, 16 Feb 2026 18:23:28 +0200
Subject: [PATCH 143/247] ui/buttons: refactor
---
apps/client/src/widgets/dialogs/bulk_actions.tsx | 2 +-
apps/client/src/widgets/dialogs/delete_notes.tsx | 2 +-
apps/client/src/widgets/dialogs/export.tsx | 2 +-
apps/client/src/widgets/dialogs/import.tsx | 2 +-
apps/client/src/widgets/dialogs/prompt.tsx | 2 +-
apps/client/src/widgets/dialogs/revisions.tsx | 2 +-
apps/client/src/widgets/dialogs/upload_attachments.tsx | 2 +-
apps/client/src/widgets/ribbon/FilePropertiesTab.tsx | 2 +-
apps/client/src/widgets/ribbon/ImagePropertiesTab.tsx | 2 +-
apps/client/src/widgets/type_widgets/ProtectedSession.tsx | 2 +-
apps/client/src/widgets/type_widgets/Render.tsx | 2 +-
apps/client/src/widgets/type_widgets/WebView.tsx | 4 ++--
apps/client/src/widgets/type_widgets/options/password.tsx | 2 +-
apps/client/src/widgets/type_widgets/options/sync.tsx | 2 +-
14 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/apps/client/src/widgets/dialogs/bulk_actions.tsx b/apps/client/src/widgets/dialogs/bulk_actions.tsx
index 05033255c4..cbb04a404a 100644
--- a/apps/client/src/widgets/dialogs/bulk_actions.tsx
+++ b/apps/client/src/widgets/dialogs/bulk_actions.tsx
@@ -57,7 +57,7 @@ export default function BulkActionsDialog() {
className="bulk-actions-dialog"
size="xl"
title={t("bulk_actions.bulk_actions")}
- footer={ }
+ footer={ }
show={shown}
onSubmit={async () => {
await server.post("bulk-action/execute", {
diff --git a/apps/client/src/widgets/dialogs/delete_notes.tsx b/apps/client/src/widgets/dialogs/delete_notes.tsx
index bb1b4e7cd0..c58d440b72 100644
--- a/apps/client/src/widgets/dialogs/delete_notes.tsx
+++ b/apps/client/src/widgets/dialogs/delete_notes.tsx
@@ -72,7 +72,7 @@ export default function DeleteNotesDialog() {
footer={<>
setShown(false)} />
- {
opts.callback?.({ proceed: true, deleteAllClones, eraseNotes });
diff --git a/apps/client/src/widgets/dialogs/export.tsx b/apps/client/src/widgets/dialogs/export.tsx
index 2f10f8063b..01caa7659f 100644
--- a/apps/client/src/widgets/dialogs/export.tsx
+++ b/apps/client/src/widgets/dialogs/export.tsx
@@ -58,7 +58,7 @@ export default function ExportDialog() {
setShown(false);
}}
onHidden={() => setShown(false)}
- footer={ }
+ footer={ }
show={shown}
>
diff --git a/apps/client/src/widgets/dialogs/import.tsx b/apps/client/src/widgets/dialogs/import.tsx
index 46db5b8cd7..0c37466d59 100644
--- a/apps/client/src/widgets/dialogs/import.tsx
+++ b/apps/client/src/widgets/dialogs/import.tsx
@@ -55,7 +55,7 @@ export default function ImportDialog() {
setShown(false);
setFiles(null);
}}
- footer={ }
+ footer={ }
show={shown}
>
}
+ footer={ }
show={shown}
stackable
>
diff --git a/apps/client/src/widgets/dialogs/revisions.tsx b/apps/client/src/widgets/dialogs/revisions.tsx
index 1d11145f3e..41d22864f3 100644
--- a/apps/client/src/widgets/dialogs/revisions.tsx
+++ b/apps/client/src/widgets/dialogs/revisions.tsx
@@ -203,7 +203,7 @@ function RevisionPreview({noteContent, revisionItem, showDiff, setShown, onRevis
}} />
{
diff --git a/apps/client/src/widgets/dialogs/upload_attachments.tsx b/apps/client/src/widgets/dialogs/upload_attachments.tsx
index 7d3d54bda0..841dd69541 100644
--- a/apps/client/src/widgets/dialogs/upload_attachments.tsx
+++ b/apps/client/src/widgets/dialogs/upload_attachments.tsx
@@ -35,7 +35,7 @@ export default function UploadAttachmentsDialog() {
className="upload-attachments-dialog"
size="lg"
title={t("upload_attachments.upload_attachments_to_note")}
- footer={ }
+ footer={ }
onSubmit={async () => {
if (!files || !parentNoteId) {
return;
diff --git a/apps/client/src/widgets/ribbon/FilePropertiesTab.tsx b/apps/client/src/widgets/ribbon/FilePropertiesTab.tsx
index bf843c41e8..2d93b29f54 100644
--- a/apps/client/src/widgets/ribbon/FilePropertiesTab.tsx
+++ b/apps/client/src/widgets/ribbon/FilePropertiesTab.tsx
@@ -43,7 +43,7 @@ export default function FilePropertiesTab({ note, ntxId }: Pick downloadFileNote(note, parentComponent, ntxId)}
/>
diff --git a/apps/client/src/widgets/ribbon/ImagePropertiesTab.tsx b/apps/client/src/widgets/ribbon/ImagePropertiesTab.tsx
index 0e229dc994..c9e031863f 100644
--- a/apps/client/src/widgets/ribbon/ImagePropertiesTab.tsx
+++ b/apps/client/src/widgets/ribbon/ImagePropertiesTab.tsx
@@ -43,7 +43,7 @@ export default function ImagePropertiesTab({ note, ntxId }: TabContext) {
downloadFileNote(note, parentComponent, ntxId)}
/>
diff --git a/apps/client/src/widgets/type_widgets/ProtectedSession.tsx b/apps/client/src/widgets/type_widgets/ProtectedSession.tsx
index 5f7b763501..a735f271e4 100644
--- a/apps/client/src/widgets/type_widgets/ProtectedSession.tsx
+++ b/apps/client/src/widgets/type_widgets/ProtectedSession.tsx
@@ -34,7 +34,7 @@ export default function ProtectedSession() {
diff --git a/apps/client/src/widgets/type_widgets/Render.tsx b/apps/client/src/widgets/type_widgets/Render.tsx
index 4d4fd4cd8e..46c2e3a7a8 100644
--- a/apps/client/src/widgets/type_widgets/Render.tsx
+++ b/apps/client/src/widgets/type_widgets/Render.tsx
@@ -107,7 +107,7 @@ function DisabledRender({ note }: TypeWidgetProps) {
text={t("render.disabled_button_enable")}
icon="bx bx-check-shield"
onClick={() => attributes.toggleDangerousAttribute(note, "relation", "renderNote", true)}
- primary
+ kind="primary"
/>
);
diff --git a/apps/client/src/widgets/type_widgets/WebView.tsx b/apps/client/src/widgets/type_widgets/WebView.tsx
index d61725d064..dd3ab43c9e 100644
--- a/apps/client/src/widgets/type_widgets/WebView.tsx
+++ b/apps/client/src/widgets/type_widgets/WebView.tsx
@@ -74,7 +74,7 @@ function SetupWebView({note}: {note: FNote}) {
@@ -96,7 +96,7 @@ function DisabledWebView({ note, url }: { note: FNote, url: string }) {
text={t("web_view_setup.disabled_button_enable")}
icon="bx bx-check-shield"
onClick={() => attributes.toggleDangerousAttribute(note, "label", "webViewSrc", true)}
- primary
+ kind="primary"
/>
);
diff --git a/apps/client/src/widgets/type_widgets/options/password.tsx b/apps/client/src/widgets/type_widgets/options/password.tsx
index 019d3bcbe7..6e2eec9581 100644
--- a/apps/client/src/widgets/type_widgets/options/password.tsx
+++ b/apps/client/src/widgets/type_widgets/options/password.tsx
@@ -95,7 +95,7 @@ function ChangePassword() {
diff --git a/apps/client/src/widgets/type_widgets/options/sync.tsx b/apps/client/src/widgets/type_widgets/options/sync.tsx
index fbd2f6a60f..1ffb40f373 100644
--- a/apps/client/src/widgets/type_widgets/options/sync.tsx
+++ b/apps/client/src/widgets/type_widgets/options/sync.tsx
@@ -65,7 +65,7 @@ export function SyncConfiguration() {
-
+
openInAppHelpFromUrl("cbkrhQjrkKrh")} />
From 5a06193e6527f91efc4ec862c36870f2b6ad421a Mon Sep 17 00:00:00 2001
From: Adorian Doran
Date: Mon, 16 Feb 2026 20:11:11 +0200
Subject: [PATCH 144/247] ui/pager: restyle
---
.../src/widgets/collections/Pagination.css | 25 +++++++++++++++++++
.../src/widgets/collections/Pagination.tsx | 21 +++++++++++-----
2 files changed, 40 insertions(+), 6 deletions(-)
create mode 100644 apps/client/src/widgets/collections/Pagination.css
diff --git a/apps/client/src/widgets/collections/Pagination.css b/apps/client/src/widgets/collections/Pagination.css
new file mode 100644
index 0000000000..e51faca6e4
--- /dev/null
+++ b/apps/client/src/widgets/collections/Pagination.css
@@ -0,0 +1,25 @@
+.note-list-pager {
+ display: flex;
+ align-items: center;
+ font-size: .8rem;
+
+ .note-list-pager-page-button-container {
+ .note-list-pager-page-button {
+ min-width: 40px;
+ padding-inline: 0;
+
+ &.note-list-pager-page-button-current {
+ color: orange;
+ font-weight: bold;
+ opacity: unset;
+ }
+ }
+
+ .note-list-pager-ellipsis {
+ display: inline-block;
+ width: 20px;
+ text-align: center;
+ opacity: .5;
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/client/src/widgets/collections/Pagination.tsx b/apps/client/src/widgets/collections/Pagination.tsx
index b152e59638..b48550e008 100644
--- a/apps/client/src/widgets/collections/Pagination.tsx
+++ b/apps/client/src/widgets/collections/Pagination.tsx
@@ -6,6 +6,8 @@ import { useNoteLabelInt } from "../react/hooks";
import { t } from "../../services/i18n";
import ActionButton from "../react/ActionButton";
import Button from "../react/Button";
+import "./Pagination.css";
+import clsx from "clsx";
interface PaginationContext {
page: number;
@@ -24,16 +26,18 @@ export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit