From f3dccc0aec8c4b50fbf696f268d5dab832609b89 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 09:19:46 +0200
Subject: [PATCH 01/37] feat(badges/content): detect icon pack
---
apps/client/src/stylesheets/theme-next-light.css | 3 ++-
apps/client/src/widgets/layout/NoteBadges.css | 1 +
apps/client/src/widgets/layout/NoteBadges.tsx | 15 +++++++++++++++
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/stylesheets/theme-next-light.css b/apps/client/src/stylesheets/theme-next-light.css
index 2d7862ae00..44f594d0ab 100644
--- a/apps/client/src/stylesheets/theme-next-light.css
+++ b/apps/client/src/stylesheets/theme-next-light.css
@@ -202,6 +202,7 @@
--badge-share-background-color: #6b6b6b;
--badge-clipped-note-background-color: #2284c0;
--badge-execute-background-color: #7b47af;
+ --badge-icon-pack-background-color: rgb(228, 163, 44);
--note-icon-background-color: #4f4f4f;
--note-icon-color: white;
@@ -322,4 +323,4 @@
.note-split.with-hue *::selection,
.quick-edit-dialog-wrapper.with-hue *::selection {
--selection-background-color: hsl(var(--custom-color-hue), 60%, 90%);
-}
\ No newline at end of file
+}
diff --git a/apps/client/src/widgets/layout/NoteBadges.css b/apps/client/src/widgets/layout/NoteBadges.css
index ec163da6f2..7c11c7fce6 100644
--- a/apps/client/src/widgets/layout/NoteBadges.css
+++ b/apps/client/src/widgets/layout/NoteBadges.css
@@ -37,6 +37,7 @@
pointer-events: none;
}
}
+ &.icon-pack-badge { --color: var(--badge-icon-pack-background-color); }
min-width: 0;
diff --git a/apps/client/src/widgets/layout/NoteBadges.tsx b/apps/client/src/widgets/layout/NoteBadges.tsx
index b4fba9e28a..669778d71b 100644
--- a/apps/client/src/widgets/layout/NoteBadges.tsx
+++ b/apps/client/src/widgets/layout/NoteBadges.tsx
@@ -19,6 +19,7 @@ export default function NoteBadges() {
+
);
}
@@ -147,3 +148,17 @@ export function SaveStatusBadge() {
/>
);
}
+
+function IconPackBadge() {
+ const { note } = useNoteContext();
+ const isEnabledIconPack = useNoteLabelBoolean(note, "iconPack");
+ const isDisabledIconPack = useNoteLabelBoolean(note, "disabled:iconPack");
+
+ return ((isEnabledIconPack || isDisabledIconPack) &&
+
+ );
+}
From 7d103f8c50fafb5af87e7af96b73e0d7dcc2dab4 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 09:37:48 +0200
Subject: [PATCH 02/37] refactor(badges/content): extract to separate file
---
.../widgets/layout/ActiveContentBadges.tsx | 34 +++++++++++++++++++
apps/client/src/widgets/layout/NoteBadges.css | 5 +++
apps/client/src/widgets/layout/NoteBadges.tsx | 17 ++--------
3 files changed, 41 insertions(+), 15 deletions(-)
create mode 100644 apps/client/src/widgets/layout/ActiveContentBadges.tsx
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
new file mode 100644
index 0000000000..1ffa2792c3
--- /dev/null
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -0,0 +1,34 @@
+import { Badge } from "../react/Badge";
+import FormToggle from "../react/FormToggle";
+import { useNoteContext, useNoteLabelBoolean } from "../react/hooks";
+
+export function ActiveContentBadges() {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+function IconPackBadge() {
+ const { note } = useNoteContext();
+ const [ isEnabledIconPack ] = useNoteLabelBoolean(note, "iconPack");
+ const [ isDisabledIconPack ] = useNoteLabelBoolean(note, "disabled:iconPack");
+
+ return ((isEnabledIconPack || isDisabledIconPack) &&
+
+ );
+}
+
+function ActiveContentToggle() {
+ return ;
+}
diff --git a/apps/client/src/widgets/layout/NoteBadges.css b/apps/client/src/widgets/layout/NoteBadges.css
index 7c11c7fce6..b2b6e18f77 100644
--- a/apps/client/src/widgets/layout/NoteBadges.css
+++ b/apps/client/src/widgets/layout/NoteBadges.css
@@ -46,6 +46,11 @@
text-overflow: ellipsis;
min-width: 0;
}
+
+ .switch-button {
+ --switch-track-height: 8px;
+ --switch-track-width: 30px;
+ }
}
.dropdown-badge {
diff --git a/apps/client/src/widgets/layout/NoteBadges.tsx b/apps/client/src/widgets/layout/NoteBadges.tsx
index 669778d71b..bf484edf73 100644
--- a/apps/client/src/widgets/layout/NoteBadges.tsx
+++ b/apps/client/src/widgets/layout/NoteBadges.tsx
@@ -10,6 +10,7 @@ import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useGetContextData, useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
import { useShareState } from "../ribbon/BasicPropertiesTab";
import { useShareInfo } from "../shared_info";
+import { ActiveContentBadges } from "./ActiveContentBadges";
export default function NoteBadges() {
return (
@@ -19,7 +20,7 @@ export default function NoteBadges() {
-
+
);
}
@@ -148,17 +149,3 @@ export function SaveStatusBadge() {
/>
);
}
-
-function IconPackBadge() {
- const { note } = useNoteContext();
- const isEnabledIconPack = useNoteLabelBoolean(note, "iconPack");
- const isDisabledIconPack = useNoteLabelBoolean(note, "disabled:iconPack");
-
- return ((isEnabledIconPack || isDisabledIconPack) &&
-
- );
-}
From a2264847b6a19f496657ab4c45f33c3b98aad349 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 09:53:37 +0200
Subject: [PATCH 03/37] refactor(badges/content): use shared mechanism for
extracting info
---
.../widgets/layout/ActiveContentBadges.tsx | 60 ++++++++++++++++---
1 file changed, 53 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 1ffa2792c3..62eb528898 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -1,22 +1,26 @@
+import { useEffect, useState } from "preact/hooks";
+
+import FNote from "../../entities/fnote";
+import attributes from "../../services/attributes";
import { Badge } from "../react/Badge";
import FormToggle from "../react/FormToggle";
-import { useNoteContext, useNoteLabelBoolean } from "../react/hooks";
+import { useNoteContext, useTriliumEvent } from "../react/hooks";
export function ActiveContentBadges() {
+ const { note } = useNoteContext();
+ const info = useActiveContentInfo(note);
+ console.log("Got inf ", info);
+
return (
<>
-
+ {info?.type === "iconPack" && }
>
);
}
function IconPackBadge() {
- const { note } = useNoteContext();
- const [ isEnabledIconPack ] = useNoteLabelBoolean(note, "iconPack");
- const [ isDisabledIconPack ] = useNoteLabelBoolean(note, "disabled:iconPack");
-
- return ((isEnabledIconPack || isDisabledIconPack) &&
+ return (
;
}
+
+const activeContentLabels = [ "iconPack" ] as const;
+
+interface ActiveContentInfo {
+ type: "iconPack";
+}
+
+function useActiveContentInfo(note: FNote | null | undefined) {
+ const [ info, setInfo ] = useState(null);
+
+ function refresh() {
+ let type: ActiveContentInfo["type"] | null = null;
+
+ if (!note) {
+ setInfo(null);
+ return;
+ }
+
+ for (const labelToCheck of activeContentLabels ) {
+ if (note.hasLabel(labelToCheck)) {
+ type = labelToCheck;
+ }
+ }
+
+ if (type) {
+ setInfo({ type });
+ } else {
+ setInfo(null);
+ }
+ }
+
+ // Refresh on note change.
+ useEffect(refresh, [ note ]);
+
+ useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
+ if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) {
+ refresh();
+ }
+ });
+
+ return info;
+}
From ccff210b4c9834cae4c410f740f804006ed2a8c2 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 09:57:40 +0200
Subject: [PATCH 04/37] feat(badges/content): indicate enabled/disabled state
---
.../widgets/layout/ActiveContentBadges.tsx | 21 ++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 62eb528898..c2eab4a68a 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -11,10 +11,10 @@ export function ActiveContentBadges() {
const info = useActiveContentInfo(note);
console.log("Got inf ", info);
- return (
+ return (info &&
<>
- {info?.type === "iconPack" && }
-
+ {info.type === "iconPack" && }
+
>
);
}
@@ -29,11 +29,11 @@ function IconPackBadge() {
);
}
-function ActiveContentToggle() {
- return ;
}
@@ -41,6 +41,7 @@ const activeContentLabels = [ "iconPack" ] as const;
interface ActiveContentInfo {
type: "iconPack";
+ isEnabled: boolean;
}
function useActiveContentInfo(note: FNote | null | undefined) {
@@ -48,6 +49,7 @@ function useActiveContentInfo(note: FNote | null | undefined) {
function refresh() {
let type: ActiveContentInfo["type"] | null = null;
+ let isEnabled = true;
if (!note) {
setInfo(null);
@@ -57,11 +59,16 @@ function useActiveContentInfo(note: FNote | null | undefined) {
for (const labelToCheck of activeContentLabels ) {
if (note.hasLabel(labelToCheck)) {
type = labelToCheck;
+ break;
+ } else if (note.hasLabel(`disabled:${labelToCheck}`)) {
+ type = labelToCheck;
+ isEnabled = false;
+ break;
}
}
if (type) {
- setInfo({ type });
+ setInfo({ type, isEnabled });
} else {
setInfo(null);
}
From 9b3396349e025ca2f412def8d5a25fd7d980d7ee Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 10:30:24 +0200
Subject: [PATCH 05/37] refactor(commons): add builtin_attributes to commons
---
apps/server/src/services/anonymization.ts | 10 +++++-----
apps/server/src/services/attributes.ts | 20 +++++++++----------
packages/commons/src/index.ts | 1 +
.../commons/src/lib}/builtin_attributes.ts | 0
4 files changed, 15 insertions(+), 16 deletions(-)
rename {apps/server/src/services => packages/commons/src/lib}/builtin_attributes.ts (100%)
diff --git a/apps/server/src/services/anonymization.ts b/apps/server/src/services/anonymization.ts
index 6950be72d8..262251ba91 100644
--- a/apps/server/src/services/anonymization.ts
+++ b/apps/server/src/services/anonymization.ts
@@ -1,11 +1,11 @@
-import BUILTIN_ATTRIBUTES from "./builtin_attributes.js";
+import { AnonymizedDbResponse, BUILTIN_ATTRIBUTES, DatabaseAnonymizeResponse } from "@triliumnext/commons";
+import Database from "better-sqlite3";
import fs from "fs";
+import path from "path";
+
import dataDir from "./data_dir.js";
import dateUtils from "./date_utils.js";
-import Database from "better-sqlite3";
import sql from "./sql.js";
-import path from "path";
-import { AnonymizedDbResponse, DatabaseAnonymizeResponse } from "@triliumnext/commons";
function getFullAnonymizationScript() {
// we want to delete all non-builtin attributes because they can contain sensitive names and values
@@ -86,7 +86,7 @@ function getExistingAnonymizedDatabases() {
.readdirSync(dataDir.ANONYMIZED_DB_DIR)
.filter((fileName) => fileName.includes("anonymized"))
.map((fileName) => ({
- fileName: fileName,
+ fileName,
filePath: path.resolve(dataDir.ANONYMIZED_DB_DIR, fileName)
})) satisfies AnonymizedDbResponse[];
}
diff --git a/apps/server/src/services/attributes.ts b/apps/server/src/services/attributes.ts
index c1fec6808f..2e1a207447 100644
--- a/apps/server/src/services/attributes.ts
+++ b/apps/server/src/services/attributes.ts
@@ -1,13 +1,11 @@
-"use strict";
+import { type AttributeRow, BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
-import searchService from "./search/services/search.js";
-import sql from "./sql.js";
import becca from "../becca/becca.js";
import BAttribute from "../becca/entities/battribute.js";
-import attributeFormatter from "./attribute_formatter.js";
-import BUILTIN_ATTRIBUTES from "./builtin_attributes.js";
import type BNote from "../becca/entities/bnote.js";
-import type { AttributeRow } from "@triliumnext/commons";
+import attributeFormatter from "./attribute_formatter.js";
+import searchService from "./search/services/search.js";
+import sql from "./sql.js";
const ATTRIBUTE_TYPES = new Set(["label", "relation"]);
@@ -41,18 +39,18 @@ function getNoteWithLabel(name: string, value?: string): BNote | null {
function createLabel(noteId: string, name: string, value: string = "") {
return createAttribute({
- noteId: noteId,
+ noteId,
type: "label",
- name: name,
- value: value
+ name,
+ value
});
}
function createRelation(noteId: string, name: string, targetNoteId: string) {
return createAttribute({
- noteId: noteId,
+ noteId,
type: "relation",
- name: name,
+ name,
value: targetNoteId
});
}
diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts
index 1ae730a563..d2e4f85d4c 100644
--- a/packages/commons/src/index.ts
+++ b/packages/commons/src/index.ts
@@ -13,3 +13,4 @@ export * from "./lib/attribute_names.js";
export * from "./lib/utils.js";
export * from "./lib/dayjs.js";
export * from "./lib/notes.js";
+export { default as BUILTIN_ATTRIBUTES } from "./lib/builtin_attributes.js";
diff --git a/apps/server/src/services/builtin_attributes.ts b/packages/commons/src/lib/builtin_attributes.ts
similarity index 100%
rename from apps/server/src/services/builtin_attributes.ts
rename to packages/commons/src/lib/builtin_attributes.ts
From 7be637798ff242b40a9c1ae2cd5da178d4dc43fb Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 10:30:34 +0200
Subject: [PATCH 06/37] feat(badges/content): functional enable/disable toggle
---
.../widgets/layout/ActiveContentBadges.tsx | 33 ++++++++++++++++---
1 file changed, 28 insertions(+), 5 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index c2eab4a68a..8b0cb3744c 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -1,3 +1,4 @@
+import { BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
@@ -6,15 +7,17 @@ import { Badge } from "../react/Badge";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useTriliumEvent } from "../react/hooks";
+const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous);
+const activeContentLabels = [ "iconPack" ] as const;
+
export function ActiveContentBadges() {
const { note } = useNoteContext();
const info = useActiveContentInfo(note);
- console.log("Got inf ", info);
- return (info &&
+ return (note && info &&
<>
{info.type === "iconPack" && }
-
+
>
);
}
@@ -29,15 +32,35 @@ function IconPackBadge() {
);
}
-function ActiveContentToggle({ info }: { info: ActiveContentInfo }) {
+function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentInfo }) {
return info && {
+ const attrs = note.getOwnedAttributes()
+ .filter(attr => {
+ if (attr.isInheritable) return false;
+ const baseName = getNameWithoutPrefix(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.
+ await attributes.addLabel(note.noteId, newName, attr.value);
+ await attributes.removeAttributeById(note.noteId, attr.attributeId);
+ }
+ }}
/>;
}
-const activeContentLabels = [ "iconPack" ] as const;
+function getNameWithoutPrefix(name: string) {
+ return name.startsWith("disabled:") ? name.substring(9) : name;
+}
interface ActiveContentInfo {
type: "iconPack";
From 46556c1c14bfbbd7d807ce1b475e2752536fc2c6 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 10:40:19 +0200
Subject: [PATCH 07/37] chore(badges/content): make toggle more compact
---
apps/client/src/translations/en/translation.json | 5 +++++
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 8 +++++---
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index f434ffeddd..48f5707563 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2288,5 +2288,10 @@
},
"bookmark_buttons": {
"bookmarks": "Bookmarks"
+ },
+ "active_content_badges": {
+ "type_icon_pack": "Icon pack",
+ "toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
+ "toggle_tooltip_disable_tooltip": "Click to disable this {{type}}."
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 8b0cb3744c..6f93ada5cb 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -3,6 +3,7 @@ import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
+import { t } from "../../services/i18n";
import { Badge } from "../react/Badge";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useTriliumEvent } from "../react/hooks";
@@ -27,16 +28,17 @@ function IconPackBadge() {
);
}
function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentInfo }) {
return info && {
const attrs = note.getOwnedAttributes()
.filter(attr => {
From a68e82c1c89481f22fd92d99cd95a77ab0ae1062 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 10:57:32 +0200
Subject: [PATCH 08/37] feat(badges/content): basic support for backend scripts
---
.../src/translations/en/translation.json | 1 +
.../widgets/layout/ActiveContentBadges.tsx | 34 +++++++++++++++----
2 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 48f5707563..8717ecac23 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2291,6 +2291,7 @@
},
"active_content_badges": {
"type_icon_pack": "Icon pack",
+ "type_backend_script": "Backend script",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}."
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 6f93ada5cb..dc867d8828 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -11,34 +11,50 @@ import { useNoteContext, useTriliumEvent } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous);
const activeContentLabels = [ "iconPack" ] as const;
+const typeIconMappings: Record = {
+ iconPack: "bx bx-package",
+ backendScript: "bx bx-server"
+};
+
export function ActiveContentBadges() {
const { note } = useNoteContext();
const info = useActiveContentInfo(note);
return (note && info &&
<>
- {info.type === "iconPack" && }
+
>
);
}
-function IconPackBadge() {
+function ActiveContentBadge({ info }: { note: FNote, info: ActiveContentInfo }) {
return (
);
}
+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");
+ }
+}
+
function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentInfo }) {
+ const typeTranslation = getTranslationForType(info.type);
+
return info && {
const attrs = note.getOwnedAttributes()
.filter(attr => {
@@ -65,7 +81,7 @@ function getNameWithoutPrefix(name: string) {
}
interface ActiveContentInfo {
- type: "iconPack";
+ type: "iconPack" | "backendScript";
isEnabled: boolean;
}
@@ -81,6 +97,10 @@ function useActiveContentInfo(note: FNote | null | undefined) {
return;
}
+ if (note.type === "code" && note.mime === "application/javascript;env=backend") {
+ type = "backendScript";
+ }
+
for (const labelToCheck of activeContentLabels ) {
if (note.hasLabel(labelToCheck)) {
type = labelToCheck;
From 66ff009b72622d497ba14b03cae0575f5a8caf38 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:08:30 +0200
Subject: [PATCH 09/37] feat(badges/content): option to open documentation
---
.../src/translations/en/translation.json | 3 +-
.../widgets/layout/ActiveContentBadges.tsx | 31 ++++++++++++++-----
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 8717ecac23..8145a74231 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2293,6 +2293,7 @@
"type_icon_pack": "Icon pack",
"type_backend_script": "Backend script",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
- "toggle_tooltip_disable_tooltip": "Click to disable 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 dc867d8828..088d97cd6e 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -4,16 +4,27 @@ import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
-import { Badge } from "../react/Badge";
+import { openInAppHelpFromUrl } from "../../services/utils";
+import { Badge, BadgeWithDropdown } from "../react/Badge";
+import { FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useTriliumEvent } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous);
const activeContentLabels = [ "iconPack" ] as const;
-const typeIconMappings: Record = {
- iconPack: "bx bx-package",
- backendScript: "bx bx-server"
+const typeMappings: Record = {
+ iconPack: {
+ icon: "bx bx-package",
+ helpPage: "g1mlRoU8CsqC"
+ },
+ backendScript: {
+ icon: "bx bx-server",
+ helpPage: "SPirpZypehBG"
+ }
};
export function ActiveContentBadges() {
@@ -29,12 +40,18 @@ export function ActiveContentBadges() {
}
function ActiveContentBadge({ info }: { note: FNote, info: ActiveContentInfo }) {
+ const { icon, helpPage } = typeMappings[info.type];
return (
-
+ >
+ openInAppHelpFromUrl(helpPage)}
+ >{t("active_content_badges.menu_docs")}
+
);
}
From a739d2856357476b80edb7aa0aa8f4f5eb561b6c Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:12:39 +0200
Subject: [PATCH 10/37] feat(badges/content): option to open API docs
---
.../widgets/layout/ActiveContentBadges.tsx | 22 ++++++++++++++-----
.../src/widgets/ribbon/NoteActionsCustom.tsx | 10 ---------
2 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 088d97cd6e..daa787b927 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -5,7 +5,7 @@ import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import { openInAppHelpFromUrl } from "../../services/utils";
-import { Badge, BadgeWithDropdown } from "../react/Badge";
+import { BadgeWithDropdown } from "../react/Badge";
import { FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useTriliumEvent } from "../react/hooks";
@@ -16,14 +16,21 @@ const activeContentLabels = [ "iconPack" ] as const;
const typeMappings: Record = {
iconPack: {
icon: "bx bx-package",
- helpPage: "g1mlRoU8CsqC"
+ helpPage: "g1mlRoU8CsqC",
},
backendScript: {
icon: "bx bx-server",
- helpPage: "SPirpZypehBG"
+ helpPage: "SPirpZypehBG",
+ apiDocsPage: "MEtfsqa5VwNi"
+ },
+ frontendScript: {
+ icon: "bx bx-window",
+ helpPage: "yIhgI5H7A2Sm",
+ apiDocsPage: "Q2z6av6JZVWm"
}
};
@@ -40,7 +47,7 @@ export function ActiveContentBadges() {
}
function ActiveContentBadge({ info }: { note: FNote, info: ActiveContentInfo }) {
- const { icon, helpPage } = typeMappings[info.type];
+ const { icon, helpPage, apiDocsPage } = typeMappings[info.type];
return (
openInAppHelpFromUrl(helpPage)}
>{t("active_content_badges.menu_docs")}
+
+ {apiDocsPage && openInAppHelpFromUrl(apiDocsPage)}
+ >{t("code_buttons.trilium_api_docs_button_title")} }
);
}
@@ -98,7 +110,7 @@ function getNameWithoutPrefix(name: string) {
}
interface ActiveContentInfo {
- type: "iconPack" | "backendScript";
+ type: "iconPack" | "backendScript" | "frontendScript";
isEnabled: boolean;
}
diff --git a/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx b/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx
index 2864e50eaf..482cd1a693 100644
--- a/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx
+++ b/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx
@@ -70,7 +70,6 @@ export default function NoteActionsCustom(props: NoteActionsCustomProps) {
>
-
@@ -230,15 +229,6 @@ function SaveToNoteButton({ note, noteMime }: NoteActionsCustomInnerProps) {
/>;
}
-function OpenTriliumApiDocsButton({ noteMime }: NoteActionsCustomInnerProps) {
- const isEnabled = noteMime.startsWith("application/javascript;env=");
- return isEnabled && openInAppHelpFromUrl(noteMime.endsWith("frontend") ? "Q2z6av6JZVWm" : "MEtfsqa5VwNi")}
- />;
-}
-
function InAppHelpButton({ note }: NoteActionsCustomInnerProps) {
const helpUrl = getHelpUrlForNote(note);
const isEnabled = !!helpUrl;
From ef75de63fe0f47c199d699c8c65a7d112eed4237 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:15:08 +0200
Subject: [PATCH 11/37] feat(badges/content): option to execute now
---
.../src/translations/en/translation.json | 3 ++-
.../widgets/layout/ActiveContentBadges.tsx | 21 +++++++++++++++----
2 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 8145a74231..c8ded0fbb4 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2294,6 +2294,7 @@
"type_backend_script": "Backend script",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}.",
- "menu_docs": "Open documentation"
+ "menu_docs": "Open documentation",
+ "menu_execute_now": "Execute script now"
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index daa787b927..936c6da7a6 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -6,7 +6,7 @@ import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import { openInAppHelpFromUrl } from "../../services/utils";
import { BadgeWithDropdown } from "../react/Badge";
-import { FormListItem } from "../react/FormList";
+import { FormDropdownDivider, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useTriliumEvent } from "../react/hooks";
@@ -17,6 +17,7 @@ const typeMappings: Record = {
iconPack: {
icon: "bx bx-package",
@@ -25,12 +26,14 @@ const typeMappings: Record
+ {isExecutable && (
+ <>
+ {t("active_content_badges.menu_execute_now")}
+
+ >
+ )}
+
openInAppHelpFromUrl(helpPage)}
From bd1f0909a24df62bfcd4ff39d932f27a3fb98bcc Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:30:05 +0200
Subject: [PATCH 12/37] feat(badges/content): configurable backend run options
---
.../src/translations/en/translation.json | 7 ++-
.../widgets/layout/ActiveContentBadges.tsx | 51 +++++++++++++++++--
2 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index c8ded0fbb4..cc468ff597 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2295,6 +2295,11 @@
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}.",
"menu_docs": "Open documentation",
- "menu_execute_now": "Execute script now"
+ "menu_execute_now": "Execute script now",
+ "menu_run": "Run automatically",
+ "menu_run_disabled": "Manually",
+ "menu_run_backend_startup": "When the backend starts up",
+ "menu_run_hourly": "Hourly",
+ "menu_run_daily": "Daily"
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 936c6da7a6..a0b3af16c7 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -1,4 +1,5 @@
import { BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
+import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
@@ -6,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, FormListItem } from "../react/FormList";
+import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
-import { useNoteContext, useTriliumEvent } from "../react/hooks";
+import { useNoteContext, useNoteLabel, useTriliumEvent, useTriliumOption } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous);
const activeContentLabels = [ "iconPack" ] as const;
@@ -49,7 +50,7 @@ export function ActiveContentBadges() {
);
}
-function ActiveContentBadge({ info }: { note: FNote, info: ActiveContentInfo }) {
+function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentInfo }) {
const { icon, helpPage, apiDocsPage, isExecutable } = typeMappings[info.type];
return (
{t("active_content_badges.menu_execute_now")}
+
>
)}
@@ -80,6 +82,49 @@ function ActiveContentBadge({ info }: { note: FNote, info: ActiveContentInfo })
);
}
+function ScriptRunOptions({ note }: { note: FNote }) {
+ const [ run, setRun ] = useNoteLabel(note, "run");
+
+ const options: {
+ title: string;
+ value: string | null;
+ type: "backendScript" | "frontendScript";
+ }[] = [
+ {
+ title: t("active_content_badges.menu_run_disabled"),
+ value: null,
+ type: "backendScript"
+ },
+ {
+ 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"
+ },
+ ];
+
+ return (
+
+ {options.map(({ title, value }) => (
+ setRun(value)}
+ checked={run ? run === value : value === null }
+ >{title}
+ ))}
+
+ );
+}
+
function getTranslationForType(type: ActiveContentInfo["type"]) {
switch (type) {
case "iconPack":
From 2d34cdef5efde8db7c96d03ff62110d56b575784 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:37:20 +0200
Subject: [PATCH 13/37] feat(badges/content): integrate options for frontend
script
---
.../src/translations/en/translation.json | 5 +++-
.../widgets/layout/ActiveContentBadges.tsx | 26 ++++++++++++++-----
2 files changed, 24 insertions(+), 7 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index cc468ff597..d2fdb2f4b9 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2292,6 +2292,7 @@
"active_content_badges": {
"type_icon_pack": "Icon pack",
"type_backend_script": "Backend script",
+ "type_frontend_script": "Frontend script",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}.",
"menu_docs": "Open documentation",
@@ -2300,6 +2301,8 @@
"menu_run_disabled": "Manually",
"menu_run_backend_startup": "When the backend starts up",
"menu_run_hourly": "Hourly",
- "menu_run_daily": "Daily"
+ "menu_run_daily": "Daily",
+ "menu_run_frontend_startup": "When the desktop frontend starts up",
+ "menu_run_mobile_startup": "When the mobile frontend starts up"
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index a0b3af16c7..4d00cbca26 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -64,7 +64,7 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
icon="bx bx-play"
triggerCommand="runActiveNote"
>{t("active_content_badges.menu_execute_now")}
-
+
>
)}
@@ -82,18 +82,18 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
);
}
-function ScriptRunOptions({ note }: { note: FNote }) {
+function ScriptRunOptions({ info, note }: { note: FNote, info: ActiveContentInfo }) {
const [ run, setRun ] = useNoteLabel(note, "run");
const options: {
title: string;
value: string | null;
- type: "backendScript" | "frontendScript";
- }[] = [
+ type: "both" | "backendScript" | "frontendScript";
+ }[] = ([
{
title: t("active_content_badges.menu_run_disabled"),
value: null,
- type: "backendScript"
+ type: "both"
},
{
title: t("active_content_badges.menu_run_backend_startup"),
@@ -110,7 +110,17 @@ function ScriptRunOptions({ note }: { note: FNote }) {
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 (
@@ -131,6 +141,8 @@ function getTranslationForType(type: ActiveContentInfo["type"]) {
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");
}
}
@@ -186,6 +198,8 @@ function useActiveContentInfo(note: FNote | null | undefined) {
if (note.type === "code" && note.mime === "application/javascript;env=backend") {
type = "backendScript";
+ } else if (note.type === "code" && note.mime === "application/javascript;env=frontend") {
+ type = "frontendScript";
}
for (const labelToCheck of activeContentLabels ) {
From 3107bc88400851a5427e5d1deadb05ce82fb4289 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:47:42 +0200
Subject: [PATCH 14/37] feat(badges/content): add toggle for widget
---
.../src/translations/en/translation.json | 3 ++-
.../src/widgets/layout/ActiveContentBadges.tsx | 18 ++++++++++++++++--
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index d2fdb2f4b9..5e6bba615a 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2303,6 +2303,7 @@
"menu_run_hourly": "Hourly",
"menu_run_daily": "Daily",
"menu_run_frontend_startup": "When the desktop frontend starts up",
- "menu_run_mobile_startup": "When the mobile frontend starts up"
+ "menu_run_mobile_startup": "When the mobile frontend starts up",
+ "menu_toggle_widget": "Widget"
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 4d00cbca26..64ef118181 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, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
-import { useNoteContext, useNoteLabel, useTriliumEvent, useTriliumOption } from "../react/hooks";
+import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent, useTriliumOption } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous);
const activeContentLabels = [ "iconPack" ] as const;
@@ -65,6 +65,7 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
triggerCommand="runActiveNote"
>{t("active_content_badges.menu_execute_now")}
+ {info.type === "frontendScript" && }
>
)}
@@ -135,6 +136,19 @@ function ScriptRunOptions({ info, note }: { note: FNote, info: ActiveContentInfo
);
}
+function WidgetSwitcher({ note }: { note: FNote }) {
+ const [ widget, setWidget ] = useNoteLabelBoolean(note, "widget");
+
+ return (
+ setWidget(newValue)}
+ />
+ );
+}
+
function getTranslationForType(type: ActiveContentInfo["type"]) {
switch (type) {
case "iconPack":
From 7a3e7fccec08c9abe48381cc705beefa8f0d068b Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 11:54:27 +0200
Subject: [PATCH 15/37] feat(badges/content): handle widgets as separate
content type
---
.../src/translations/en/translation.json | 4 ++-
.../widgets/layout/ActiveContentBadges.tsx | 35 ++++++++++++-------
2 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 5e6bba615a..2ced551e90 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2293,6 +2293,7 @@
"type_icon_pack": "Icon pack",
"type_backend_script": "Backend script",
"type_frontend_script": "Frontend script",
+ "type_widget": "Widget",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}.",
"menu_docs": "Open documentation",
@@ -2304,6 +2305,7 @@
"menu_run_daily": "Daily",
"menu_run_frontend_startup": "When the desktop frontend starts up",
"menu_run_mobile_startup": "When the mobile frontend starts up",
- "menu_toggle_widget": "Widget"
+ "menu_change_to_widget": "Change to widget",
+ "menu_change_to_frontend_script": "Change to frontend script"
}
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 64ef118181..70190fa855 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -1,5 +1,4 @@
import { BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
-import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
@@ -7,12 +6,12 @@ import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import { openInAppHelpFromUrl } from "../../services/utils";
import { BadgeWithDropdown } from "../react/Badge";
-import { FormDropdownDivider, FormDropdownSubmenu, FormListItem, FormListToggleableItem } from "../react/FormList";
+import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
-import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent, useTriliumOption } from "../react/hooks";
+import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous);
-const activeContentLabels = [ "iconPack" ] as const;
+const activeContentLabels = [ "iconPack", "widget" ] as const;
const typeMappings: Record{t("active_content_badges.menu_execute_now")}
- {info.type === "frontendScript" && }
+
+ >
+ )}
+
+ {(info.type === "frontendScript" || info.type === "widget") && (
+ <>
+
>
)}
@@ -140,12 +149,12 @@ function WidgetSwitcher({ note }: { note: FNote }) {
const [ widget, setWidget ] = useNoteLabelBoolean(note, "widget");
return (
- setWidget(newValue)}
- />
+ setWidget(!widget)}
+ >
+ {widget ? t("active_content_badges.menu_change_to_frontend_script") : t("active_content_badges.menu_change_to_widget")}
+
);
}
@@ -157,6 +166,8 @@ function getTranslationForType(type: ActiveContentInfo["type"]) {
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");
}
}
@@ -194,7 +205,7 @@ function getNameWithoutPrefix(name: string) {
}
interface ActiveContentInfo {
- type: "iconPack" | "backendScript" | "frontendScript";
+ type: "iconPack" | "backendScript" | "frontendScript" | "widget";
isEnabled: boolean;
}
From 866d3110da1d170b4554c35d060a0235896b74dd Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 12:05:24 +0200
Subject: [PATCH 16/37] feat(badges/content): add badge for custom CSS
---
apps/client/src/translations/en/translation.json | 1 +
.../src/widgets/layout/ActiveContentBadges.tsx | 12 +++++++++---
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 2ced551e90..317c64bd1c 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2294,6 +2294,7 @@
"type_backend_script": "Backend script",
"type_frontend_script": "Frontend script",
"type_widget": "Widget",
+ "type_app_css": "Custom CSS",
"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 70190fa855..ae5dc4ca0f 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -10,8 +10,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);
-const activeContentLabels = [ "iconPack", "widget" ] as const;
+const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || a.name === "appCss");
+const activeContentLabels = [ "iconPack", "widget", "appCss" ] as const;
const typeMappings: Record
Date: Sat, 14 Feb 2026 12:22:27 +0200
Subject: [PATCH 17/37] feat(badges/content): disable toggle when not necessary
---
apps/client/src/entities/fnote.ts | 4 ++
.../widgets/layout/ActiveContentBadges.tsx | 37 ++++++++++++++-----
2 files changed, 32 insertions(+), 9 deletions(-)
diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts
index f161d7adb1..fbe9493128 100644
--- a/apps/client/src/entities/fnote.ts
+++ b/apps/client/src/entities/fnote.ts
@@ -700,6 +700,10 @@ export default class FNote {
return this.hasAttribute(LABEL, name);
}
+ hasLabelOrDisabled(name: string) {
+ return this.hasLabel(name) || this.hasLabel(`disabled:${name}`);
+ }
+
/**
* @param name - label name
* @returns true if label exists (including inherited) and does not have "false" value.
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index ae5dc4ca0f..35160eee8d 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -51,8 +51,8 @@ export function ActiveContentBadges() {
return (note && info &&
<>
+ {info.canToggleEnabled && }
-
>
);
}
@@ -151,15 +151,23 @@ function ScriptRunOptions({ info, note }: { note: FNote, info: ActiveContentInfo
function WidgetSwitcher({ note }: { note: FNote }) {
const [ widget, setWidget ] = useNoteLabelBoolean(note, "widget");
+ const [ disabledWidget, setDisabledWidget ] = useNoteLabelBoolean(note, "disabled:widget");
- return (
- {
+ setWidget(false);
+ setDisabledWidget(false);
+ }}
+ >{t("active_content_badges.menu_change_to_frontend_script")}
+ : setWidget(!widget)}
- >
- {widget ? t("active_content_badges.menu_change_to_frontend_script") : t("active_content_badges.menu_change_to_widget")}
-
- );
+ onClick={() => {
+ setWidget(true);
+ }}
+ >{t("active_content_badges.menu_change_to_widget")};
+
}
function getTranslationForType(type: ActiveContentInfo["type"]) {
@@ -213,6 +221,7 @@ function getNameWithoutPrefix(name: string) {
interface ActiveContentInfo {
type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss";
isEnabled: boolean;
+ canToggleEnabled: boolean;
}
function useActiveContentInfo(note: FNote | null | undefined) {
@@ -221,6 +230,7 @@ function useActiveContentInfo(note: FNote | null | undefined) {
function refresh() {
let type: ActiveContentInfo["type"] | null = null;
let isEnabled = true;
+ let canToggleEnabled = false;
if (!note) {
setInfo(null);
@@ -229,8 +239,17 @@ function useActiveContentInfo(note: FNote | null | undefined) {
if (note.type === "code" && note.mime === "application/javascript;env=backend") {
type = "backendScript";
+ for (const backendLabel of [ "run", "customRequestHandler", "customResourceProvider" ]) {
+ isEnabled ||= note.hasLabel(backendLabel);
+
+ if (!canToggleEnabled && note.hasLabelOrDisabled(backendLabel)) {
+ canToggleEnabled = true;
+ }
+ }
} else if (note.type === "code" && note.mime === "application/javascript;env=frontend") {
type = "frontendScript";
+ isEnabled = note.hasLabel("widget") || note.hasLabel("run");
+ canToggleEnabled = note.hasLabelOrDisabled("widget") || note.hasLabelOrDisabled("run");
}
for (const labelToCheck of activeContentLabels ) {
@@ -245,7 +264,7 @@ function useActiveContentInfo(note: FNote | null | undefined) {
}
if (type) {
- setInfo({ type, isEnabled });
+ setInfo({ type, isEnabled, canToggleEnabled });
} else {
setInfo(null);
}
From 50dcd3ba443f30284c1fabd0fd964040c5d0dbbb Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 12:30:43 +0200
Subject: [PATCH 18/37] feat(badges/content): add support for render note
---
.../src/translations/en/translation.json | 1 +
.../widgets/layout/ActiveContentBadges.tsx | 30 ++++++++++++++-----
2 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 317c64bd1c..2f78d6a341 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2295,6 +2295,7 @@
"type_frontend_script": "Frontend script",
"type_widget": "Widget",
"type_app_css": "Custom CSS",
+ "type_render_note": "Render note",
"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 35160eee8d..d39ab75f36 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -13,6 +13,12 @@ import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } fr
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || a.name === "appCss");
const activeContentLabels = [ "iconPack", "widget", "appCss" ] as const;
+interface ActiveContentInfo {
+ type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss" | "renderNote";
+ isEnabled: boolean;
+ canToggleEnabled: boolean;
+}
+
const typeMappings: Record(null);
@@ -237,7 +247,11 @@ function useActiveContentInfo(note: FNote | null | undefined) {
return;
}
- if (note.type === "code" && note.mime === "application/javascript;env=backend") {
+ if (note.type === "render") {
+ type = "renderNote";
+ isEnabled = note.hasRelation("renderNote");
+ canToggleEnabled = note.hasRelation("renderNote") || note.hasRelation("disabled:renderNote");
+ } else if (note.type === "code" && note.mime === "application/javascript;env=backend") {
type = "backendScript";
for (const backendLabel of [ "run", "customRequestHandler", "customResourceProvider" ]) {
isEnabled ||= note.hasLabel(backendLabel);
From 5a2b04adba376c8212ff0a517adcd683a4e59625 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 12:35:06 +0200
Subject: [PATCH 19/37] feat(badges/content): add support for web view
---
apps/client/src/translations/en/translation.json | 1 +
.../src/widgets/layout/ActiveContentBadges.tsx | 12 +++++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json
index 2f78d6a341..ea5fda411a 100644
--- a/apps/client/src/translations/en/translation.json
+++ b/apps/client/src/translations/en/translation.json
@@ -2296,6 +2296,7 @@
"type_widget": "Widget",
"type_app_css": "Custom CSS",
"type_render_note": "Render note",
+ "type_web_view": "Web view",
"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 d39ab75f36..644ad7c49e 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -14,7 +14,7 @@ const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || a.n
const activeContentLabels = [ "iconPack", "widget", "appCss" ] as const;
interface ActiveContentInfo {
- type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss" | "renderNote";
+ type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss" | "renderNote" | "webView";
isEnabled: boolean;
canToggleEnabled: boolean;
}
@@ -52,6 +52,10 @@ const typeMappings: Record
Date: Sat, 14 Feb 2026 12:41:41 +0200
Subject: [PATCH 20/37] feat(badges/content): improve color support
---
apps/client/src/stylesheets/theme-next-dark.css | 7 ++++---
apps/client/src/stylesheets/theme-next-light.css | 2 +-
apps/client/src/widgets/layout/ActiveContentBadges.tsx | 3 ++-
apps/client/src/widgets/layout/NoteBadges.css | 5 ++++-
4 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/stylesheets/theme-next-dark.css b/apps/client/src/stylesheets/theme-next-dark.css
index f8fb305726..cf88b7f2d2 100644
--- a/apps/client/src/stylesheets/theme-next-dark.css
+++ b/apps/client/src/stylesheets/theme-next-dark.css
@@ -210,6 +210,7 @@
--badge-share-background-color: #4d4d4d;
--badge-clipped-note-background-color: #295773;
--badge-execute-background-color: #604180;
+ --badge-active-content-background-color: rgb(12, 68, 70);
--note-icon-background-color: #444444;
--note-icon-color: #d4d4d4;
@@ -238,9 +239,9 @@
--bottom-panel-background-color: #11111180;
--bottom-panel-title-bar-background-color: #3F3F3F80;
-
+
--status-bar-border-color: var(--main-border-color);
-
+
--scrollbar-thumb-color: #fdfdfd5c;
--scrollbar-thumb-hover-color: #ffffff7d;
--scrollbar-background-color: transparent;
@@ -351,4 +352,4 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
.note-split.with-hue *::selection,
.quick-edit-dialog-wrapper.with-hue *::selection {
--selection-background-color: hsl(var(--custom-color-hue), 49.2%, 35%);
-}
\ No newline at end of file
+}
diff --git a/apps/client/src/stylesheets/theme-next-light.css b/apps/client/src/stylesheets/theme-next-light.css
index 44f594d0ab..00678c6ae9 100644
--- a/apps/client/src/stylesheets/theme-next-light.css
+++ b/apps/client/src/stylesheets/theme-next-light.css
@@ -202,7 +202,7 @@
--badge-share-background-color: #6b6b6b;
--badge-clipped-note-background-color: #2284c0;
--badge-execute-background-color: #7b47af;
- --badge-icon-pack-background-color: rgb(228, 163, 44);
+ --badge-active-content-background-color: rgb(27, 164, 168);
--note-icon-background-color: #4f4f4f;
--note-icon-color: white;
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index 644ad7c49e..ff94f19f04 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -1,4 +1,5 @@
import { BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
+import clsx from "clsx";
import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
@@ -75,7 +76,7 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
const { icon, helpPage, apiDocsPage, isExecutable } = typeMappings[info.type];
return (
diff --git a/apps/client/src/widgets/layout/NoteBadges.css b/apps/client/src/widgets/layout/NoteBadges.css
index b2b6e18f77..013963adb4 100644
--- a/apps/client/src/widgets/layout/NoteBadges.css
+++ b/apps/client/src/widgets/layout/NoteBadges.css
@@ -37,7 +37,10 @@
pointer-events: none;
}
}
- &.icon-pack-badge { --color: var(--badge-icon-pack-background-color); }
+ &.active-content-badge { --color: var(--badge-active-content-background-color); }
+ &.active-content-badge.disabled {
+ opacity: 0.5;
+ }
min-width: 0;
From 2ef4eb7eaee9281e1319438241ecf379ef3fa320 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sat, 14 Feb 2026 12:43:40 +0200
Subject: [PATCH 21/37] chore(client): fix type errors
---
packages/commons/src/lib/attribute_names.ts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts
index b524b409f0..31997489ce 100644
--- a/packages/commons/src/lib/attribute_names.ts
+++ b/packages/commons/src/lib/attribute_names.ts
@@ -22,6 +22,11 @@ type Labels = {
pageUrl: string;
dateNote: string;
+ // Scripting
+ run: string;
+ widget: boolean;
+ "disabled:widget": boolean;
+
// Tree specific
subtreeHidden: boolean;
From 15e569dcea32449a79c1d15cdd27724d8f5ba8e2 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 11:32:27 +0200
Subject: [PATCH 22/37] chore(client): address requested changes
---
apps/client/src/entities/fnote.ts | 5 +++++
.../src/widgets/layout/ActiveContentBadges.tsx | 12 ++++++------
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts
index fbe9493128..2b515f2412 100644
--- a/apps/client/src/entities/fnote.ts
+++ b/apps/client/src/entities/fnote.ts
@@ -700,6 +700,11 @@ export default class FNote {
return this.hasAttribute(LABEL, name);
}
+ /**
+ * Returns `true` if the note has a label with the given name (same as {@link hasOwnedLabel}), or it has a label with the `disabled:` prefix (for example due to a safe import).
+ * @param name the name of the label to look for.
+ * @returns `true` if the label exists, or its version with the `disabled:` prefix.
+ */
hasLabelOrDisabled(name: string) {
return this.hasLabel(name) || this.hasLabel(`disabled:${name}`);
}
diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
index ff94f19f04..80b37ec291 100644
--- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx
+++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx
@@ -76,7 +76,7 @@ function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentIn
const { icon, helpPage, apiDocsPage, isExecutable } = typeMappings[info.type];
return (
@@ -200,7 +200,7 @@ function getTranslationForType(type: ActiveContentInfo["type"]) {
case "renderNote":
return t("active_content_badges.type_render_note");
case "webView":
- return t("note_types.web-view");
+ return t("active_content_badges.type_web_view");
}
}
@@ -210,8 +210,8 @@ function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentI
return info && {
const attrs = note.getOwnedAttributes()
.filter(attr => {
@@ -227,7 +227,7 @@ function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentI
// 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.addLabel(note.noteId, newName, attr.value);
+ await attributes.setLabel(note.noteId, newName, attr.value);
} else {
await attributes.setRelation(note.noteId, newName, attr.value);
}
@@ -246,7 +246,7 @@ function useActiveContentInfo(note: FNote | null | undefined) {
function refresh() {
let type: ActiveContentInfo["type"] | null = null;
- let isEnabled = true;
+ let isEnabled = false;
let canToggleEnabled = false;
if (!note) {
From 1588c8103c5a05793cf97e43ac12caea7271bc18 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 12:32:41 +0200
Subject: [PATCH 23/37] docs(user): improve documentation on scripts & active
content
---
.../doc_notes/en/User Guide/!!!meta.json | 2 +-
.../Active content.html | 126 ++++++++
.../Themes/Icon Packs.html | 7 +-
.../User Guide/Note Types/Render Note.html | 45 +--
.../User Guide/Scripting/Backend scripts.html | 29 ++
.../Scripting/Backend scripts/Events.html | 258 ++++++++--------
.../User Guide/Scripting/Frontend Basics.html | 153 +++++-----
.../Frontend Basics/Custom Widgets.html | 161 +++++-----
.../Frontend Basics/Launch Bar Widgets.html | 26 +-
.../Developer Guide/Documentation.md | 2 +-
docs/User Guide/!!!meta.json | 280 +++++++++++++++++-
.../Active content.md | 33 +++
.../User Guide/Note Types/Render Note.md | 10 +-
.../User Guide/Scripting/Backend scripts.md | 24 ++
.../Scripting/Backend scripts/Events.md | 2 +-
.../User Guide/Scripting/Frontend Basics.md | 63 ++--
.../Frontend Basics/Custom Widgets.md | 24 +-
.../Frontend Basics/Launch Bar Widgets.md | 6 +-
18 files changed, 865 insertions(+), 386 deletions(-)
create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Active content.html
create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Backend scripts.html
create mode 100644 docs/User Guide/User Guide/Basic Concepts and Features/Active content.md
create mode 100644 docs/User Guide/User Guide/Scripting/Backend scripts.md
diff --git a/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json b/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
index b828947f2c..498d3a4aa5 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
+++ b/apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
@@ -1 +1 @@
-[{"id":"_help_BOCnjTMBCoxW","title":"Feature Highlights","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Feature Highlights"},{"name":"iconClass","value":"bx bx-star","type":"label"}]},{"id":"_help_Otzi9La2YAUX","title":"Installation & Setup","type":"book","attributes":[{"name":"iconClass","value":"bx bx-cog","type":"label"}],"children":[{"id":"_help_poXkQfguuA0U","title":"Desktop Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation"},{"name":"iconClass","value":"bx bx-desktop","type":"label"}],"children":[{"id":"_help_nRqcgfTb97uV","title":"Using the desktop application as a server","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Using the desktop application "},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_Rp0q8bSP6Ayl","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Nix flake"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]}]},{"id":"_help_WOcw2SLH6tbX","title":"Server Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_Dgg7bR3b6K9j","title":"1. Installing the server","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_3tW6mORuTHnB","title":"Packaged version for Linux","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Packaged version for Linux"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_rWX5eY045zbE","title":"Using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker"},{"name":"iconClass","value":"bx bxl-docker","type":"label"}]},{"id":"_help_moVgBcoxE3EK","title":"On NixOS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/On NixOS"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_J1Bb6lVlwU5T","title":"Manually","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Manually"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]},{"id":"_help_DCmT6e7clMoP","title":"Using Kubernetes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Kubernetes"},{"name":"iconClass","value":"bx bxl-kubernetes","type":"label"}]},{"id":"_help_klCWNks3ReaQ","title":"Multiple server instances","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Multiple server instances"},{"name":"iconClass","value":"bx bxs-user-account","type":"label"}]}]},{"id":"_help_vcjrb3VVYPZI","title":"2. Reverse proxy","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_ud6MShXL4WpO","title":"Nginx","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Nginx"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_fDLvzOx29Pfg","title":"Apache using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Apache using Docker"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_LLzSMXACKhUs","title":"Trusted proxy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Trusted proxy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_5ERVJb9s4FRD","title":"Traefik","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Traefik"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_l2VkvOwUNfZj","title":"HTTPS (TLS)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/HTTPS (TLS)"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_0hzsNCP31IAB","title":"Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Authentication"},{"name":"iconClass","value":"bx bx-user","type":"label"}]},{"id":"_help_7DAiwaf8Z7Rz","title":"Multi-Factor Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Multi-Factor Authentication"},{"name":"iconClass","value":"bx bx-stopwatch","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Nix flake.clone"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_yeEaYqosGLSh","title":"Third-party cloud hosting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Third-party cloud hosting"},{"name":"iconClass","value":"bx bx-cloud","type":"label"}]},{"id":"_help_iGTnKjubbXkA","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]}]},{"id":"_help_cbkrhQjrkKrh","title":"Synchronization","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Synchronization"},{"name":"iconClass","value":"bx bx-sync","type":"label"}]},{"id":"_help_RDslemsQ6gCp","title":"Mobile Frontend","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Mobile Frontend"},{"name":"iconClass","value":"bx bx-mobile-alt","type":"label"}]},{"id":"_help_MtPxeAWVAzMg","title":"Web Clipper","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Web Clipper"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_n1lujUxCwipy","title":"Upgrading TriliumNext","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Upgrading TriliumNext"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]},{"id":"_help_ODY7qQn5m2FT","title":"Backup","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Backup"},{"name":"iconClass","value":"bx bx-hdd","type":"label"}]},{"id":"_help_tAassRL4RSQL","title":"Data directory","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Data directory"},{"name":"iconClass","value":"bx bx-folder-open","type":"label"}]}]},{"id":"_help_gh7bpGYxajRS","title":"Basic Concepts and Features","type":"book","attributes":[{"name":"iconClass","value":"bx bx-help-circle","type":"label"}],"children":[{"id":"_help_Vc8PjrjAGuOp","title":"UI Elements","type":"book","attributes":[{"name":"iconClass","value":"bx bx-window-alt","type":"label"}],"children":[{"id":"_help_x0JgW8UqGXvq","title":"Vertical and horizontal layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Vertical and horizontal layout"},{"name":"iconClass","value":"bx bxs-layout","type":"label"}]},{"id":"_help_x3i7MxGccDuM","title":"Global menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Global menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_oPVyFC7WL2Lp","title":"Note Tree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree"},{"name":"iconClass","value":"bx bxs-tree-alt","type":"label"}],"children":[{"id":"_help_YtSN43OrfzaA","title":"Note tree contextual menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Note tree contextual menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_yTjUdsOi4CIE","title":"Multiple selection","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Multiple selection"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_DvdZhoQZY9Yd","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_wyaGBBQrl4i3","title":"Hiding the subtree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree"},{"name":"iconClass","value":"bx bx-hide","type":"label"}]}]},{"id":"_help_BlN9DFI679QC","title":"Ribbon","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Ribbon"},{"name":"iconClass","value":"bx bx-dots-horizontal","type":"label"}]},{"id":"_help_3seOhtN8uLIY","title":"Tabs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs"},{"name":"iconClass","value":"bx bx-dock-top","type":"label"}]},{"id":"_help_xYmIYSP6wE3F","title":"Launch Bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Launch Bar"},{"name":"iconClass","value":"bx bx-sidebar","type":"label"}]},{"id":"_help_8YBEPzcpUgxw","title":"Note buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note buttons"},{"name":"iconClass","value":"bx bx-dots-vertical-rounded","type":"label"}]},{"id":"_help_4TIF1oA4VQRO","title":"Options","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Options"},{"name":"iconClass","value":"bx bx-cog","type":"label"}]},{"id":"_help_luNhaphA37EO","title":"Split View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Split View"},{"name":"iconClass","value":"bx bx-dock-right","type":"label"}]},{"id":"_help_XpOYSgsLkTJy","title":"Floating buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Floating buttons"},{"name":"iconClass","value":"bx bx-rectangle","type":"label"}]},{"id":"_help_RnaPdbciOfeq","title":"Right Sidebar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Right Sidebar"},{"name":"iconClass","value":"bx bxs-dock-right","type":"label"}]},{"id":"_help_r5JGHN99bVKn","title":"Recent Changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Recent Changes"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_ny318J39E5Z0","title":"Zoom","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Zoom"},{"name":"iconClass","value":"bx bx-zoom-in","type":"label"}]},{"id":"_help_lgKX7r3aL30x","title":"Note Tooltip","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tooltip"},{"name":"iconClass","value":"bx bx-message-detail","type":"label"}]},{"id":"_help_IjZS7iK5EXtb","title":"New Layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout"},{"name":"iconClass","value":"bx bx-layout","type":"label"}],"children":[{"id":"_help_I6p2a06hdnL6","title":"Breadcrumb","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Breadcrumb"},{"name":"iconClass","value":"bx bx-chevron-right","type":"label"}]},{"id":"_help_AlJ73vBCjWDw","title":"Status bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Status bar"},{"name":"iconClass","value":"bx bx-dock-bottom","type":"label"}]}]}]},{"id":"_help_BFs8mudNFgCS","title":"Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes"},{"name":"iconClass","value":"bx bx-notepad","type":"label"}],"children":[{"id":"_help_p9kXRFAkwN4o","title":"Note Icons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Icons"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_0vhv7lsOLy82","title":"Attachments","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Attachments"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_IakOLONlIfGI","title":"Cloning Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes"},{"name":"iconClass","value":"bx bx-duplicate","type":"label"}],"children":[{"id":"_help_TBwsyfadTA18","title":"Branch prefix","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes/Branch prefix"},{"name":"iconClass","value":"bx bx-rename","type":"label"}]}]},{"id":"_help_bwg0e8ewQMak","title":"Protected Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Protected Notes"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_MKmLg5x6xkor","title":"Archived Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Archived Notes"},{"name":"iconClass","value":"bx bx-box","type":"label"}]},{"id":"_help_vZWERwf8U3nx","title":"Note Revisions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_aGlEvb9hyDhS","title":"Sorting Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Sorting Notes"},{"name":"iconClass","value":"bx bx-sort-up","type":"label"}]},{"id":"_help_NRnIZmSMc5sj","title":"Printing & Exporting as PDF","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF"},{"name":"iconClass","value":"bx bx-printer","type":"label"}]},{"id":"_help_CoFPLs3dRlXc","title":"Read-Only Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Read-Only Notes"},{"name":"iconClass","value":"bx bx-edit-alt","type":"label"}]},{"id":"_help_0ESUbbAxVnoK","title":"Note List","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note List"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]}]},{"id":"_help_wArbEsdSae6g","title":"Navigation","type":"book","attributes":[{"name":"iconClass","value":"bx bx-navigation","type":"label"}],"children":[{"id":"_help_kBrnXNG3Hplm","title":"Tree Concepts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Tree Concepts"},{"name":"iconClass","value":"bx bx-pyramid","type":"label"}]},{"id":"_help_MMiBEQljMQh2","title":"Note Navigation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation"},{"name":"iconClass","value":"bx bxs-navigation","type":"label"}]},{"id":"_help_Ms1nauBra7gq","title":"Quick search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_F1r9QtzQLZqm","title":"Jump to...","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Jump to"},{"name":"iconClass","value":"bx bx-send","type":"label"}]},{"id":"_help_eIg8jdvaoNNd","title":"Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_u3YFHC9tQlpm","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmarks","type":"label"}]},{"id":"_help_OR8WJ7Iz9K4U","title":"Note Hoisting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Hoisting"},{"name":"iconClass","value":"bx bxs-chevrons-up","type":"label"}]},{"id":"_help_ZjLYv08Rp3qC","title":"Quick edit","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick edit"},{"name":"iconClass","value":"bx bx-edit","type":"label"}]},{"id":"_help_9sRHySam5fXb","title":"Workspaces","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Workspaces"},{"name":"iconClass","value":"bx bx-door-open","type":"label"}]},{"id":"_help_xWtq5NUHOwql","title":"Similar Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Similar Notes"},{"name":"iconClass","value":"bx bx-bar-chart","type":"label"}]},{"id":"_help_McngOG2jbUWX","title":"Search in note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search in note"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]}]},{"id":"_help_A9Oc6YKKc65v","title":"Keyboard Shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Keyboard Shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_Wy267RK4M69c","title":"Themes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes"},{"name":"iconClass","value":"bx bx-palette","type":"label"}],"children":[{"id":"_help_VbjZvtUek0Ln","title":"Theme Gallery","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Theme Gallery"},{"name":"iconClass","value":"bx bx-book-reader","type":"label"}]},{"id":"_help_gOKqSJgXLcIj","title":"Icon Packs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_mHbBMPDPkVV5","title":"Import & Export","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export"},{"name":"iconClass","value":"bx bx-import","type":"label"}],"children":[{"id":"_help_Oau6X9rCuegd","title":"Markdown","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}],"children":[{"id":"_help_rJ9grSgoExl9","title":"Supported syntax","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown/Supported syntax"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]}]},{"id":"_help_syuSEKf2rUGr","title":"Evernote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Evernote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}],"children":[{"id":"_help_dj3j8dG4th4l","title":"Process internal links by title","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_GnhlmrATVqcH","title":"OneNote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/OneNote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}]}]},{"id":"_help_rC3pL2aptaRE","title":"Zen mode","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Zen mode"},{"name":"iconClass","value":"bx bxs-yin-yang","type":"label"}]}]},{"id":"_help_s3YCWHBfmYuM","title":"Quick Start","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Quick Start"},{"name":"iconClass","value":"bx bx-run","type":"label"}]},{"id":"_help_i6dbnitykE5D","title":"FAQ","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/FAQ"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_KSZ04uQ2D1St","title":"Note Types","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types"},{"name":"iconClass","value":"bx bx-edit","type":"label"}],"children":[{"id":"_help_iPIMuisry3hd","title":"Text","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text"},{"name":"iconClass","value":"bx bx-note","type":"label"}],"children":[{"id":"_help_NwBbFdNZ9h7O","title":"Block quotes & admonitions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Block quotes & admonitions"},{"name":"iconClass","value":"bx bx-info-circle","type":"label"}]},{"id":"_help_oSuaNgyyKnhu","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmark","type":"label"}]},{"id":"_help_veGu4faJErEM","title":"Content language & Right-to-left support","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Content language & Right-to-le"},{"name":"iconClass","value":"bx bx-align-right","type":"label"}]},{"id":"_help_2x0ZAX9ePtzV","title":"Cut to subnote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Cut to subnote"},{"name":"iconClass","value":"bx bx-cut","type":"label"}]},{"id":"_help_UYuUB1ZekNQU","title":"Developer-specific formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_QxEyIjRBizuC","title":"Code blocks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting/Code blocks"},{"name":"iconClass","value":"bx bx-code","type":"label"}]}]},{"id":"_help_AgjCISero73a","title":"Footnotes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Footnotes"},{"name":"iconClass","value":"bx bx-bracket","type":"label"}]},{"id":"_help_nRhnJkTT8cPs","title":"Formatting toolbar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Formatting toolbar"},{"name":"iconClass","value":"bx bx-text","type":"label"}]},{"id":"_help_Gr6xFaF6ioJ5","title":"General formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/General formatting"},{"name":"iconClass","value":"bx bx-bold","type":"label"}]},{"id":"_help_AxshuNRegLAv","title":"Highlights list","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Highlights list"},{"name":"iconClass","value":"bx bx-highlight","type":"label"}]},{"id":"_help_mT0HEkOsz6i1","title":"Images","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images"},{"name":"iconClass","value":"bx bx-image-alt","type":"label"}],"children":[{"id":"_help_0Ofbk1aSuVRu","title":"Image references","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images/Image references"},{"name":"iconClass","value":"bx bxs-file-image","type":"label"}]}]},{"id":"_help_nBAXQFj20hS1","title":"Include Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Include Note"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_CohkqWQC1iBv","title":"Insert buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Insert buttons"},{"name":"iconClass","value":"bx bx-plus","type":"label"}]},{"id":"_help_oiVPnW8QfnvS","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_QEAPj01N5f7w","title":"Links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links"},{"name":"iconClass","value":"bx bx-link-alt","type":"label"}],"children":[{"id":"_help_3IDVtesTQ8ds","title":"External links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/External links"},{"name":"iconClass","value":"bx bx-link-external","type":"label"}]},{"id":"_help_hrZ1D00cLbal","title":"Internal (reference) links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/Internal (reference) links"},{"name":"iconClass","value":"bx bx-link","type":"label"}]}]},{"id":"_help_S6Xx8QIWTV66","title":"Lists","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Lists"},{"name":"iconClass","value":"bx bx-list-ul","type":"label"}]},{"id":"_help_QrtTYPmdd1qq","title":"Markdown-like formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Markdown-like formatting"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}]},{"id":"_help_YfYAtQBcfo5V","title":"Math Equations","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Math Equations"},{"name":"iconClass","value":"bx bx-math","type":"label"}]},{"id":"_help_dEHYtoWWi8ct","title":"Other features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Other features"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_gLt3vA97tMcp","title":"Premium features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features"},{"name":"iconClass","value":"bx bx-star","type":"label"}],"children":[{"id":"_help_ZlN4nump6EbW","title":"Slash Commands","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Slash Commands"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_pwc194wlRzcH","title":"Text Snippets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Text Snippets"},{"name":"iconClass","value":"bx bx-align-left","type":"label"}]},{"id":"_help_5wZallV2Qo1t","title":"Format Painter","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Format Painter"},{"name":"iconClass","value":"bx bxs-paint-roll","type":"label"}]}]},{"id":"_help_BFvAtE74rbP6","title":"Table of contents","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Table of contents"},{"name":"iconClass","value":"bx bx-heading","type":"label"}]},{"id":"_help_NdowYOC1GFKS","title":"Tables","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Tables"},{"name":"iconClass","value":"bx bx-table","type":"label"}]}]},{"id":"_help_6f9hih2hXXZk","title":"Code","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Code"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_m523cpzocqaD","title":"Saved Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Saved Search"},{"name":"iconClass","value":"bx bx-file-find","type":"label"}]},{"id":"_help_iRwzGnHPzonm","title":"Relation Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Relation Map"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_bdUJEHsAPYQR","title":"Note Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Note Map"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_HcABDtFCkbFN","title":"Render Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Render Note"},{"name":"iconClass","value":"bx bx-extension","type":"label"}]},{"id":"_help_s1aBHPd79XYj","title":"Mermaid Diagrams","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mermaid Diagrams"},{"name":"iconClass","value":"bx bx-selection","type":"label"}],"children":[{"id":"_help_RH6yLjjWJHof","title":"ELK layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mermaid Diagrams/ELK layout"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_WWgeUaBb7UfC","title":"Syntax reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://mermaid.js.org/intro/syntax-reference.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_grjYqerjn243","title":"Canvas","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Canvas"},{"name":"iconClass","value":"bx bx-pen","type":"label"}]},{"id":"_help_1vHRoWCEjj0L","title":"Web View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Web View"},{"name":"iconClass","value":"bx bx-globe-alt","type":"label"}]},{"id":"_help_gBbsAeiuUxI5","title":"Mind Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mind Map"},{"name":"iconClass","value":"bx bx-sitemap","type":"label"}]},{"id":"_help_W8vYD3Q1zjCR","title":"File","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File"},{"name":"iconClass","value":"bx bx-file-blank","type":"label"}],"children":[{"id":"_help_XJGJrpu7F9sh","title":"PDFs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File/PDFs"},{"name":"iconClass","value":"bx bxs-file-pdf","type":"label"}]}]}]},{"id":"_help_GTwFsgaA0lCt","title":"Collections","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections"},{"name":"iconClass","value":"bx bx-book","type":"label"}],"children":[{"id":"_help_xWbu3jpNWapp","title":"Calendar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Calendar"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]},{"id":"_help_2FvYrpmOXm29","title":"Table","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Table"},{"name":"iconClass","value":"bx bx-table","type":"label"}]},{"id":"_help_CtBQqbwXDx1w","title":"Kanban Board","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Kanban Board"},{"name":"iconClass","value":"bx bx-columns","type":"label"}]},{"id":"_help_81SGnPGMk7Xc","title":"Geo Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Geo Map"},{"name":"iconClass","value":"bx bx-map-alt","type":"label"}]},{"id":"_help_zP3PMqaG71Ct","title":"Presentation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Presentation"},{"name":"iconClass","value":"bx bx-slideshow","type":"label"}]},{"id":"_help_8QqnMzx393bx","title":"Grid View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Grid View"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_mULW0Q3VojwY","title":"List View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/List View"},{"name":"iconClass","value":"bx bx-list-ul","type":"label"}]}]},{"id":"_help_BgmBlOIl72jZ","title":"Troubleshooting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting"},{"name":"iconClass","value":"bx bx-bug","type":"label"}],"children":[{"id":"_help_wy8So3yZZlH9","title":"Reporting issues","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Reporting issues"},{"name":"iconClass","value":"bx bx-bug-alt","type":"label"}]},{"id":"_help_x59R8J8KV5Bp","title":"Anonymized Database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Anonymized Database"},{"name":"iconClass","value":"bx bx-low-vision","type":"label"}]},{"id":"_help_qzNzp9LYQyPT","title":"Error logs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Error logs"},{"name":"iconClass","value":"bx bx-comment-error","type":"label"}],"children":[{"id":"_help_bnyigUA2UK7s","title":"Backend (server) logs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Error logs/Backend (server) logs"},{"name":"iconClass","value":"bx bx-server","type":"label"}]},{"id":"_help_9yEHzMyFirZR","title":"Frontend logs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Error logs/Frontend logs"},{"name":"iconClass","value":"bx bx-window-alt","type":"label"}]}]},{"id":"_help_vdlYGAcpXAgc","title":"Synchronization fails with 504 Gateway Timeout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Synchronization fails with 504"},{"name":"iconClass","value":"bx bx-error","type":"label"}]},{"id":"_help_s8alTXmpFR61","title":"Refreshing the application","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Refreshing the application"},{"name":"iconClass","value":"bx bx-refresh","type":"label"}]}]},{"id":"_help_pKK96zzmvBGf","title":"Theme development","type":"book","attributes":[{"name":"iconClass","value":"bx bx-palette","type":"label"}],"children":[{"id":"_help_7NfNr5pZpVKV","title":"Creating a custom theme","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Creating a custom theme"},{"name":"iconClass","value":"bx bxs-color","type":"label"}]},{"id":"_help_WFGzWeUK6arS","title":"Customize the Next theme","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Customize the Next theme"},{"name":"iconClass","value":"bx bx-news","type":"label"}]},{"id":"_help_WN5z4M8ASACJ","title":"Reference","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Reference"},{"name":"iconClass","value":"bx bx-book-open","type":"label"}]},{"id":"_help_AlhDUqhENtH7","title":"Custom app-wide CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Custom app-wide CSS"},{"name":"iconClass","value":"bx bxs-file-css","type":"label"}]},{"id":"_help_g1mlRoU8CsqC","title":"Creating an icon pack","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Creating an icon pack"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_tC7s2alapj8V","title":"Advanced Usage","type":"book","attributes":[{"name":"iconClass","value":"bx bx-rocket","type":"label"}],"children":[{"id":"_help_zEY4DaJG4YT5","title":"Attributes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes"},{"name":"iconClass","value":"bx bx-list-check","type":"label"}],"children":[{"id":"_help_HI6GBBIduIgv","title":"Labels","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Labels"},{"name":"iconClass","value":"bx bx-hash","type":"label"}]},{"id":"_help_Cq5X6iKQop6R","title":"Relations","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Relations"},{"name":"iconClass","value":"bx bx-transfer","type":"label"}]},{"id":"_help_bwZpz2ajCEwO","title":"Attribute Inheritance","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Attribute Inheritance"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_OFXdgB2nNk1F","title":"Promoted Attributes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Promoted Attributes"},{"name":"iconClass","value":"bx bx-table","type":"label"}]}]},{"id":"_help_KC1HB96bqqHX","title":"Templates","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Templates"},{"name":"iconClass","value":"bx bx-copy","type":"label"}]},{"id":"_help_BCkXAVs63Ttv","title":"Note Map (Link map, Tree map)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Note Map (Link map, Tree map)"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_R9pX4DGra2Vt","title":"Sharing","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing"},{"name":"iconClass","value":"bx bx-share-alt","type":"label"}],"children":[{"id":"_help_Qjt68inQ2bRj","title":"Serving directly the content of a note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing/Serving directly the content o"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_ycBFjKrrwE9p","title":"Exporting static HTML for web publishing","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing/Exporting static HTML for web "},{"name":"iconClass","value":"bx bxs-file-html","type":"label"}]},{"id":"_help_sLIJ6f1dkJYW","title":"Reverse proxy configuration","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing/Reverse proxy configuration"},{"name":"iconClass","value":"bx bx-world","type":"label"}]}]},{"id":"_help_5668rwcirq1t","title":"Advanced Showcases","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases"},{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_l0tKav7yLHGF","title":"Day Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases/Day Notes"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]},{"id":"_help_R7abl2fc6Mxi","title":"Weight Tracker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases/Weight Tracker"},{"name":"iconClass","value":"bx bx-line-chart","type":"label"}]},{"id":"_help_xYjQUYhpbUEW","title":"Task Manager","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases/Task Manager"},{"name":"iconClass","value":"bx bx-calendar-check","type":"label"}]}]},{"id":"_help_J5Ex1ZrMbyJ6","title":"Custom Request Handler","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Custom Request Handler"},{"name":"iconClass","value":"bx bx-globe","type":"label"}]},{"id":"_help_d3fAXQ2diepH","title":"Custom Resource Providers","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Custom Resource Providers"},{"name":"iconClass","value":"bx bxs-file-plus","type":"label"}]},{"id":"_help_pgxEVkzLl1OP","title":"ETAPI (REST API)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/ETAPI (REST API)"},{"name":"iconClass","value":"bx bx-extension","type":"label"}],"children":[{"id":"_help_9qPsTWBorUhQ","title":"API Reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/rest-api/etapi/"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_47ZrP6FNuoG8","title":"Default Note Title","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Default Note Title"},{"name":"iconClass","value":"bx bx-edit-alt","type":"label"}]},{"id":"_help_wX4HbRucYSDD","title":"Database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database"},{"name":"iconClass","value":"bx bx-data","type":"label"}],"children":[{"id":"_help_oyIAJ9PvvwHX","title":"Manually altering the database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database/Manually altering the database"},{"name":"iconClass","value":"bx bxs-edit","type":"label"}],"children":[{"id":"_help_YKWqdJhzi2VY","title":"SQL Console","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database/Manually altering the database/SQL Console"},{"name":"iconClass","value":"bx bx-data","type":"label"}]}]},{"id":"_help_6tZeKvSHEUiB","title":"Demo Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database/Demo Notes"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_Gzjqa934BdH4","title":"Configuration (config.ini or environment variables)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Configuration (config.ini or e"},{"name":"iconClass","value":"bx bx-cog","type":"label"}],"children":[{"id":"_help_c5xB8m4g2IY6","title":"Trilium instance","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Configuration (config.ini or environment variables)/Trilium instance"},{"name":"iconClass","value":"bx bx-windows","type":"label"}]},{"id":"_help_LWtBjFej3wX3","title":"Cross-Origin Resource Sharing (CORS)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Configuration (config.ini or environment variables)/Cross-Origin Resource Sharing "},{"name":"iconClass","value":"bx bx-lock","type":"label"}]}]},{"id":"_help_ivYnonVFBxbQ","title":"Bulk Actions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Bulk Actions"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_4FahAwuGTAwC","title":"Note source","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Note source"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_1YeN2MzFUluU","title":"Technologies used","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used"},{"name":"iconClass","value":"bx bx-pyramid","type":"label"}],"children":[{"id":"_help_MI26XDLSAlCD","title":"CKEditor","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/CKEditor"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_N4IDkixaDG9C","title":"MindElixir","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/MindElixir"},{"name":"iconClass","value":"bx bx-sitemap","type":"label"}]},{"id":"_help_H0mM1lTxF9JI","title":"Excalidraw","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/Excalidraw"},{"name":"iconClass","value":"bx bx-pen","type":"label"}]},{"id":"_help_MQHyy2dIFgxS","title":"Leaflet","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/Leaflet"},{"name":"iconClass","value":"bx bx-map-alt","type":"label"}]}]},{"id":"_help_m1lbrzyKDaRB","title":"Note ID","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Note ID"},{"name":"iconClass","value":"bx bx-hash","type":"label"}]},{"id":"_help_0vTSyvhPTAOz","title":"Internal API","type":"book","attributes":[{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_z8O2VG4ZZJD7","title":"API Reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/rest-api/internal/"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_2mUhVmZK8RF3","title":"Hidden Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Hidden Notes"},{"name":"iconClass","value":"bx bx-hide","type":"label"}]},{"id":"_help_uYF7pmepw27K","title":"Metrics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Metrics"},{"name":"iconClass","value":"bx bxs-data","type":"label"}],"children":[{"id":"_help_bOP3TB56fL1V","title":"grafana-dashboard.json","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_64ZTlUPgEPtW","title":"Safe mode","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Safe mode"},{"name":"iconClass","value":"bx bxs-virus-block","type":"label"}]},{"id":"_help_HAIOFBoYIIdO","title":"Nightly release","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Nightly release"},{"name":"iconClass","value":"bx bx-moon","type":"label"}]},{"id":"_help_ZmT9ln8XJX2o","title":"Read-only database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Read-only database"},{"name":"iconClass","value":"bx bx-book-reader","type":"label"}]}]},{"id":"_help_GBBMSlVSOIGP","title":"AI","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI"},{"name":"iconClass","value":"bx bx-bot","type":"label"}],"children":[{"id":"_help_WkM7gsEUyCXs","title":"Providers","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers"},{"name":"iconClass","value":"bx bx-select-multiple","type":"label"}],"children":[{"id":"_help_7EdTxPADv95W","title":"Ollama","type":"book","attributes":[{"name":"iconClass","value":"bx bx-message-dots","type":"label"}],"children":[{"id":"_help_vvUCN7FDkq7G","title":"Installing Ollama","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers/Ollama/Installing Ollama"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_ZavFigBX9AwP","title":"OpenAI","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers/OpenAI"},{"name":"iconClass","value":"bx bx-message-dots","type":"label"}]},{"id":"_help_e0lkirXEiSNc","title":"Anthropic","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers/Anthropic"},{"name":"iconClass","value":"bx bx-message-dots","type":"label"}]}]}]},{"id":"_help_CdNpE2pqjmI6","title":"Scripting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting"},{"name":"iconClass","value":"bx bxs-file-js","type":"label"}],"children":[{"id":"_help_yIhgI5H7A2Sm","title":"Frontend Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics"},{"name":"iconClass","value":"bx bx-window","type":"label"}],"children":[{"id":"_help_MgibgPcfeuGz","title":"Custom Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}],"children":[{"id":"_help_SynTBQiBsdYJ","title":"Widget Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GhurYZjh8e1V","title":"Note context aware widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_M8IppdwVHSjG","title":"Right pane widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_YNxAqkI5Kg1M","title":"Word count widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Word count widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_VqGQnnPGnqAU","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gMkgcLJ6jBkg","title":"Troubleshooting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Troubleshooting"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_es8OU2GuguFU","title":"Examples","type":"book","attributes":[{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_TjLYAo3JMO8X","title":"\"New Task\" launcher button","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/New Task launcher button"},{"name":"iconClass","value":"bx bx-task","type":"label"}]},{"id":"_help_7kZPMD0uFwkH","title":"Downloading responses from Google Forms","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Downloading responses from Goo"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_DL92EjAaXT26","title":"Using promoted attributes to configure scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Using promoted attributes to c"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_4Gn3psZKsfSm","title":"Launch Bar Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets"},{"name":"iconClass","value":"bx bx-dock-left","type":"label"}],"children":[{"id":"_help_IPArqVfDQ4We","title":"Note Title Widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gcI7RPbaNSh3","title":"Analog Watch","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Analog Watch"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_KLsqhjaqh1QW","title":"Preact","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact"},{"name":"iconClass","value":"bx bxl-react","type":"label"}],"children":[{"id":"_help_Bqde6BvPo05g","title":"Component libraries","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries"},{"name":"iconClass","value":"bx bxs-component","type":"label"}]},{"id":"_help_ykYtbM9k3a7B","title":"Hooks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_Sg9GrCtyftZf","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS"},{"name":"iconClass","value":"bx bxs-file-css","type":"label"}]},{"id":"_help_RSssb9S3xgSr","title":"Built-in components","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components"},{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_i9B4IW7b6V6z","title":"Widget showcase","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}]}]},{"id":"_help_SPirpZypehBG","title":"Backend scripts","type":"book","attributes":[{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_fZ2IGYFXjkEy","title":"Server-side imports","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Server-side imports"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GPERMystNGTB","title":"Events","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Events"},{"name":"iconClass","value":"bx bx-rss","type":"label"}]}]},{"id":"_help_wqXwKJl6VpNk","title":"Common concepts","type":"book","attributes":[{"name":"iconClass","value":"bx bxl-nodejs","type":"label"}],"children":[{"id":"_help_hA834UaHhSNn","title":"Script bundles","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Common concepts/Script bundles"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_GLks18SNjxmC","title":"Script API","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API"},{"name":"iconClass","value":"bx bx-code-curly","type":"label"}],"children":[{"id":"_help_Q2z6av6JZVWm","title":"Frontend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend"},{"name":"iconClass","value":"bx bx-folder","type":"label"}],"enforceAttributes":true,"children":[{"id":"_help_habiZ3HU8Kw8","title":"FNote","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend/interfaces/FNote.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_MEtfsqa5VwNi","title":"Backend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/backend"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true},{"id":"_help_ApVHZ8JY5ofC","title":"Day.js","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API/Day.js"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]}]},{"id":"_help_vElnKeDNPSVl","title":"Logging","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Logging"},{"name":"iconClass","value":"bx bx-terminal","type":"label"}]},{"id":"_help_cNpC0ITcfX0N","title":"Breaking changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]}]},{"id":"_help_Fm0j45KqyHpU","title":"Miscellaneous","type":"book","attributes":[{"name":"iconClass","value":"bx bx-info-circle","type":"label"}],"children":[{"id":"_help_WFbFXrgnDyyU","title":"Privacy Policy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Privacy Policy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_NcsmUYZRWEW4","title":"Patterns of personal knowledge","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Patterns of personal knowledge"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}]
\ No newline at end of file
+[{"id":"_help_BOCnjTMBCoxW","title":"Feature Highlights","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Feature Highlights"},{"name":"iconClass","value":"bx bx-star","type":"label"}]},{"id":"_help_Otzi9La2YAUX","title":"Installation & Setup","type":"book","attributes":[{"name":"iconClass","value":"bx bx-cog","type":"label"}],"children":[{"id":"_help_poXkQfguuA0U","title":"Desktop Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation"},{"name":"iconClass","value":"bx bx-desktop","type":"label"}],"children":[{"id":"_help_nRqcgfTb97uV","title":"Using the desktop application as a server","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Using the desktop application "},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_Rp0q8bSP6Ayl","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Desktop Installation/Nix flake"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]}]},{"id":"_help_WOcw2SLH6tbX","title":"Server Installation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_Dgg7bR3b6K9j","title":"1. Installing the server","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_3tW6mORuTHnB","title":"Packaged version for Linux","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Packaged version for Linux"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_rWX5eY045zbE","title":"Using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Docker"},{"name":"iconClass","value":"bx bxl-docker","type":"label"}]},{"id":"_help_moVgBcoxE3EK","title":"On NixOS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/On NixOS"},{"name":"iconClass","value":"bx bxl-tux","type":"label"}]},{"id":"_help_J1Bb6lVlwU5T","title":"Manually","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Manually"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]},{"id":"_help_DCmT6e7clMoP","title":"Using Kubernetes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Using Kubernetes"},{"name":"iconClass","value":"bx bxl-kubernetes","type":"label"}]},{"id":"_help_klCWNks3ReaQ","title":"Multiple server instances","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/1. Installing the server/Multiple server instances"},{"name":"iconClass","value":"bx bxs-user-account","type":"label"}]}]},{"id":"_help_vcjrb3VVYPZI","title":"2. Reverse proxy","type":"book","attributes":[{"name":"iconClass","value":"bx bx-folder","type":"label"}],"children":[{"id":"_help_ud6MShXL4WpO","title":"Nginx","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Nginx"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_fDLvzOx29Pfg","title":"Apache using Docker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Apache using Docker"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_LLzSMXACKhUs","title":"Trusted proxy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Trusted proxy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_5ERVJb9s4FRD","title":"Traefik","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/2. Reverse proxy/Traefik"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_l2VkvOwUNfZj","title":"HTTPS (TLS)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/HTTPS (TLS)"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_0hzsNCP31IAB","title":"Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Authentication"},{"name":"iconClass","value":"bx bx-user","type":"label"}]},{"id":"_help_7DAiwaf8Z7Rz","title":"Multi-Factor Authentication","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Multi-Factor Authentication"},{"name":"iconClass","value":"bx bx-stopwatch","type":"label"}]},{"id":"_help_Un4wj2Mak2Ky","title":"Nix flake","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Nix flake.clone"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_yeEaYqosGLSh","title":"Third-party cloud hosting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/Third-party cloud hosting"},{"name":"iconClass","value":"bx bx-cloud","type":"label"}]},{"id":"_help_iGTnKjubbXkA","title":"System Requirements","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Server Installation/System Requirements"},{"name":"iconClass","value":"bx bx-chip","type":"label"}]}]},{"id":"_help_cbkrhQjrkKrh","title":"Synchronization","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Synchronization"},{"name":"iconClass","value":"bx bx-sync","type":"label"}]},{"id":"_help_RDslemsQ6gCp","title":"Mobile Frontend","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Mobile Frontend"},{"name":"iconClass","value":"bx bx-mobile-alt","type":"label"}]},{"id":"_help_MtPxeAWVAzMg","title":"Web Clipper","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Web Clipper"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_n1lujUxCwipy","title":"Upgrading TriliumNext","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Upgrading TriliumNext"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]},{"id":"_help_ODY7qQn5m2FT","title":"Backup","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Backup"},{"name":"iconClass","value":"bx bx-hdd","type":"label"}]},{"id":"_help_tAassRL4RSQL","title":"Data directory","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Installation & Setup/Data directory"},{"name":"iconClass","value":"bx bx-folder-open","type":"label"}]}]},{"id":"_help_gh7bpGYxajRS","title":"Basic Concepts and Features","type":"book","attributes":[{"name":"iconClass","value":"bx bx-help-circle","type":"label"}],"children":[{"id":"_help_Vc8PjrjAGuOp","title":"UI Elements","type":"book","attributes":[{"name":"iconClass","value":"bx bx-window-alt","type":"label"}],"children":[{"id":"_help_x0JgW8UqGXvq","title":"Vertical and horizontal layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Vertical and horizontal layout"},{"name":"iconClass","value":"bx bxs-layout","type":"label"}]},{"id":"_help_x3i7MxGccDuM","title":"Global menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Global menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_oPVyFC7WL2Lp","title":"Note Tree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree"},{"name":"iconClass","value":"bx bxs-tree-alt","type":"label"}],"children":[{"id":"_help_YtSN43OrfzaA","title":"Note tree contextual menu","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Note tree contextual menu"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_yTjUdsOi4CIE","title":"Multiple selection","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Multiple selection"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_DvdZhoQZY9Yd","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_wyaGBBQrl4i3","title":"Hiding the subtree","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree"},{"name":"iconClass","value":"bx bx-hide","type":"label"}]}]},{"id":"_help_BlN9DFI679QC","title":"Ribbon","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Ribbon"},{"name":"iconClass","value":"bx bx-dots-horizontal","type":"label"}]},{"id":"_help_3seOhtN8uLIY","title":"Tabs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs"},{"name":"iconClass","value":"bx bx-dock-top","type":"label"}]},{"id":"_help_xYmIYSP6wE3F","title":"Launch Bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Launch Bar"},{"name":"iconClass","value":"bx bx-sidebar","type":"label"}]},{"id":"_help_8YBEPzcpUgxw","title":"Note buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note buttons"},{"name":"iconClass","value":"bx bx-dots-vertical-rounded","type":"label"}]},{"id":"_help_4TIF1oA4VQRO","title":"Options","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Options"},{"name":"iconClass","value":"bx bx-cog","type":"label"}]},{"id":"_help_luNhaphA37EO","title":"Split View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Split View"},{"name":"iconClass","value":"bx bx-dock-right","type":"label"}]},{"id":"_help_XpOYSgsLkTJy","title":"Floating buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Floating buttons"},{"name":"iconClass","value":"bx bx-rectangle","type":"label"}]},{"id":"_help_RnaPdbciOfeq","title":"Right Sidebar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Right Sidebar"},{"name":"iconClass","value":"bx bxs-dock-right","type":"label"}]},{"id":"_help_r5JGHN99bVKn","title":"Recent Changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Recent Changes"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_ny318J39E5Z0","title":"Zoom","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Zoom"},{"name":"iconClass","value":"bx bx-zoom-in","type":"label"}]},{"id":"_help_lgKX7r3aL30x","title":"Note Tooltip","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tooltip"},{"name":"iconClass","value":"bx bx-message-detail","type":"label"}]},{"id":"_help_IjZS7iK5EXtb","title":"New Layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout"},{"name":"iconClass","value":"bx bx-layout","type":"label"}],"children":[{"id":"_help_I6p2a06hdnL6","title":"Breadcrumb","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Breadcrumb"},{"name":"iconClass","value":"bx bx-chevron-right","type":"label"}]},{"id":"_help_AlJ73vBCjWDw","title":"Status bar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/UI Elements/New Layout/Status bar"},{"name":"iconClass","value":"bx bx-dock-bottom","type":"label"}]}]}]},{"id":"_help_BFs8mudNFgCS","title":"Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes"},{"name":"iconClass","value":"bx bx-notepad","type":"label"}],"children":[{"id":"_help_p9kXRFAkwN4o","title":"Note Icons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Icons"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_0vhv7lsOLy82","title":"Attachments","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Attachments"},{"name":"iconClass","value":"bx bx-paperclip","type":"label"}]},{"id":"_help_IakOLONlIfGI","title":"Cloning Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes"},{"name":"iconClass","value":"bx bx-duplicate","type":"label"}],"children":[{"id":"_help_TBwsyfadTA18","title":"Branch prefix","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Cloning Notes/Branch prefix"},{"name":"iconClass","value":"bx bx-rename","type":"label"}]}]},{"id":"_help_bwg0e8ewQMak","title":"Protected Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Protected Notes"},{"name":"iconClass","value":"bx bx-lock-alt","type":"label"}]},{"id":"_help_MKmLg5x6xkor","title":"Archived Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Archived Notes"},{"name":"iconClass","value":"bx bx-box","type":"label"}]},{"id":"_help_vZWERwf8U3nx","title":"Note Revisions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note Revisions"},{"name":"iconClass","value":"bx bx-history","type":"label"}]},{"id":"_help_aGlEvb9hyDhS","title":"Sorting Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Sorting Notes"},{"name":"iconClass","value":"bx bx-sort-up","type":"label"}]},{"id":"_help_NRnIZmSMc5sj","title":"Printing & Exporting as PDF","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Printing & Exporting as PDF"},{"name":"iconClass","value":"bx bx-printer","type":"label"}]},{"id":"_help_CoFPLs3dRlXc","title":"Read-Only Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Read-Only Notes"},{"name":"iconClass","value":"bx bx-edit-alt","type":"label"}]},{"id":"_help_0ESUbbAxVnoK","title":"Note List","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Notes/Note List"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]}]},{"id":"_help_wArbEsdSae6g","title":"Navigation","type":"book","attributes":[{"name":"iconClass","value":"bx bx-navigation","type":"label"}],"children":[{"id":"_help_kBrnXNG3Hplm","title":"Tree Concepts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Tree Concepts"},{"name":"iconClass","value":"bx bx-pyramid","type":"label"}]},{"id":"_help_MMiBEQljMQh2","title":"Note Navigation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation"},{"name":"iconClass","value":"bx bxs-navigation","type":"label"}]},{"id":"_help_Ms1nauBra7gq","title":"Quick search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_F1r9QtzQLZqm","title":"Jump to...","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Jump to"},{"name":"iconClass","value":"bx bx-send","type":"label"}]},{"id":"_help_eIg8jdvaoNNd","title":"Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]},{"id":"_help_u3YFHC9tQlpm","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmarks","type":"label"}]},{"id":"_help_OR8WJ7Iz9K4U","title":"Note Hoisting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Note Hoisting"},{"name":"iconClass","value":"bx bxs-chevrons-up","type":"label"}]},{"id":"_help_ZjLYv08Rp3qC","title":"Quick edit","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Quick edit"},{"name":"iconClass","value":"bx bx-edit","type":"label"}]},{"id":"_help_9sRHySam5fXb","title":"Workspaces","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Workspaces"},{"name":"iconClass","value":"bx bx-door-open","type":"label"}]},{"id":"_help_xWtq5NUHOwql","title":"Similar Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Similar Notes"},{"name":"iconClass","value":"bx bx-bar-chart","type":"label"}]},{"id":"_help_McngOG2jbUWX","title":"Search in note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Navigation/Search in note"},{"name":"iconClass","value":"bx bx-search-alt-2","type":"label"}]}]},{"id":"_help_A9Oc6YKKc65v","title":"Keyboard Shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Keyboard Shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_Wy267RK4M69c","title":"Themes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes"},{"name":"iconClass","value":"bx bx-palette","type":"label"}],"children":[{"id":"_help_VbjZvtUek0Ln","title":"Theme Gallery","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Theme Gallery"},{"name":"iconClass","value":"bx bx-book-reader","type":"label"}]},{"id":"_help_gOKqSJgXLcIj","title":"Icon Packs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_mHbBMPDPkVV5","title":"Import & Export","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export"},{"name":"iconClass","value":"bx bx-import","type":"label"}],"children":[{"id":"_help_Oau6X9rCuegd","title":"Markdown","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}],"children":[{"id":"_help_rJ9grSgoExl9","title":"Supported syntax","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Markdown/Supported syntax"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}]}]},{"id":"_help_syuSEKf2rUGr","title":"Evernote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/Evernote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}],"children":[{"id":"_help_dj3j8dG4th4l","title":"Process internal links by title","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_GnhlmrATVqcH","title":"OneNote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Import & Export/OneNote"},{"name":"iconClass","value":"bx bx-window-open","type":"label"}]}]},{"id":"_help_rC3pL2aptaRE","title":"Zen mode","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Zen mode"},{"name":"iconClass","value":"bx bxs-yin-yang","type":"label"}]},{"id":"_help_YzMcWlCVeW09","title":"Active content","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Basic Concepts and Features/Active content"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}]}]},{"id":"_help_s3YCWHBfmYuM","title":"Quick Start","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Quick Start"},{"name":"iconClass","value":"bx bx-run","type":"label"}]},{"id":"_help_i6dbnitykE5D","title":"FAQ","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/FAQ"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_KSZ04uQ2D1St","title":"Note Types","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types"},{"name":"iconClass","value":"bx bx-edit","type":"label"}],"children":[{"id":"_help_iPIMuisry3hd","title":"Text","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text"},{"name":"iconClass","value":"bx bx-note","type":"label"}],"children":[{"id":"_help_NwBbFdNZ9h7O","title":"Block quotes & admonitions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Block quotes & admonitions"},{"name":"iconClass","value":"bx bx-info-circle","type":"label"}]},{"id":"_help_oSuaNgyyKnhu","title":"Bookmarks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Bookmarks"},{"name":"iconClass","value":"bx bx-bookmark","type":"label"}]},{"id":"_help_veGu4faJErEM","title":"Content language & Right-to-left support","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Content language & Right-to-le"},{"name":"iconClass","value":"bx bx-align-right","type":"label"}]},{"id":"_help_2x0ZAX9ePtzV","title":"Cut to subnote","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Cut to subnote"},{"name":"iconClass","value":"bx bx-cut","type":"label"}]},{"id":"_help_UYuUB1ZekNQU","title":"Developer-specific formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting"},{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_QxEyIjRBizuC","title":"Code blocks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Developer-specific formatting/Code blocks"},{"name":"iconClass","value":"bx bx-code","type":"label"}]}]},{"id":"_help_AgjCISero73a","title":"Footnotes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Footnotes"},{"name":"iconClass","value":"bx bx-bracket","type":"label"}]},{"id":"_help_nRhnJkTT8cPs","title":"Formatting toolbar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Formatting toolbar"},{"name":"iconClass","value":"bx bx-text","type":"label"}]},{"id":"_help_Gr6xFaF6ioJ5","title":"General formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/General formatting"},{"name":"iconClass","value":"bx bx-bold","type":"label"}]},{"id":"_help_AxshuNRegLAv","title":"Highlights list","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Highlights list"},{"name":"iconClass","value":"bx bx-highlight","type":"label"}]},{"id":"_help_mT0HEkOsz6i1","title":"Images","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images"},{"name":"iconClass","value":"bx bx-image-alt","type":"label"}],"children":[{"id":"_help_0Ofbk1aSuVRu","title":"Image references","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Images/Image references"},{"name":"iconClass","value":"bx bxs-file-image","type":"label"}]}]},{"id":"_help_nBAXQFj20hS1","title":"Include Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Include Note"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_CohkqWQC1iBv","title":"Insert buttons","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Insert buttons"},{"name":"iconClass","value":"bx bx-plus","type":"label"}]},{"id":"_help_oiVPnW8QfnvS","title":"Keyboard shortcuts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Keyboard shortcuts"},{"name":"iconClass","value":"bx bxs-keyboard","type":"label"}]},{"id":"_help_QEAPj01N5f7w","title":"Links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links"},{"name":"iconClass","value":"bx bx-link-alt","type":"label"}],"children":[{"id":"_help_3IDVtesTQ8ds","title":"External links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/External links"},{"name":"iconClass","value":"bx bx-link-external","type":"label"}]},{"id":"_help_hrZ1D00cLbal","title":"Internal (reference) links","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Links/Internal (reference) links"},{"name":"iconClass","value":"bx bx-link","type":"label"}]}]},{"id":"_help_S6Xx8QIWTV66","title":"Lists","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Lists"},{"name":"iconClass","value":"bx bx-list-ul","type":"label"}]},{"id":"_help_QrtTYPmdd1qq","title":"Markdown-like formatting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Markdown-like formatting"},{"name":"iconClass","value":"bx bxl-markdown","type":"label"}]},{"id":"_help_YfYAtQBcfo5V","title":"Math Equations","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Math Equations"},{"name":"iconClass","value":"bx bx-math","type":"label"}]},{"id":"_help_dEHYtoWWi8ct","title":"Other features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Other features"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_gLt3vA97tMcp","title":"Premium features","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features"},{"name":"iconClass","value":"bx bx-star","type":"label"}],"children":[{"id":"_help_ZlN4nump6EbW","title":"Slash Commands","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Slash Commands"},{"name":"iconClass","value":"bx bx-menu","type":"label"}]},{"id":"_help_pwc194wlRzcH","title":"Text Snippets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Text Snippets"},{"name":"iconClass","value":"bx bx-align-left","type":"label"}]},{"id":"_help_5wZallV2Qo1t","title":"Format Painter","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Premium features/Format Painter"},{"name":"iconClass","value":"bx bxs-paint-roll","type":"label"}]}]},{"id":"_help_BFvAtE74rbP6","title":"Table of contents","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Table of contents"},{"name":"iconClass","value":"bx bx-heading","type":"label"}]},{"id":"_help_NdowYOC1GFKS","title":"Tables","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Text/Tables"},{"name":"iconClass","value":"bx bx-table","type":"label"}]}]},{"id":"_help_6f9hih2hXXZk","title":"Code","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Code"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_m523cpzocqaD","title":"Saved Search","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Saved Search"},{"name":"iconClass","value":"bx bx-file-find","type":"label"}]},{"id":"_help_iRwzGnHPzonm","title":"Relation Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Relation Map"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_bdUJEHsAPYQR","title":"Note Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Note Map"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_HcABDtFCkbFN","title":"Render Note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Render Note"},{"name":"iconClass","value":"bx bx-extension","type":"label"}]},{"id":"_help_s1aBHPd79XYj","title":"Mermaid Diagrams","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mermaid Diagrams"},{"name":"iconClass","value":"bx bx-selection","type":"label"}],"children":[{"id":"_help_RH6yLjjWJHof","title":"ELK layout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mermaid Diagrams/ELK layout"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_WWgeUaBb7UfC","title":"Syntax reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://mermaid.js.org/intro/syntax-reference.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_grjYqerjn243","title":"Canvas","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Canvas"},{"name":"iconClass","value":"bx bx-pen","type":"label"}]},{"id":"_help_1vHRoWCEjj0L","title":"Web View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Web View"},{"name":"iconClass","value":"bx bx-globe-alt","type":"label"}]},{"id":"_help_gBbsAeiuUxI5","title":"Mind Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/Mind Map"},{"name":"iconClass","value":"bx bx-sitemap","type":"label"}]},{"id":"_help_W8vYD3Q1zjCR","title":"File","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File"},{"name":"iconClass","value":"bx bx-file-blank","type":"label"}],"children":[{"id":"_help_XJGJrpu7F9sh","title":"PDFs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Note Types/File/PDFs"},{"name":"iconClass","value":"bx bxs-file-pdf","type":"label"}]}]}]},{"id":"_help_GTwFsgaA0lCt","title":"Collections","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections"},{"name":"iconClass","value":"bx bx-book","type":"label"}],"children":[{"id":"_help_xWbu3jpNWapp","title":"Calendar","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Calendar"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]},{"id":"_help_2FvYrpmOXm29","title":"Table","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Table"},{"name":"iconClass","value":"bx bx-table","type":"label"}]},{"id":"_help_CtBQqbwXDx1w","title":"Kanban Board","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Kanban Board"},{"name":"iconClass","value":"bx bx-columns","type":"label"}]},{"id":"_help_81SGnPGMk7Xc","title":"Geo Map","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Geo Map"},{"name":"iconClass","value":"bx bx-map-alt","type":"label"}]},{"id":"_help_zP3PMqaG71Ct","title":"Presentation","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Presentation"},{"name":"iconClass","value":"bx bx-slideshow","type":"label"}]},{"id":"_help_8QqnMzx393bx","title":"Grid View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/Grid View"},{"name":"iconClass","value":"bx bxs-grid","type":"label"}]},{"id":"_help_mULW0Q3VojwY","title":"List View","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Collections/List View"},{"name":"iconClass","value":"bx bx-list-ul","type":"label"}]}]},{"id":"_help_BgmBlOIl72jZ","title":"Troubleshooting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting"},{"name":"iconClass","value":"bx bx-bug","type":"label"}],"children":[{"id":"_help_wy8So3yZZlH9","title":"Reporting issues","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Reporting issues"},{"name":"iconClass","value":"bx bx-bug-alt","type":"label"}]},{"id":"_help_x59R8J8KV5Bp","title":"Anonymized Database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Anonymized Database"},{"name":"iconClass","value":"bx bx-low-vision","type":"label"}]},{"id":"_help_qzNzp9LYQyPT","title":"Error logs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Error logs"},{"name":"iconClass","value":"bx bx-comment-error","type":"label"}],"children":[{"id":"_help_bnyigUA2UK7s","title":"Backend (server) logs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Error logs/Backend (server) logs"},{"name":"iconClass","value":"bx bx-server","type":"label"}]},{"id":"_help_9yEHzMyFirZR","title":"Frontend logs","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Error logs/Frontend logs"},{"name":"iconClass","value":"bx bx-window-alt","type":"label"}]}]},{"id":"_help_vdlYGAcpXAgc","title":"Synchronization fails with 504 Gateway Timeout","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Synchronization fails with 504"},{"name":"iconClass","value":"bx bx-error","type":"label"}]},{"id":"_help_s8alTXmpFR61","title":"Refreshing the application","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Troubleshooting/Refreshing the application"},{"name":"iconClass","value":"bx bx-refresh","type":"label"}]}]},{"id":"_help_pKK96zzmvBGf","title":"Theme development","type":"book","attributes":[{"name":"iconClass","value":"bx bx-palette","type":"label"}],"children":[{"id":"_help_7NfNr5pZpVKV","title":"Creating a custom theme","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Creating a custom theme"},{"name":"iconClass","value":"bx bxs-color","type":"label"}]},{"id":"_help_WFGzWeUK6arS","title":"Customize the Next theme","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Customize the Next theme"},{"name":"iconClass","value":"bx bx-news","type":"label"}]},{"id":"_help_WN5z4M8ASACJ","title":"Reference","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Reference"},{"name":"iconClass","value":"bx bx-book-open","type":"label"}]},{"id":"_help_AlhDUqhENtH7","title":"Custom app-wide CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Custom app-wide CSS"},{"name":"iconClass","value":"bx bxs-file-css","type":"label"}]},{"id":"_help_g1mlRoU8CsqC","title":"Creating an icon pack","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Theme development/Creating an icon pack"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_tC7s2alapj8V","title":"Advanced Usage","type":"book","attributes":[{"name":"iconClass","value":"bx bx-rocket","type":"label"}],"children":[{"id":"_help_zEY4DaJG4YT5","title":"Attributes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes"},{"name":"iconClass","value":"bx bx-list-check","type":"label"}],"children":[{"id":"_help_HI6GBBIduIgv","title":"Labels","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Labels"},{"name":"iconClass","value":"bx bx-hash","type":"label"}]},{"id":"_help_Cq5X6iKQop6R","title":"Relations","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Relations"},{"name":"iconClass","value":"bx bx-transfer","type":"label"}]},{"id":"_help_bwZpz2ajCEwO","title":"Attribute Inheritance","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Attribute Inheritance"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_OFXdgB2nNk1F","title":"Promoted Attributes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Attributes/Promoted Attributes"},{"name":"iconClass","value":"bx bx-table","type":"label"}]}]},{"id":"_help_KC1HB96bqqHX","title":"Templates","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Templates"},{"name":"iconClass","value":"bx bx-copy","type":"label"}]},{"id":"_help_BCkXAVs63Ttv","title":"Note Map (Link map, Tree map)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Note Map (Link map, Tree map)"},{"name":"iconClass","value":"bx bxs-network-chart","type":"label"}]},{"id":"_help_R9pX4DGra2Vt","title":"Sharing","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing"},{"name":"iconClass","value":"bx bx-share-alt","type":"label"}],"children":[{"id":"_help_Qjt68inQ2bRj","title":"Serving directly the content of a note","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing/Serving directly the content o"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_ycBFjKrrwE9p","title":"Exporting static HTML for web publishing","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing/Exporting static HTML for web "},{"name":"iconClass","value":"bx bxs-file-html","type":"label"}]},{"id":"_help_sLIJ6f1dkJYW","title":"Reverse proxy configuration","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Sharing/Reverse proxy configuration"},{"name":"iconClass","value":"bx bx-world","type":"label"}]}]},{"id":"_help_5668rwcirq1t","title":"Advanced Showcases","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases"},{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_l0tKav7yLHGF","title":"Day Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases/Day Notes"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]},{"id":"_help_R7abl2fc6Mxi","title":"Weight Tracker","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases/Weight Tracker"},{"name":"iconClass","value":"bx bx-line-chart","type":"label"}]},{"id":"_help_xYjQUYhpbUEW","title":"Task Manager","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Advanced Showcases/Task Manager"},{"name":"iconClass","value":"bx bx-calendar-check","type":"label"}]}]},{"id":"_help_J5Ex1ZrMbyJ6","title":"Custom Request Handler","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Custom Request Handler"},{"name":"iconClass","value":"bx bx-globe","type":"label"}]},{"id":"_help_d3fAXQ2diepH","title":"Custom Resource Providers","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Custom Resource Providers"},{"name":"iconClass","value":"bx bxs-file-plus","type":"label"}]},{"id":"_help_pgxEVkzLl1OP","title":"ETAPI (REST API)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/ETAPI (REST API)"},{"name":"iconClass","value":"bx bx-extension","type":"label"}],"children":[{"id":"_help_9qPsTWBorUhQ","title":"API Reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/rest-api/etapi/"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_47ZrP6FNuoG8","title":"Default Note Title","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Default Note Title"},{"name":"iconClass","value":"bx bx-edit-alt","type":"label"}]},{"id":"_help_wX4HbRucYSDD","title":"Database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database"},{"name":"iconClass","value":"bx bx-data","type":"label"}],"children":[{"id":"_help_oyIAJ9PvvwHX","title":"Manually altering the database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database/Manually altering the database"},{"name":"iconClass","value":"bx bxs-edit","type":"label"}],"children":[{"id":"_help_YKWqdJhzi2VY","title":"SQL Console","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database/Manually altering the database/SQL Console"},{"name":"iconClass","value":"bx bx-data","type":"label"}]}]},{"id":"_help_6tZeKvSHEUiB","title":"Demo Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Database/Demo Notes"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_Gzjqa934BdH4","title":"Configuration (config.ini or environment variables)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Configuration (config.ini or e"},{"name":"iconClass","value":"bx bx-cog","type":"label"}],"children":[{"id":"_help_c5xB8m4g2IY6","title":"Trilium instance","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Configuration (config.ini or environment variables)/Trilium instance"},{"name":"iconClass","value":"bx bx-windows","type":"label"}]},{"id":"_help_LWtBjFej3wX3","title":"Cross-Origin Resource Sharing (CORS)","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Configuration (config.ini or environment variables)/Cross-Origin Resource Sharing "},{"name":"iconClass","value":"bx bx-lock","type":"label"}]}]},{"id":"_help_ivYnonVFBxbQ","title":"Bulk Actions","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Bulk Actions"},{"name":"iconClass","value":"bx bx-list-plus","type":"label"}]},{"id":"_help_4FahAwuGTAwC","title":"Note source","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Note source"},{"name":"iconClass","value":"bx bx-code","type":"label"}]},{"id":"_help_1YeN2MzFUluU","title":"Technologies used","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used"},{"name":"iconClass","value":"bx bx-pyramid","type":"label"}],"children":[{"id":"_help_MI26XDLSAlCD","title":"CKEditor","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/CKEditor"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_N4IDkixaDG9C","title":"MindElixir","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/MindElixir"},{"name":"iconClass","value":"bx bx-sitemap","type":"label"}]},{"id":"_help_H0mM1lTxF9JI","title":"Excalidraw","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/Excalidraw"},{"name":"iconClass","value":"bx bx-pen","type":"label"}]},{"id":"_help_MQHyy2dIFgxS","title":"Leaflet","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Technologies used/Leaflet"},{"name":"iconClass","value":"bx bx-map-alt","type":"label"}]}]},{"id":"_help_m1lbrzyKDaRB","title":"Note ID","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Note ID"},{"name":"iconClass","value":"bx bx-hash","type":"label"}]},{"id":"_help_0vTSyvhPTAOz","title":"Internal API","type":"book","attributes":[{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_z8O2VG4ZZJD7","title":"API Reference","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/rest-api/internal/"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_2mUhVmZK8RF3","title":"Hidden Notes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Hidden Notes"},{"name":"iconClass","value":"bx bx-hide","type":"label"}]},{"id":"_help_uYF7pmepw27K","title":"Metrics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Metrics"},{"name":"iconClass","value":"bx bxs-data","type":"label"}],"children":[{"id":"_help_bOP3TB56fL1V","title":"grafana-dashboard.json","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_64ZTlUPgEPtW","title":"Safe mode","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Safe mode"},{"name":"iconClass","value":"bx bxs-virus-block","type":"label"}]},{"id":"_help_HAIOFBoYIIdO","title":"Nightly release","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Nightly release"},{"name":"iconClass","value":"bx bx-moon","type":"label"}]},{"id":"_help_ZmT9ln8XJX2o","title":"Read-only database","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Advanced Usage/Read-only database"},{"name":"iconClass","value":"bx bx-book-reader","type":"label"}]}]},{"id":"_help_GBBMSlVSOIGP","title":"AI","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI"},{"name":"iconClass","value":"bx bx-bot","type":"label"}],"children":[{"id":"_help_WkM7gsEUyCXs","title":"Providers","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers"},{"name":"iconClass","value":"bx bx-select-multiple","type":"label"}],"children":[{"id":"_help_7EdTxPADv95W","title":"Ollama","type":"book","attributes":[{"name":"iconClass","value":"bx bx-message-dots","type":"label"}],"children":[{"id":"_help_vvUCN7FDkq7G","title":"Installing Ollama","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers/Ollama/Installing Ollama"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_ZavFigBX9AwP","title":"OpenAI","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers/OpenAI"},{"name":"iconClass","value":"bx bx-message-dots","type":"label"}]},{"id":"_help_e0lkirXEiSNc","title":"Anthropic","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/AI/Providers/Anthropic"},{"name":"iconClass","value":"bx bx-message-dots","type":"label"}]}]}]},{"id":"_help_CdNpE2pqjmI6","title":"Scripting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting"},{"name":"iconClass","value":"bx bxs-file-js","type":"label"}],"children":[{"id":"_help_yIhgI5H7A2Sm","title":"Frontend Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics"},{"name":"iconClass","value":"bx bx-window","type":"label"}],"children":[{"id":"_help_MgibgPcfeuGz","title":"Custom Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets"},{"name":"iconClass","value":"bx bxs-widget","type":"label"}],"children":[{"id":"_help_SynTBQiBsdYJ","title":"Widget Basics","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GhurYZjh8e1V","title":"Note context aware widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_M8IppdwVHSjG","title":"Right pane widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_YNxAqkI5Kg1M","title":"Word count widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Word count widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_VqGQnnPGnqAU","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gMkgcLJ6jBkg","title":"Troubleshooting","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Troubleshooting"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_es8OU2GuguFU","title":"Examples","type":"book","attributes":[{"name":"iconClass","value":"bx bx-code-alt","type":"label"}],"children":[{"id":"_help_TjLYAo3JMO8X","title":"\"New Task\" launcher button","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/New Task launcher button"},{"name":"iconClass","value":"bx bx-task","type":"label"}]},{"id":"_help_7kZPMD0uFwkH","title":"Downloading responses from Google Forms","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Downloading responses from Goo"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_DL92EjAaXT26","title":"Using promoted attributes to configure scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Examples/Using promoted attributes to c"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_4Gn3psZKsfSm","title":"Launch Bar Widgets","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets"},{"name":"iconClass","value":"bx bx-dock-left","type":"label"}],"children":[{"id":"_help_IPArqVfDQ4We","title":"Note Title Widget","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_gcI7RPbaNSh3","title":"Analog Watch","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Analog Watch"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]},{"id":"_help_KLsqhjaqh1QW","title":"Preact","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact"},{"name":"iconClass","value":"bx bxl-react","type":"label"}],"children":[{"id":"_help_Bqde6BvPo05g","title":"Component libraries","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries"},{"name":"iconClass","value":"bx bxs-component","type":"label"}]},{"id":"_help_ykYtbM9k3a7B","title":"Hooks","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks"},{"name":"iconClass","value":"bx bx-question-mark","type":"label"}]},{"id":"_help_Sg9GrCtyftZf","title":"CSS","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS"},{"name":"iconClass","value":"bx bxs-file-css","type":"label"}]},{"id":"_help_RSssb9S3xgSr","title":"Built-in components","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components"},{"name":"iconClass","value":"bx bxs-component","type":"label"}],"children":[{"id":"_help_i9B4IW7b6V6z","title":"Widget showcase","type":"doc","attributes":[{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}]}]},{"id":"_help_SPirpZypehBG","title":"Backend scripts","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts"},{"name":"iconClass","value":"bx bx-server","type":"label"}],"children":[{"id":"_help_fZ2IGYFXjkEy","title":"Server-side imports","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Server-side imports"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_GPERMystNGTB","title":"Events","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Backend scripts/Events"},{"name":"iconClass","value":"bx bx-rss","type":"label"}]}]},{"id":"_help_wqXwKJl6VpNk","title":"Common concepts","type":"book","attributes":[{"name":"iconClass","value":"bx bxl-nodejs","type":"label"}],"children":[{"id":"_help_hA834UaHhSNn","title":"Script bundles","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Common concepts/Script bundles"},{"name":"iconClass","value":"bx bx-package","type":"label"}]}]},{"id":"_help_GLks18SNjxmC","title":"Script API","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API"},{"name":"iconClass","value":"bx bx-code-curly","type":"label"}],"children":[{"id":"_help_Q2z6av6JZVWm","title":"Frontend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend"},{"name":"iconClass","value":"bx bx-folder","type":"label"}],"enforceAttributes":true,"children":[{"id":"_help_habiZ3HU8Kw8","title":"FNote","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/frontend/interfaces/FNote.html"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true}]},{"id":"_help_MEtfsqa5VwNi","title":"Backend API","type":"webView","attributes":[{"type":"label","name":"webViewSrc","value":"https://docs.triliumnotes.org/script-api/backend"},{"name":"iconClass","value":"bx bx-file","type":"label"}],"enforceAttributes":true},{"id":"_help_ApVHZ8JY5ofC","title":"Day.js","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Script API/Day.js"},{"name":"iconClass","value":"bx bx-calendar","type":"label"}]}]},{"id":"_help_vElnKeDNPSVl","title":"Logging","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Logging"},{"name":"iconClass","value":"bx bx-terminal","type":"label"}]},{"id":"_help_cNpC0ITcfX0N","title":"Breaking changes","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Scripting/Breaking changes"},{"name":"iconClass","value":"bx bx-up-arrow-alt","type":"label"}]}]},{"id":"_help_Fm0j45KqyHpU","title":"Miscellaneous","type":"book","attributes":[{"name":"iconClass","value":"bx bx-info-circle","type":"label"}],"children":[{"id":"_help_WFbFXrgnDyyU","title":"Privacy Policy","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Privacy Policy"},{"name":"iconClass","value":"bx bx-file","type":"label"}]},{"id":"_help_NcsmUYZRWEW4","title":"Patterns of personal knowledge","type":"doc","attributes":[{"type":"label","name":"docName","value":"User Guide/User Guide/Miscellaneous/Patterns of personal knowledge"},{"name":"iconClass","value":"bx bx-file","type":"label"}]}]}]
\ No newline at end of file
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
new file mode 100644
index 0000000000..f45db3c93d
--- /dev/null
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Active content.html
@@ -0,0 +1,126 @@
+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 .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
+ 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).
+
+
+
+
+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/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs.html
index 243bc06cd4..190a1f070c 100644
--- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs.html
+++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Themes/Icon Packs.html
@@ -29,10 +29,9 @@
Ideally, create a dedicated spot in your note tree where to place the
icon packs.
Right click the note where to put it and select Import into note .
- Uncheck Safe import .
- Select Import .
- Refresh the application .
+ Uncheck Safe import .
+ Select Import .
+ Refresh the application .
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 24/37] 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 25/37] 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 26/37] 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 27/37] 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 28/37] 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 29/37] 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 30/37] 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:34:48 +0200
Subject: [PATCH 31/37] 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 32/37] 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 178ac088b45bff21d1a92f96deca0ffcb6388bab Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:44:16 +0200
Subject: [PATCH 33/37] 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 34/37] 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 5a3c7355c13e55e929066837d0b8b07e960d448b Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 13:51:48 +0200
Subject: [PATCH 35/37] 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 36/37] 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 e411e5f2cfaf567dede59e770d31a7f43a49dbc9 Mon Sep 17 00:00:00 2001
From: Elian Doran
Date: Sun, 15 Feb 2026 14:20:09 +0200
Subject: [PATCH 37/37] 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}
>
- )
+ );
}