From 5b95b9875b6b7273aaeec0f263b9022f5fa54631 Mon Sep 17 00:00:00 2001 From: SiriusXT <1160925501@qq.com> Date: Mon, 5 Jan 2026 19:27:44 +0800 Subject: [PATCH 01/68] feat(tree): open notes in new window from tree --- apps/client/src/components/app_context.ts | 1 + apps/client/src/menus/tree_context_menu.ts | 6 ++++++ apps/client/src/translations/en/translation.json | 1 + 3 files changed, 8 insertions(+) diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index 560a00438..b5f203b24 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -154,6 +154,7 @@ export type CommandMappings = { }; openInTab: ContextMenuCommandData; openNoteInSplit: ContextMenuCommandData; + openNoteInWindow: ContextMenuCommandData; openNoteInPopup: ContextMenuCommandData; toggleNoteHoisting: ContextMenuCommandData; insertNoteAfter: ContextMenuCommandData; diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index 51f9912b3..81d4878b1 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -79,6 +79,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener | null)[] = [ { title: t("tree-context-menu.open-in-a-new-tab"), command: "openInTab", shortcut: "Ctrl+Click", uiIcon: "bx bx-link-external", enabled: noSelectedNotes }, { title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes }, + { title: t("tree-context-menu.open-in-a-new-window"), command: "openNoteInWindow", uiIcon: "bx bx-window-open", enabled: noSelectedNotes }, { title: t("tree-context-menu.open-in-popup"), command: "openNoteInPopup", uiIcon: "bx bx-edit", enabled: noSelectedNotes }, isHoisted @@ -309,6 +310,11 @@ export default class TreeContextMenu implements SelectMenuItemEventListener Date: Tue, 6 Jan 2026 00:59:32 +0200 Subject: [PATCH 02/68] feat(tree): use direct DOM manipulation instead of jQuery --- apps/client/src/widgets/note_tree.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 9a2c61d22..742d6490a 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -153,7 +153,7 @@ const TPL = /*html*/` const MAX_SEARCH_RESULTS_IN_TREE = 100; // this has to be hanged on the actual elements to effectively intercept and stop click event -const cancelClickPropagation: (e: JQuery.ClickEvent) => void = (e) => e.stopPropagation(); +const cancelClickPropagation: (e: JQuery.ClickEvent | MouseEvent) => void = (e) => e.stopPropagation(); // TODO: Fix once we remove Node.js API from public type Timeout = NodeJS.Timeout | string | number | undefined; @@ -652,12 +652,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { && !note.isLaunchBarConfig() && !note.noteId.startsWith("_help") ) { - const $createChildNoteButton = $(``).on( - "click", - cancelClickPropagation - ); - - $span.append($createChildNoteButton); + const createChildEl = document.createElement("span"); + createChildEl.className = "tree-item-button tn-icon add-note-button bx bx-plus"; + createChildEl.title = t("note_tree.create-child-note"); + createChildEl.addEventListener("click", cancelClickPropagation); + node.span.append(createChildEl); } if (isHoistedNote) { From bde6068f2d4da3b63c1a40ee7582da1a83e77436 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 01:07:37 +0200 Subject: [PATCH 03/68] refactor(tree): extract enchance title into separate method --- apps/client/src/widgets/note_tree.ts | 192 ++++++++++++++------------- 1 file changed, 97 insertions(+), 95 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 742d6490a..3e9481458 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -598,101 +598,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { clones: { highlightActiveClones: true }, - async enhanceTitle ( - event: Event, - data: { - node: Fancytree.FancytreeNode; - noteId: string; - } - ) { - const node = data.node; - - if (!node.data.noteId) { - // if there's "non-note" node, then don't enhance - // this can happen for e.g. "Load error!" node - return; - } - - const note = await froca.getNote(node.data.noteId, true); - - if (!note) { - return; - } - - const activeNoteContext = appContext.tabManager.getActiveContext(); - - const $span = $(node.span); - - $span.find(".tree-item-button").remove(); - $span.find(".note-indicator-icon").remove(); - - const isHoistedNote = activeNoteContext && activeNoteContext.hoistedNoteId === note.noteId && note.noteId !== "root"; - - if (note.hasLabel("workspace") && !isHoistedNote) { - const $enterWorkspaceButton = $(``).on( - "click", - cancelClickPropagation - ); - - $span.append($enterWorkspaceButton); - } - - if (note.type === "search") { - const $refreshSearchButton = $(``).on( - "click", - cancelClickPropagation - ); - - $span.append($refreshSearchButton); - } - - // TODO: Deduplicate with server's notes.ts#getAndValidateParent - if (!["search", "launcher"].includes(note.type) - && !note.isOptions() - && !note.isLaunchBarConfig() - && !note.noteId.startsWith("_help") - ) { - const createChildEl = document.createElement("span"); - createChildEl.className = "tree-item-button tn-icon add-note-button bx bx-plus"; - createChildEl.title = t("note_tree.create-child-note"); - createChildEl.addEventListener("click", cancelClickPropagation); - node.span.append(createChildEl); - } - - if (isHoistedNote) { - const $unhoistButton = $(``).on("click", cancelClickPropagation); - - $span.append($unhoistButton); - } - - // Add clone indicator with tooltip if note has multiple parents - const parentNotes = note.getParentNotes(); - const realParents = parentNotes.filter( - (parent) => !["_share", "_lbBookmarks"].includes(parent.noteId) && parent.type !== "search" - ); - - if (realParents.length > 1) { - const parentTitles = realParents.map((p) => p.title).join(", "); - const tooltipText = realParents.length === 2 - ? t("note_tree.clone-indicator-tooltip-single", { parent: realParents[1].title }) - : t("note_tree.clone-indicator-tooltip", { count: realParents.length, parents: parentTitles }); - - const $cloneIndicator = $(``); - $cloneIndicator.attr("title", tooltipText); - $span.find(".fancytree-title").append($cloneIndicator); - } - - // Add shared indicator with tooltip if note is shared - if (note.isShared()) { - const shareId = note.getOwnedLabelValue("shareAlias") || note.noteId; - const shareUrl = `${location.origin}${location.pathname}share/${shareId}`; - const tooltipText = t("note_tree.shared-indicator-tooltip-with-url", { url: shareUrl }); - - const $sharedIndicator = $(``); - $sharedIndicator.attr("title", tooltipText); - $span.find(".fancytree-title").append($sharedIndicator); - } - }, + enhanceTitle: buildEnhanceTitle(), // this is done to automatically lazy load all expanded notes after tree load loadChildren: (event, data) => { data.node.visit((subNode) => { @@ -1881,3 +1787,99 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { return items; } } + +function buildEnhanceTitle() { + return async (event: Event, + data: { + node: Fancytree.FancytreeNode; + noteId: string; + }) => { + const node = data.node; + + if (!node.data.noteId) { + // if there's "non-note" node, then don't enhance + // this can happen for e.g. "Load error!" node + return; + } + + const note = await froca.getNote(node.data.noteId, true); + + if (!note) { + return; + } + + const activeNoteContext = appContext.tabManager.getActiveContext(); + + const $span = $(node.span); + + $span.find(".tree-item-button").remove(); + $span.find(".note-indicator-icon").remove(); + + const isHoistedNote = activeNoteContext && activeNoteContext.hoistedNoteId === note.noteId && note.noteId !== "root"; + + if (note.hasLabel("workspace") && !isHoistedNote) { + const $enterWorkspaceButton = $(``).on( + "click", + cancelClickPropagation + ); + + $span.append($enterWorkspaceButton); + } + + if (note.type === "search") { + const $refreshSearchButton = $(``).on( + "click", + cancelClickPropagation + ); + + $span.append($refreshSearchButton); + } + + // TODO: Deduplicate with server's notes.ts#getAndValidateParent + if (!["search", "launcher"].includes(note.type) + && !note.isOptions() + && !note.isLaunchBarConfig() + && !note.noteId.startsWith("_help") + ) { + const createChildEl = document.createElement("span"); + createChildEl.className = "tree-item-button tn-icon add-note-button bx bx-plus"; + createChildEl.title = t("note_tree.create-child-note"); + createChildEl.addEventListener("click", cancelClickPropagation); + node.span.append(createChildEl); + } + + if (isHoistedNote) { + const $unhoistButton = $(``).on("click", cancelClickPropagation); + + $span.append($unhoistButton); + } + + // Add clone indicator with tooltip if note has multiple parents + const parentNotes = note.getParentNotes(); + const realParents = parentNotes.filter( + (parent) => !["_share", "_lbBookmarks"].includes(parent.noteId) && parent.type !== "search" + ); + + if (realParents.length > 1) { + const parentTitles = realParents.map((p) => p.title).join(", "); + const tooltipText = realParents.length === 2 + ? t("note_tree.clone-indicator-tooltip-single", { parent: realParents[1].title }) + : t("note_tree.clone-indicator-tooltip", { count: realParents.length, parents: parentTitles }); + + const $cloneIndicator = $(``); + $cloneIndicator.attr("title", tooltipText); + $span.find(".fancytree-title").append($cloneIndicator); + } + + // Add shared indicator with tooltip if note is shared + if (note.isShared()) { + const shareId = note.getOwnedLabelValue("shareAlias") || note.noteId; + const shareUrl = `${location.origin}${location.pathname}share/${shareId}`; + const tooltipText = t("note_tree.shared-indicator-tooltip-with-url", { url: shareUrl }); + + const $sharedIndicator = $(``); + $sharedIndicator.attr("title", tooltipText); + $span.find(".fancytree-title").append($sharedIndicator); + } + }; +} From 0867b81c7ad492f6fe475a349f1853a52436e9aa Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 01:13:31 +0200 Subject: [PATCH 04/68] feat(tree): use template for create child to improve performance --- apps/client/src/widgets/note_tree.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 3e9481458..b0446fae9 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1789,11 +1789,16 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } function buildEnhanceTitle() { - return async (event: Event, + const createChildTemplate = document.createElement("span"); + createChildTemplate.className = "tree-item-button tn-icon add-note-button bx bx-plus"; + createChildTemplate.title = t("note_tree.create-child-note"); + createChildTemplate.addEventListener("click", cancelClickPropagation); + + return async function enhanceTitle(event: Event, data: { node: Fancytree.FancytreeNode; noteId: string; - }) => { + }) { const node = data.node; if (!node.data.noteId) { @@ -1841,11 +1846,7 @@ function buildEnhanceTitle() { && !note.isLaunchBarConfig() && !note.noteId.startsWith("_help") ) { - const createChildEl = document.createElement("span"); - createChildEl.className = "tree-item-button tn-icon add-note-button bx bx-plus"; - createChildEl.title = t("note_tree.create-child-note"); - createChildEl.addEventListener("click", cancelClickPropagation); - node.span.append(createChildEl); + node.span.append(createChildTemplate.cloneNode()); } if (isHoistedNote) { From d0cdcfc32c9cf03fec0e55f44dbfa73d97d3cf5e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 01:21:43 +0200 Subject: [PATCH 05/68] refactor(tree): use loop for mini optimisation --- apps/client/src/widgets/note_tree.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index b0446fae9..c4d4ce0c2 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1857,9 +1857,12 @@ function buildEnhanceTitle() { // Add clone indicator with tooltip if note has multiple parents const parentNotes = note.getParentNotes(); - const realParents = parentNotes.filter( - (parent) => !["_share", "_lbBookmarks"].includes(parent.noteId) && parent.type !== "search" - ); + const realParents: FNote[] = []; + for (const parent of parentNotes) { + if (parent.noteId !== "_share" && parent.noteId !== "_lbBookmarks" && parent.type !== "search") { + realParents.push(parent); + } + } if (realParents.length > 1) { const parentTitles = realParents.map((p) => p.title).join(", "); From dec4dafba61c084adbfcba012b2c38fede122049 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 01:26:56 +0200 Subject: [PATCH 06/68] feat(tree): avoid async --- apps/client/src/widgets/note_tree.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index c4d4ce0c2..f815edfc9 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1807,11 +1807,8 @@ function buildEnhanceTitle() { return; } - const note = await froca.getNote(node.data.noteId, true); - - if (!note) { - return; - } + const note = froca.getNoteFromCache(node.data.noteId); + if (!note) return; const activeNoteContext = appContext.tabManager.getActiveContext(); From aff4f7e01091281895ab7bdaa2f60ec8f907b5b0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 01:34:02 +0200 Subject: [PATCH 07/68] feat(tree): disable animation for performance --- apps/client/src/widgets/note_tree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index f815edfc9..8f4ead199 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -353,6 +353,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { this.$tree.fancytree({ titlesTabbable: true, keyboard: true, + toggleEffect: false, extensions: ["dnd5", "clones", "filter"], source: treeData, scrollOfs: { From 1f77540dbb3d8874dc2f8da6a690685c21cd1715 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 7 Jan 2026 19:32:37 +0200 Subject: [PATCH 08/68] fix(text): Title is not selected when creating a note via the launcher (#8292) --- apps/client/src/widgets/type_widgets/text/EditableText.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/type_widgets/text/EditableText.tsx b/apps/client/src/widgets/type_widgets/text/EditableText.tsx index e5ea9c498..fba7d6966 100644 --- a/apps/client/src/widgets/type_widgets/text/EditableText.tsx +++ b/apps/client/src/widgets/type_widgets/text/EditableText.tsx @@ -286,7 +286,7 @@ function useWatchdogCrashHandling() { const currentState = watchdog.state; logInfo(`CKEditor state changed to ${currentState}`); - if (currentState === "ready") { + if (currentState === "ready" && hasCrashed.current) { hasCrashed.current = false; watchdog.editor?.focus(); } From 9d380dd828e147d51e68bfcad7356bed8bc54c0a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 6 Jan 2026 00:10:11 +0200 Subject: [PATCH 09/68] fix(sql_console): cannot copy table data (#8268) --- apps/client/src/widgets/sql_result.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/sql_result.tsx b/apps/client/src/widgets/sql_result.tsx index e4fde650b..7aaa5739d 100644 --- a/apps/client/src/widgets/sql_result.tsx +++ b/apps/client/src/widgets/sql_result.tsx @@ -23,7 +23,7 @@ export default function SqlResults() { {t("sql_result.no_rows")} ) : ( -
+
{results?.map(rows => { // inserts, updates if (typeof rows === "object" && !Array.isArray(rows)) { From b1dc0e234f08d9c17c05c3d5e9be9eb39feab77b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 1 Jan 2026 20:56:04 +0200 Subject: [PATCH 10/68] fix(popupEditor): fix closing of popupEditor when inserting note link (#8224) --- apps/client/src/widgets/dialogs/PopupEditor.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/client/src/widgets/dialogs/PopupEditor.tsx b/apps/client/src/widgets/dialogs/PopupEditor.tsx index 887568d37..e8e73ae78 100644 --- a/apps/client/src/widgets/dialogs/PopupEditor.tsx +++ b/apps/client/src/widgets/dialogs/PopupEditor.tsx @@ -88,6 +88,7 @@ export default function PopupEditor() { onHidden={() => setShown(false)} keepInDom // needed for faster loading noFocus // automatic focus breaks block popup + stackable > {!isNewLayout && } From 53e1fa1047285d2a2a315beeff61ce3cb83ed401 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 1 Jan 2026 20:58:56 +0200 Subject: [PATCH 11/68] fix(mermaid) diagrams not saving content and SVG attachment (#8220) --- apps/client/src/widgets/type_widgets/Mermaid.tsx | 1 + apps/client/src/widgets/type_widgets/code/Code.tsx | 6 ++++-- .../type_widgets/helpers/SvgSplitEditor.tsx | 14 +++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/Mermaid.tsx b/apps/client/src/widgets/type_widgets/Mermaid.tsx index 65574aa01..9403c4cf6 100644 --- a/apps/client/src/widgets/type_widgets/Mermaid.tsx +++ b/apps/client/src/widgets/type_widgets/Mermaid.tsx @@ -29,6 +29,7 @@ export default function Mermaid(props: TypeWidgetProps) { ); diff --git a/apps/client/src/widgets/type_widgets/code/Code.tsx b/apps/client/src/widgets/type_widgets/code/Code.tsx index 1c08ee808..b3c8686fa 100644 --- a/apps/client/src/widgets/type_widgets/code/Code.tsx +++ b/apps/client/src/widgets/type_widgets/code/Code.tsx @@ -1,6 +1,7 @@ import "./code.css"; import { default as VanillaCodeMirror, getThemeById } from "@triliumnext/codemirror"; +import { NoteType } from "@triliumnext/commons"; import { useEffect, useRef, useState } from "preact/hooks"; import appContext, { CommandListenerData } from "../../../components/app_context"; @@ -24,6 +25,7 @@ export interface EditableCodeProps extends TypeWidgetProps, Omit void; /** Invoked after the content of the note has been uploaded to the server, using a spaced update. */ @@ -72,14 +74,14 @@ function formatViewSource(note: FNote, content: string) { return content; } -export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentComponent, updateInterval, onContentChanged, dataSaved, ...editorProps }: EditableCodeProps) { +export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentComponent, updateInterval, noteType = "code", onContentChanged, dataSaved, ...editorProps }: EditableCodeProps) { const editorRef = useRef(null); const containerRef = useRef(null); const [ vimKeymapEnabled ] = useTriliumOptionBool("vimKeymapEnabled"); const mime = useNoteProperty(note, "mime"); const spacedUpdate = useEditorSpacedUpdate({ note, - noteType: "code", + noteType, noteContext, getData: () => ({ content: editorRef.current?.getText() ?? "" }), onContentChange: (content) => { diff --git a/apps/client/src/widgets/type_widgets/helpers/SvgSplitEditor.tsx b/apps/client/src/widgets/type_widgets/helpers/SvgSplitEditor.tsx index 3c9eff27a..9a1c1dd4e 100644 --- a/apps/client/src/widgets/type_widgets/helpers/SvgSplitEditor.tsx +++ b/apps/client/src/widgets/type_widgets/helpers/SvgSplitEditor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "preact/hooks"; +import { useCallback, useEffect, useRef, useState } from "preact/hooks"; import { t } from "../../../services/i18n"; import SplitEditor, { PreviewButton, SplitEditorProps } from "./SplitEditor"; import { RawHtmlBlock } from "../../react/RawHtml"; @@ -55,7 +55,9 @@ export default function SvgSplitEditor({ ntxId, note, attachmentName, renderSvg, } // Save as attachment. - function onSave() { + const onSave = useCallback(() => { + if (!svg) return; // Don't save if SVG hasn't been rendered yet + const payload = { role: "image", title: `${attachmentName}.svg`, @@ -65,16 +67,18 @@ export default function SvgSplitEditor({ ntxId, note, attachmentName, renderSvg, }; server.post(`notes/${note.noteId}/attachments?matchBy=title`, payload); - } + }, [ svg, attachmentName, note.noteId ]); // Save the SVG when entering a note only when it does not have an attachment. useEffect(() => { + if (!svg) return; // Wait until SVG is rendered + note?.getAttachments().then((attachments) => { if (!attachments.find((a) => a.title === `${attachmentName}.svg`)) { onSave(); } - }); - }, [ note ]); + }).catch(e => console.error("Failed to get attachments for SVGSplitEditor", e)); + }, [ note, svg, attachmentName, onSave ]); // Import/export useTriliumEvent("exportSvg", async({ ntxId: eventNtxId }) => { From 02f43d62395ff77c43a4f5339a6f710906c5cce5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 15:33:16 +0200 Subject: [PATCH 12/68] fix(mermaid): code not scrollable (closes #8299) --- apps/client/src/widgets/type_widgets/helpers/SplitEditor.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css b/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css index 72f680b01..955daf26b 100644 --- a/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css +++ b/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css @@ -15,6 +15,8 @@ .note-detail-split .note-detail-split-editor { width: 100%; flex-grow: 1; + min-width: 0; + min-height: 0; } .note-detail-split .note-detail-split-editor .note-detail-code { From f7ae046b201919a488c81d4783b1869164f0520f Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 15:52:19 +0200 Subject: [PATCH 13/68] fix(mermaid): error container not scrollable (closes #8299) --- apps/client/src/widgets/type_widgets/helpers/SplitEditor.css | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css b/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css index 955daf26b..0dce268ea 100644 --- a/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css +++ b/apps/client/src/widgets/type_widgets/helpers/SplitEditor.css @@ -32,6 +32,7 @@ margin: 5px; white-space: pre-wrap; font-size: 0.85em; + overflow: auto; } .note-detail-split .note-detail-split-preview { From 5cc7b259ce59552d47a8cae1a0aba1c283486525 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 15:59:57 +0200 Subject: [PATCH 14/68] fix(client): max content width not preserved (closes #8065) --- apps/client/src/widgets/NoteDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx index c2f212044..ef9d68c99 100644 --- a/apps/client/src/widgets/NoteDetail.tsx +++ b/apps/client/src/widgets/NoteDetail.tsx @@ -215,7 +215,7 @@ export default function NoteDetail() { return (
{Object.entries(noteTypesToRender).map(([ itemType, Element ]) => { return Date: Thu, 8 Jan 2026 16:44:35 +0200 Subject: [PATCH 15/68] fix(client): cycle in include causing infinite loop (closes #8294) --- apps/client/src/services/content_renderer.ts | 35 +++++---- .../services/content_renderer_text.spec.ts | 78 +++++++++++++++++++ .../src/services/content_renderer_text.ts | 19 ++++- 3 files changed, 113 insertions(+), 19 deletions(-) create mode 100644 apps/client/src/services/content_renderer_text.spec.ts diff --git a/apps/client/src/services/content_renderer.ts b/apps/client/src/services/content_renderer.ts index 0e9db9302..9b059101c 100644 --- a/apps/client/src/services/content_renderer.ts +++ b/apps/client/src/services/content_renderer.ts @@ -1,18 +1,19 @@ -import renderService from "./render.js"; +import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons"; +import WheelZoom from 'vanilla-js-wheel-zoom'; + +import FAttachment from "../entities/fattachment.js"; +import FNote from "../entities/fnote.js"; +import imageContextMenuService from "../menus/image_context_menu.js"; +import { t } from "../services/i18n.js"; +import renderText from "./content_renderer_text.js"; +import renderDoc from "./doc_renderer.js"; +import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js"; +import openService from "./open.js"; import protectedSessionService from "./protected_session.js"; import protectedSessionHolder from "./protected_session_holder.js"; -import openService from "./open.js"; -import utils from "./utils.js"; -import FNote from "../entities/fnote.js"; -import FAttachment from "../entities/fattachment.js"; -import imageContextMenuService from "../menus/image_context_menu.js"; +import renderService from "./render.js"; import { applySingleBlockSyntaxHighlight } from "./syntax_highlight.js"; -import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js"; -import renderDoc from "./doc_renderer.js"; -import { t } from "../services/i18n.js"; -import WheelZoom from 'vanilla-js-wheel-zoom'; -import { normalizeMimeTypeForCKEditor } from "@triliumnext/commons"; -import renderText from "./content_renderer_text.js"; +import utils from "./utils.js"; let idCounter = 1; @@ -22,6 +23,8 @@ export interface RenderOptions { imageHasZoom?: boolean; /** If enabled, it will prevent the default behavior in which an empty note would display a list of children. */ noChildrenList?: boolean; + /** Set of note IDs that have already been seen during rendering to prevent infinite recursion. */ + seenNoteIds?: Set; } const CODE_MIME_TYPES = new Set(["application/json"]); @@ -152,7 +155,7 @@ function renderImage(entity: FNote | FAttachment, $renderedContent: JQuery") .attr("src", url || "") - .attr("id", "attachment-image-" + idCounter++) + .attr("id", `attachment-image-${ idCounter++}`) .css("max-width", "100%"); $renderedContent.append($img); @@ -231,14 +234,14 @@ function renderFile(entity: FNote | FAttachment, type: string, $renderedContent: $downloadButton.on("click", (e) => { e.stopPropagation(); - openService.downloadFileNote(entity.noteId) + openService.downloadFileNote(entity.noteId); }); $openButton.on("click", async (e) => { const iconEl = $openButton.find("> .bx"); iconEl.removeClass("bx bx-link-external"); iconEl.addClass("bx bx-loader spin"); e.stopPropagation(); - await openService.openNoteExternally(entity.noteId, entity.mime) + await openService.openNoteExternally(entity.noteId, entity.mime); iconEl.removeClass("bx bx-loader spin"); iconEl.addClass("bx bx-link-external"); }); @@ -266,7 +269,7 @@ async function renderMermaid(note: FNote | FAttachment, $renderedContent: JQuery try { await loadElkIfNeeded(mermaid, content); - const { svg } = await mermaid.mermaidAPI.render("in-mermaid-graph-" + idCounter++, content); + const { svg } = await mermaid.mermaidAPI.render(`in-mermaid-graph-${ idCounter++}`, content); $renderedContent.append($(postprocessMermaidSvg(svg))); } catch (e) { diff --git a/apps/client/src/services/content_renderer_text.spec.ts b/apps/client/src/services/content_renderer_text.spec.ts new file mode 100644 index 000000000..3bd9b6554 --- /dev/null +++ b/apps/client/src/services/content_renderer_text.spec.ts @@ -0,0 +1,78 @@ +import { trimIndentation } from "@triliumnext/commons"; +import { describe, expect, it } from "vitest"; + +import { buildNote } from "../test/easy-froca"; +import renderText from "./content_renderer_text"; + +describe("Text content renderer", () => { + it("renders included note", async () => { + const contentEl = document.createElement("div"); + const includedNote = buildNote({ + title: "Included note", + content: "

This is the included note.

" + }); + const note = buildNote({ + title: "New note", + content: trimIndentation` +

+ Hi there +

+
+   +
+ ` + }); + await renderText(note, $(contentEl)); + expect(contentEl.querySelectorAll("section.include-note").length).toBe(1); + expect(contentEl.querySelectorAll("section.include-note p").length).toBe(1); + }); + + it("doesn't enter infinite loop on direct recursion", async () => { + const contentEl = document.createElement("div"); + const note = buildNote({ + title: "New note", + id: "Y7mBwmRjQyb4", + content: trimIndentation` +

+ Hi there +

+
+   +
+
+   +
+ ` + }); + await renderText(note, $(contentEl)); + expect(contentEl.querySelectorAll("section.include-note").length).toBe(0); + }); + + it("doesn't enter infinite loop on indirect recursion", async () => { + const contentEl = document.createElement("div"); + buildNote({ + id: "first", + title: "Included note", + content: trimIndentation`\ +

This is the included note.

+
+   +
+ ` + }); + const note = buildNote({ + id: "second", + title: "New note", + content: trimIndentation` +

+ Hi there +

+
+   +
+ ` + }); + await renderText(note, $(contentEl)); + expect(contentEl.querySelectorAll("section.include-note").length).toBe(1); + }); +}); diff --git a/apps/client/src/services/content_renderer_text.ts b/apps/client/src/services/content_renderer_text.ts index 5b388d64e..72d67d4d5 100644 --- a/apps/client/src/services/content_renderer_text.ts +++ b/apps/client/src/services/content_renderer_text.ts @@ -15,7 +15,10 @@ export default async function renderText(note: FNote | FAttachment, $renderedCon if (blob && !isHtmlEmpty(blob.content)) { $renderedContent.append($('
').html(blob.content)); - await renderIncludedNotes($renderedContent[0]); + + const seenNoteIds = options.seenNoteIds ?? new Set(); + seenNoteIds.add("noteId" in note ? note.noteId : note.attachmentId); + await renderIncludedNotes($renderedContent[0], seenNoteIds); if ($renderedContent.find("span.math-tex").length > 0) { renderMathInElement($renderedContent[0], { trust: true }); @@ -39,7 +42,7 @@ export default async function renderText(note: FNote | FAttachment, $renderedCon } } -async function renderIncludedNotes(contentEl: HTMLElement) { +async function renderIncludedNotes(contentEl: HTMLElement, seenNoteIds: Set) { // TODO: Consider duplicating with server's share/content_renderer.ts. const includeNoteEls = contentEl.querySelectorAll("section.include-note"); @@ -66,8 +69,18 @@ async function renderIncludedNotes(contentEl: HTMLElement) { continue; } - const renderedContent = (await content_renderer.getRenderedContent(note)).$renderedContent; + if (seenNoteIds.has(noteId)) { + console.warn(`Skipping inclusion of ${noteId} to avoid circular reference.`); + includeNoteEl.remove(); + continue; + } + + const renderedContent = (await content_renderer.getRenderedContent(note, { + seenNoteIds + })).$renderedContent; includeNoteEl.replaceChildren(...renderedContent); + + seenNoteIds.add(noteId); } } From 4625efda7f61e3abf6f18d352ba341438cd21f21 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 16:50:27 +0200 Subject: [PATCH 16/68] fix(note_list): skip rendering of included notes for performance (closes #8017) --- apps/client/src/services/content_renderer.ts | 2 ++ .../services/content_renderer_text.spec.ts | 21 +++++++++++++++++++ .../src/services/content_renderer_text.ts | 6 +++++- .../collections/legacy/ListOrGridView.tsx | 3 ++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/content_renderer.ts b/apps/client/src/services/content_renderer.ts index 9b059101c..38bb779ac 100644 --- a/apps/client/src/services/content_renderer.ts +++ b/apps/client/src/services/content_renderer.ts @@ -23,6 +23,8 @@ export interface RenderOptions { imageHasZoom?: boolean; /** If enabled, it will prevent the default behavior in which an empty note would display a list of children. */ noChildrenList?: boolean; + /** If enabled, it will prevent rendering of included notes. */ + noIncludedNotes?: boolean; /** Set of note IDs that have already been seen during rendering to prevent infinite recursion. */ seenNoteIds?: Set; } diff --git a/apps/client/src/services/content_renderer_text.spec.ts b/apps/client/src/services/content_renderer_text.spec.ts index 3bd9b6554..6480e1991 100644 --- a/apps/client/src/services/content_renderer_text.spec.ts +++ b/apps/client/src/services/content_renderer_text.spec.ts @@ -27,6 +27,27 @@ describe("Text content renderer", () => { expect(contentEl.querySelectorAll("section.include-note p").length).toBe(1); }); + it("skips rendering included note", async () => { + const contentEl = document.createElement("div"); + const includedNote = buildNote({ + title: "Included note", + content: "

This is the included note.

" + }); + const note = buildNote({ + title: "New note", + content: trimIndentation` +

+ Hi there +

+
+   +
+ ` + }); + await renderText(note, $(contentEl), { noIncludedNotes: true }); + expect(contentEl.querySelectorAll("section.include-note").length).toBe(0); + }); + it("doesn't enter infinite loop on direct recursion", async () => { const contentEl = document.createElement("div"); const note = buildNote({ diff --git a/apps/client/src/services/content_renderer_text.ts b/apps/client/src/services/content_renderer_text.ts index 72d67d4d5..a3f277c49 100644 --- a/apps/client/src/services/content_renderer_text.ts +++ b/apps/client/src/services/content_renderer_text.ts @@ -18,7 +18,11 @@ export default async function renderText(note: FNote | FAttachment, $renderedCon const seenNoteIds = options.seenNoteIds ?? new Set(); seenNoteIds.add("noteId" in note ? note.noteId : note.attachmentId); - await renderIncludedNotes($renderedContent[0], seenNoteIds); + if (!options.noIncludedNotes) { + await renderIncludedNotes($renderedContent[0], seenNoteIds); + } else { + $renderedContent.find("section.include-note").remove(); + } if ($renderedContent.find("span.math-tex").length > 0) { renderMathInElement($renderedContent[0], { trust: true }); diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx index 8180e6d65..9a0c7391d 100644 --- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx +++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx @@ -153,7 +153,8 @@ function NoteContent({ note, trim, noChildrenList, highlightedTokens }: { note: useEffect(() => { content_renderer.getRenderedContent(note, { trim, - noChildrenList + noChildrenList, + noIncludedNotes: true }) .then(({ $renderedContent, type }) => { if (!contentRef.current) return; From b11a30c49c7d043c25e584580832ef1715ae9c17 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 16:55:51 +0200 Subject: [PATCH 17/68] fix(launcher_bar): crashing if there is a non-launcher note (closes #8218) --- .../widgets/launch_bar/LauncherContainer.tsx | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/client/src/widgets/launch_bar/LauncherContainer.tsx b/apps/client/src/widgets/launch_bar/LauncherContainer.tsx index 26a502a8a..3450a4c01 100644 --- a/apps/client/src/widgets/launch_bar/LauncherContainer.tsx +++ b/apps/client/src/widgets/launch_bar/LauncherContainer.tsx @@ -1,17 +1,18 @@ import { useCallback, useLayoutEffect, useState } from "preact/hooks"; + import FNote from "../../entities/fnote"; import froca from "../../services/froca"; import { isDesktop, isMobile } from "../../services/utils"; -import CalendarWidget from "./CalendarWidget"; -import SpacerWidget from "./SpacerWidget"; -import BookmarkButtons from "./BookmarkButtons"; -import ProtectedSessionStatusWidget from "./ProtectedSessionStatusWidget"; -import SyncStatus from "./SyncStatus"; -import HistoryNavigationButton from "./HistoryNavigation"; -import { AiChatButton, CommandButton, CustomWidget, NoteLauncher, QuickSearchLauncherWidget, ScriptLauncher, TodayLauncher } from "./LauncherDefinitions"; import { useTriliumEvent } from "../react/hooks"; import { onWheelHorizontalScroll } from "../widget_utils"; +import BookmarkButtons from "./BookmarkButtons"; +import CalendarWidget from "./CalendarWidget"; +import HistoryNavigationButton from "./HistoryNavigation"; import { LaunchBarContext } from "./launch_bar_widgets"; +import { AiChatButton, CommandButton, CustomWidget, NoteLauncher, QuickSearchLauncherWidget, ScriptLauncher, TodayLauncher } from "./LauncherDefinitions"; +import ProtectedSessionStatusWidget from "./ProtectedSessionStatusWidget"; +import SpacerWidget from "./SpacerWidget"; +import SyncStatus from "./SyncStatus"; export default function LauncherContainer({ isHorizontalLayout }: { isHorizontalLayout: boolean }) { const childNotes = useLauncherChildNotes(); @@ -34,18 +35,19 @@ export default function LauncherContainer({ isHorizontalLayout }: { isHorizontal }}> {childNotes?.map(childNote => { if (childNote.type !== "launcher") { - throw new Error(`Note '${childNote.noteId}' '${childNote.title}' is not a launcher even though it's in the launcher subtree`); + console.warn(`Note '${childNote.noteId}' '${childNote.title}' is not a launcher even though it's in the launcher subtree`); + return false; } if (!isDesktop() && childNote.isLabelTruthy("desktopOnly")) { return false; } - return + return ; })}
- ) + ); } function Launcher({ note, isHorizontalLayout }: { note: FNote, isHorizontalLayout: boolean }) { @@ -72,7 +74,7 @@ function initBuiltinWidget(note: FNote, isHorizontalLayout: boolean) { const builtinWidget = note.getLabelValue("builtinWidget"); switch (builtinWidget) { case "calendar": - return + return ; case "spacer": // || has to be inside since 0 is a valid value const baseSize = parseInt(note.getLabelValue("baseSize") || "40"); @@ -86,15 +88,15 @@ function initBuiltinWidget(note: FNote, isHorizontalLayout: boolean) { case "syncStatus": return ; case "backInHistoryButton": - return + return ; case "forwardInHistoryButton": - return + return ; case "todayInJournal": - return + return ; case "quickSearch": - return + return ; case "aiChatLauncher": - return + return ; default: throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); } From ae881101d8216f25b2801b63911887806a92974c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 17:23:40 +0200 Subject: [PATCH 18/68] fix(note_list): archived notes displayed in empty grid card (closes #8184) --- apps/client/src/services/content_renderer.ts | 2 ++ .../services/content_renderer_text.spec.ts | 35 +++++++++++++++++++ .../src/services/content_renderer_text.ts | 8 +++-- .../collections/legacy/ListOrGridView.tsx | 19 +++++++--- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/apps/client/src/services/content_renderer.ts b/apps/client/src/services/content_renderer.ts index 38bb779ac..cdfdb67fe 100644 --- a/apps/client/src/services/content_renderer.ts +++ b/apps/client/src/services/content_renderer.ts @@ -25,6 +25,8 @@ export interface RenderOptions { noChildrenList?: boolean; /** If enabled, it will prevent rendering of included notes. */ noIncludedNotes?: boolean; + /** If enabled, it will include archived notes when rendering children list. */ + includeArchivedNotes?: boolean; /** Set of note IDs that have already been seen during rendering to prevent infinite recursion. */ seenNoteIds?: Set; } diff --git a/apps/client/src/services/content_renderer_text.spec.ts b/apps/client/src/services/content_renderer_text.spec.ts index 6480e1991..e9955d4b0 100644 --- a/apps/client/src/services/content_renderer_text.spec.ts +++ b/apps/client/src/services/content_renderer_text.spec.ts @@ -96,4 +96,39 @@ describe("Text content renderer", () => { await renderText(note, $(contentEl)); expect(contentEl.querySelectorAll("section.include-note").length).toBe(1); }); + + it("renders children list when note is empty", async () => { + const contentEl = document.createElement("div"); + const parentNote = buildNote({ + title: "Parent note", + children: [ + { title: "Child note 1" }, + { title: "Child note 2" } + ] + }); + await renderText(parentNote, $(contentEl)); + console.log(contentEl.innerHTML); + const items = contentEl.querySelectorAll("a"); + expect(items.length).toBe(2); + expect(items[0].textContent).toBe("Child note 1"); + expect(items[1].textContent).toBe("Child note 2"); + }); + + it("skips archived notes in children list", async () => { + const contentEl = document.createElement("div"); + const parentNote = buildNote({ + title: "Parent note", + children: [ + { title: "Child note 1" }, + { title: "Child note 2", "#archived": "" }, + { title: "Child note 3" } + ] + }); + await renderText(parentNote, $(contentEl)); + console.log(contentEl.innerHTML); + const items = contentEl.querySelectorAll("a"); + expect(items.length).toBe(2); + expect(items[0].textContent).toBe("Child note 1"); + expect(items[1].textContent).toBe("Child note 3"); + }); }); diff --git a/apps/client/src/services/content_renderer_text.ts b/apps/client/src/services/content_renderer_text.ts index a3f277c49..090aa7f03 100644 --- a/apps/client/src/services/content_renderer_text.ts +++ b/apps/client/src/services/content_renderer_text.ts @@ -42,7 +42,7 @@ export default async function renderText(note: FNote | FAttachment, $renderedCon await rewriteMermaidDiagramsInContainer($renderedContent[0] as HTMLDivElement); await formatCodeBlocks($renderedContent); } else if (note instanceof FNote && !options.noChildrenList) { - await renderChildrenList($renderedContent, note); + await renderChildrenList($renderedContent, note, options.includeArchivedNotes ?? false); } } @@ -115,7 +115,7 @@ export async function applyInlineMermaid(container: HTMLDivElement) { } } -async function renderChildrenList($renderedContent: JQuery, note: FNote) { +async function renderChildrenList($renderedContent: JQuery, note: FNote, includeArchivedNotes: boolean) { let childNoteIds = note.getChildNoteIds(); if (!childNoteIds.length) { @@ -125,14 +125,16 @@ async function renderChildrenList($renderedContent: JQuery, note: F $renderedContent.css("padding", "10px"); $renderedContent.addClass("text-with-ellipsis"); + // just load the first 10 child notes if (childNoteIds.length > 10) { childNoteIds = childNoteIds.slice(0, 10); } - // just load the first 10 child notes const childNotes = await froca.getNotes(childNoteIds); for (const childNote of childNotes) { + if (childNote.isArchived && !includeArchivedNotes) continue; + $renderedContent.append( await link.createLink(`${note.noteId}/${childNote.noteId}`, { showTooltip: false, diff --git a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx index 9a0c7391d..74e2182b7 100644 --- a/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx +++ b/apps/client/src/widgets/collections/legacy/ListOrGridView.tsx @@ -45,6 +45,7 @@ export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens } export function GridView({ note, noteIds: unfilteredNoteIds, highlightedTokens }: ViewModeProps<{}>) { const noteIds = useFilteredNoteIds(note, unfilteredNoteIds); const { pageNotes, ...pagination } = usePagination(note, noteIds); + const [ includeArchived ] = useNoteLabelBoolean(note, "includeArchived"); return (
@@ -53,7 +54,7 @@ export function GridView({ note, noteIds: unfilteredNoteIds, highlightedTokens } @@ -95,14 +96,14 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan {isExpanded && <> - + }
); } -function GridNoteCard({ note, parentNote, highlightedTokens }: { note: FNote, parentNote: FNote, highlightedTokens: string[] | null | undefined }) { +function GridNoteCard({ note, parentNote, highlightedTokens, includeArchived }: { note: FNote, parentNote: FNote, highlightedTokens: string[] | null | undefined, includeArchived: boolean }) { const titleRef = useRef(null); const [ noteTitle, setNoteTitle ] = useState(); const notePath = getNotePath(parentNote, note); @@ -130,6 +131,7 @@ function GridNoteCard({ note, parentNote, highlightedTokens }: { note: FNote, pa note={note} trim highlightedTokens={highlightedTokens} + includeArchivedNotes={includeArchived} />
); @@ -146,7 +148,13 @@ function NoteAttributes({ note }: { note: FNote }) { return ; } -function NoteContent({ note, trim, noChildrenList, highlightedTokens }: { note: FNote, trim?: boolean, noChildrenList?: boolean, highlightedTokens: string[] | null | undefined }) { +function NoteContent({ note, trim, noChildrenList, highlightedTokens, includeArchivedNotes }: { + note: FNote; + trim?: boolean; + noChildrenList?: boolean; + highlightedTokens: string[] | null | undefined; + includeArchivedNotes: boolean; +}) { const contentRef = useRef(null); const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens); @@ -154,7 +162,8 @@ function NoteContent({ note, trim, noChildrenList, highlightedTokens }: { note: content_renderer.getRenderedContent(note, { trim, noChildrenList, - noIncludedNotes: true + noIncludedNotes: true, + includeArchivedNotes }) .then(({ $renderedContent, type }) => { if (!contentRef.current) return; From 034091a6969945b55367737707a1f1f4338ad60c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 18:08:34 +0200 Subject: [PATCH 19/68] docs(release): prepare for v0.101.2 --- docs/Release Notes/!!!meta.json | 124 +++++++++++-------- docs/Release Notes/Release Notes/v0.101.2.md | 22 ++++ 2 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 docs/Release Notes/Release Notes/v0.101.2.md diff --git a/docs/Release Notes/!!!meta.json b/docs/Release Notes/!!!meta.json index 55f076f79..dfe5da630 100644 --- a/docs/Release Notes/!!!meta.json +++ b/docs/Release Notes/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.101.0", + "appVersion": "0.101.1", "files": [ { "isClone": false, @@ -61,6 +61,32 @@ "attachments": [], "dirFileName": "Release Notes", "children": [ + { + "isClone": false, + "noteId": "vcBthaXcwAm6", + "notePath": [ + "hD3V4hiu2VW4", + "vcBthaXcwAm6" + ], + "title": "v0.101.2", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "wyurrlcDl416", + "isInheritable": false, + "position": 60 + } + ], + "format": "markdown", + "dataFileName": "v0.101.2.md", + "attachments": [] + }, { "isClone": false, "noteId": "AgUcrU9nFXuW", @@ -69,7 +95,7 @@ "AgUcrU9nFXuW" ], "title": "v0.101.1", - "notePosition": 10, + "notePosition": 20, "prefix": null, "isExpanded": false, "type": "text", @@ -95,7 +121,7 @@ "uYwlZ594eyJu" ], "title": "v0.101.0", - "notePosition": 20, + "notePosition": 30, "prefix": null, "isExpanded": false, "type": "text", @@ -121,7 +147,7 @@ "iPGKEk7pwJXK" ], "title": "v0.100.0", - "notePosition": 30, + "notePosition": 40, "prefix": null, "isExpanded": false, "type": "text", @@ -147,7 +173,7 @@ "7HKMTjmopLcM" ], "title": "v0.99.5", - "notePosition": 40, + "notePosition": 50, "prefix": null, "isExpanded": false, "type": "text", @@ -173,7 +199,7 @@ "RMBaNYPsRpIr" ], "title": "v0.99.4", - "notePosition": 50, + "notePosition": 60, "prefix": null, "isExpanded": false, "type": "text", @@ -199,7 +225,7 @@ "yuroLztFfpu5" ], "title": "v0.99.3", - "notePosition": 60, + "notePosition": 70, "prefix": null, "isExpanded": false, "type": "text", @@ -225,7 +251,7 @@ "z207sehwMJ6C" ], "title": "v0.99.2", - "notePosition": 70, + "notePosition": 80, "prefix": null, "isExpanded": false, "type": "text", @@ -251,7 +277,7 @@ "WGQsXq2jNyTi" ], "title": "v0.99.1", - "notePosition": 80, + "notePosition": 90, "prefix": null, "isExpanded": false, "type": "text", @@ -277,7 +303,7 @@ "cyw2Yue9vXf3" ], "title": "v0.99.0", - "notePosition": 90, + "notePosition": 100, "prefix": null, "isExpanded": false, "type": "text", @@ -303,7 +329,7 @@ "QOJwjruOUr4k" ], "title": "v0.98.1", - "notePosition": 100, + "notePosition": 110, "prefix": null, "isExpanded": false, "type": "text", @@ -329,7 +355,7 @@ "PLUoryywi0BC" ], "title": "v0.98.0", - "notePosition": 110, + "notePosition": 120, "prefix": null, "isExpanded": false, "type": "text", @@ -355,7 +381,7 @@ "lvOuiWsLDv8F" ], "title": "v0.97.2", - "notePosition": 120, + "notePosition": 130, "prefix": null, "isExpanded": false, "type": "text", @@ -381,7 +407,7 @@ "OtFZ6Nd9vM3n" ], "title": "v0.97.1", - "notePosition": 130, + "notePosition": 140, "prefix": null, "isExpanded": false, "type": "text", @@ -407,7 +433,7 @@ "SJZ5PwfzHSQ1" ], "title": "v0.97.0", - "notePosition": 140, + "notePosition": 150, "prefix": null, "isExpanded": false, "type": "text", @@ -433,7 +459,7 @@ "mYXFde3LuNR7" ], "title": "v0.96.0", - "notePosition": 150, + "notePosition": 160, "prefix": null, "isExpanded": false, "type": "text", @@ -459,7 +485,7 @@ "jthwbL0FdaeU" ], "title": "v0.95.0", - "notePosition": 160, + "notePosition": 170, "prefix": null, "isExpanded": false, "type": "text", @@ -485,7 +511,7 @@ "7HGYsJbLuhnv" ], "title": "v0.94.1", - "notePosition": 170, + "notePosition": 180, "prefix": null, "isExpanded": false, "type": "text", @@ -511,7 +537,7 @@ "Neq53ujRGBqv" ], "title": "v0.94.0", - "notePosition": 180, + "notePosition": 190, "prefix": null, "isExpanded": false, "type": "text", @@ -537,7 +563,7 @@ "VN3xnce1vLkX" ], "title": "v0.93.0", - "notePosition": 190, + "notePosition": 200, "prefix": null, "isExpanded": false, "type": "text", @@ -555,7 +581,7 @@ "WRaBfQqPr6qo" ], "title": "v0.92.7", - "notePosition": 200, + "notePosition": 210, "prefix": null, "isExpanded": false, "type": "text", @@ -581,7 +607,7 @@ "a2rwfKNmUFU1" ], "title": "v0.92.6", - "notePosition": 210, + "notePosition": 220, "prefix": null, "isExpanded": false, "type": "text", @@ -599,7 +625,7 @@ "fEJ8qErr0BKL" ], "title": "v0.92.5-beta", - "notePosition": 220, + "notePosition": 230, "prefix": null, "isExpanded": false, "type": "text", @@ -617,7 +643,7 @@ "kkkZQQGSXjwy" ], "title": "v0.92.4", - "notePosition": 230, + "notePosition": 240, "prefix": null, "isExpanded": false, "type": "text", @@ -635,7 +661,7 @@ "vAroNixiezaH" ], "title": "v0.92.3-beta", - "notePosition": 240, + "notePosition": 250, "prefix": null, "isExpanded": false, "type": "text", @@ -653,7 +679,7 @@ "mHEq1wxAKNZd" ], "title": "v0.92.2-beta", - "notePosition": 250, + "notePosition": 260, "prefix": null, "isExpanded": false, "type": "text", @@ -671,7 +697,7 @@ "IykjoAmBpc61" ], "title": "v0.92.1-beta", - "notePosition": 260, + "notePosition": 270, "prefix": null, "isExpanded": false, "type": "text", @@ -689,7 +715,7 @@ "dq2AJ9vSBX4Y" ], "title": "v0.92.0-beta", - "notePosition": 270, + "notePosition": 280, "prefix": null, "isExpanded": false, "type": "text", @@ -707,7 +733,7 @@ "3a8aMe4jz4yM" ], "title": "v0.91.6", - "notePosition": 280, + "notePosition": 290, "prefix": null, "isExpanded": false, "type": "text", @@ -725,7 +751,7 @@ "8djQjkiDGESe" ], "title": "v0.91.5", - "notePosition": 290, + "notePosition": 300, "prefix": null, "isExpanded": false, "type": "text", @@ -743,7 +769,7 @@ "OylxVoVJqNmr" ], "title": "v0.91.4-beta", - "notePosition": 300, + "notePosition": 310, "prefix": null, "isExpanded": false, "type": "text", @@ -761,7 +787,7 @@ "tANGQDvnyhrj" ], "title": "v0.91.3-beta", - "notePosition": 310, + "notePosition": 320, "prefix": null, "isExpanded": false, "type": "text", @@ -779,7 +805,7 @@ "hMoBfwSoj1SC" ], "title": "v0.91.2-beta", - "notePosition": 320, + "notePosition": 330, "prefix": null, "isExpanded": false, "type": "text", @@ -797,7 +823,7 @@ "a2XMSKROCl9z" ], "title": "v0.91.1-beta", - "notePosition": 330, + "notePosition": 340, "prefix": null, "isExpanded": false, "type": "text", @@ -815,7 +841,7 @@ "yqXFvWbLkuMD" ], "title": "v0.90.12", - "notePosition": 340, + "notePosition": 350, "prefix": null, "isExpanded": false, "type": "text", @@ -833,7 +859,7 @@ "veS7pg311yJP" ], "title": "v0.90.11-beta", - "notePosition": 350, + "notePosition": 360, "prefix": null, "isExpanded": false, "type": "text", @@ -851,7 +877,7 @@ "sq5W9TQxRqMq" ], "title": "v0.90.10-beta", - "notePosition": 360, + "notePosition": 370, "prefix": null, "isExpanded": false, "type": "text", @@ -869,7 +895,7 @@ "yFEGVCUM9tPx" ], "title": "v0.90.9-beta", - "notePosition": 370, + "notePosition": 380, "prefix": null, "isExpanded": false, "type": "text", @@ -887,7 +913,7 @@ "o4wAGqOQuJtV" ], "title": "v0.90.8", - "notePosition": 380, + "notePosition": 390, "prefix": null, "isExpanded": false, "type": "text", @@ -920,7 +946,7 @@ "i4A5g9iOg9I0" ], "title": "v0.90.7-beta", - "notePosition": 390, + "notePosition": 400, "prefix": null, "isExpanded": false, "type": "text", @@ -938,7 +964,7 @@ "ThNf2GaKgXUs" ], "title": "v0.90.6-beta", - "notePosition": 400, + "notePosition": 410, "prefix": null, "isExpanded": false, "type": "text", @@ -956,7 +982,7 @@ "G4PAi554kQUr" ], "title": "v0.90.5-beta", - "notePosition": 410, + "notePosition": 420, "prefix": null, "isExpanded": false, "type": "text", @@ -983,7 +1009,7 @@ "zATRobGRCmBn" ], "title": "v0.90.4", - "notePosition": 420, + "notePosition": 430, "prefix": null, "isExpanded": false, "type": "text", @@ -1001,7 +1027,7 @@ "sCDLf8IKn3Iz" ], "title": "v0.90.3", - "notePosition": 430, + "notePosition": 440, "prefix": null, "isExpanded": false, "type": "text", @@ -1019,7 +1045,7 @@ "VqqyBu4AuTjC" ], "title": "v0.90.2-beta", - "notePosition": 440, + "notePosition": 450, "prefix": null, "isExpanded": false, "type": "text", @@ -1037,7 +1063,7 @@ "RX3Nl7wInLsA" ], "title": "v0.90.1-beta", - "notePosition": 450, + "notePosition": 460, "prefix": null, "isExpanded": false, "type": "text", @@ -1055,7 +1081,7 @@ "GyueACukPWjk" ], "title": "v0.90.0-beta", - "notePosition": 460, + "notePosition": 470, "prefix": null, "isExpanded": false, "type": "text", @@ -1073,7 +1099,7 @@ "kzjHexDTTeVB" ], "title": "v0.48", - "notePosition": 470, + "notePosition": 480, "prefix": null, "isExpanded": false, "type": "text", @@ -1140,7 +1166,7 @@ "wyurrlcDl416" ], "title": "Release Template", - "notePosition": 480, + "notePosition": 490, "prefix": null, "isExpanded": false, "type": "text", diff --git a/docs/Release Notes/Release Notes/v0.101.2.md b/docs/Release Notes/Release Notes/v0.101.2.md new file mode 100644 index 000000000..6942b2e8e --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.101.2.md @@ -0,0 +1,22 @@ +# v0.101.2 +> [!NOTE] +> If you are interested in an [official mobile application](https://oss.issuehunt.io/r/TriliumNext/Trilium/issues/7447)  ([#7447](https://github.com/TriliumNext/Trilium/issues/7447)) or [multi-user support](https://oss.issuehunt.io/r/TriliumNext/Trilium/issues/4956) ([#4956](https://github.com/TriliumNext/Trilium/issues/4956)), consider offering financial support via IssueHunt (see links). + +> [!IMPORTANT] +> If you enjoyed this release, consider showing a token of appreciation by: +> +> * Pressing the “Star” button on [GitHub](https://github.com/TriliumNext/Trilium) (top-right). +> * Considering a one-time or recurrent donation to the [lead developer](https://github.com/eliandoran) via [GitHub Sponsors](https://github.com/sponsors/eliandoran) or [PayPal](https://paypal.me/eliandoran). + +## 🐞 Bugfixes + +* [SQL Console: cannot copy table data](https://github.com/TriliumNext/Trilium/pull/8268) by @SiriusXT +* [Title is not selected when creating a note via the launcher](https://github.com/TriliumNext/Trilium/pull/8292) by @SiriusXT +* [Popup editor closing after inserting a note link](https://github.com/TriliumNext/Trilium/pull/8224) by @SiriusXT +* [New Mermaid diagrams do not save content](https://github.com/TriliumNext/Trilium/pull/8220) by @lzinga +* [Can't scroll mermaid diagram code](https://github.com/TriliumNext/Trilium/issues/8299) +* [Max content width is not respected when switching between note types in the same tab](https://github.com/TriliumNext/Trilium/issues/8065) +* [Crash When a Note Includes Itself](https://github.com/TriliumNext/Trilium/issues/8294) +* [Severe Performance Degradation and Crash Issues Due to Recursive Inclusion in Included Notes](https://github.com/TriliumNext/Trilium/issues/8017) +* [ is not a launcher even though it's in the launcher subtree](https://github.com/TriliumNext/Trilium/issues/8218) +* [Archived subnotes of direct children appear in grid view without #includeArchived](https://github.com/TriliumNext/Trilium/issues/8184) \ No newline at end of file From 521952ebcc63df0b45bc91786557f412ccc77c77 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 18:10:00 +0200 Subject: [PATCH 20/68] test(client): remove debug statements --- apps/client/src/services/content_renderer_text.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/client/src/services/content_renderer_text.spec.ts b/apps/client/src/services/content_renderer_text.spec.ts index e9955d4b0..4199965cc 100644 --- a/apps/client/src/services/content_renderer_text.spec.ts +++ b/apps/client/src/services/content_renderer_text.spec.ts @@ -107,7 +107,6 @@ describe("Text content renderer", () => { ] }); await renderText(parentNote, $(contentEl)); - console.log(contentEl.innerHTML); const items = contentEl.querySelectorAll("a"); expect(items.length).toBe(2); expect(items[0].textContent).toBe("Child note 1"); @@ -125,7 +124,6 @@ describe("Text content renderer", () => { ] }); await renderText(parentNote, $(contentEl)); - console.log(contentEl.innerHTML); const items = contentEl.querySelectorAll("a"); expect(items.length).toBe(2); expect(items[0].textContent).toBe("Child note 1"); From f5e882271895cca46170dd1bb85be42518fd7841 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 19:35:35 +0200 Subject: [PATCH 21/68] chore(release): prepare for v0.101.3 --- apps/client/package.json | 2 +- apps/desktop/package.json | 2 +- apps/server/package.json | 2 +- docs/Release Notes/!!!meta.json | 126 +++++++++++-------- docs/Release Notes/Release Notes/v0.101.3.md | 24 ++++ package.json | 2 +- packages/commons/package.json | 2 +- 7 files changed, 105 insertions(+), 55 deletions(-) create mode 100644 docs/Release Notes/Release Notes/v0.101.3.md diff --git a/apps/client/package.json b/apps/client/package.json index 46701e0bd..6817b152f 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,6 +1,6 @@ { "name": "@triliumnext/client", - "version": "0.101.1", + "version": "0.101.3", "description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)", "private": true, "license": "AGPL-3.0-only", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 467c87910..00cc6e041 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@triliumnext/desktop", - "version": "0.101.1", + "version": "0.101.3", "description": "Build your personal knowledge base with Trilium Notes", "private": true, "main": "src/main.ts", diff --git a/apps/server/package.json b/apps/server/package.json index 4dcd1c561..63b3be91d 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "@triliumnext/server", - "version": "0.101.1", + "version": "0.101.3", "description": "The server-side component of TriliumNext, which exposes the client via the web, allows for sync and provides a REST API for both internal and external use.", "private": true, "main": "./src/main.ts", diff --git a/docs/Release Notes/!!!meta.json b/docs/Release Notes/!!!meta.json index dfe5da630..3c4265cdd 100644 --- a/docs/Release Notes/!!!meta.json +++ b/docs/Release Notes/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.101.1", + "appVersion": "0.101.3", "files": [ { "isClone": false, @@ -61,6 +61,32 @@ "attachments": [], "dirFileName": "Release Notes", "children": [ + { + "isClone": false, + "noteId": "IlBzLeN3MJhw", + "notePath": [ + "hD3V4hiu2VW4", + "IlBzLeN3MJhw" + ], + "title": "v0.101.3", + "notePosition": 10, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "relation", + "name": "template", + "value": "wyurrlcDl416", + "isInheritable": false, + "position": 60 + } + ], + "format": "markdown", + "dataFileName": "v0.101.3.md", + "attachments": [] + }, { "isClone": false, "noteId": "vcBthaXcwAm6", @@ -69,7 +95,7 @@ "vcBthaXcwAm6" ], "title": "v0.101.2", - "notePosition": 10, + "notePosition": 20, "prefix": null, "isExpanded": false, "type": "text", @@ -95,7 +121,7 @@ "AgUcrU9nFXuW" ], "title": "v0.101.1", - "notePosition": 20, + "notePosition": 30, "prefix": null, "isExpanded": false, "type": "text", @@ -121,7 +147,7 @@ "uYwlZ594eyJu" ], "title": "v0.101.0", - "notePosition": 30, + "notePosition": 40, "prefix": null, "isExpanded": false, "type": "text", @@ -147,7 +173,7 @@ "iPGKEk7pwJXK" ], "title": "v0.100.0", - "notePosition": 40, + "notePosition": 50, "prefix": null, "isExpanded": false, "type": "text", @@ -173,7 +199,7 @@ "7HKMTjmopLcM" ], "title": "v0.99.5", - "notePosition": 50, + "notePosition": 60, "prefix": null, "isExpanded": false, "type": "text", @@ -199,7 +225,7 @@ "RMBaNYPsRpIr" ], "title": "v0.99.4", - "notePosition": 60, + "notePosition": 70, "prefix": null, "isExpanded": false, "type": "text", @@ -225,7 +251,7 @@ "yuroLztFfpu5" ], "title": "v0.99.3", - "notePosition": 70, + "notePosition": 80, "prefix": null, "isExpanded": false, "type": "text", @@ -251,7 +277,7 @@ "z207sehwMJ6C" ], "title": "v0.99.2", - "notePosition": 80, + "notePosition": 90, "prefix": null, "isExpanded": false, "type": "text", @@ -277,7 +303,7 @@ "WGQsXq2jNyTi" ], "title": "v0.99.1", - "notePosition": 90, + "notePosition": 100, "prefix": null, "isExpanded": false, "type": "text", @@ -303,7 +329,7 @@ "cyw2Yue9vXf3" ], "title": "v0.99.0", - "notePosition": 100, + "notePosition": 110, "prefix": null, "isExpanded": false, "type": "text", @@ -329,7 +355,7 @@ "QOJwjruOUr4k" ], "title": "v0.98.1", - "notePosition": 110, + "notePosition": 120, "prefix": null, "isExpanded": false, "type": "text", @@ -355,7 +381,7 @@ "PLUoryywi0BC" ], "title": "v0.98.0", - "notePosition": 120, + "notePosition": 130, "prefix": null, "isExpanded": false, "type": "text", @@ -381,7 +407,7 @@ "lvOuiWsLDv8F" ], "title": "v0.97.2", - "notePosition": 130, + "notePosition": 140, "prefix": null, "isExpanded": false, "type": "text", @@ -407,7 +433,7 @@ "OtFZ6Nd9vM3n" ], "title": "v0.97.1", - "notePosition": 140, + "notePosition": 150, "prefix": null, "isExpanded": false, "type": "text", @@ -433,7 +459,7 @@ "SJZ5PwfzHSQ1" ], "title": "v0.97.0", - "notePosition": 150, + "notePosition": 160, "prefix": null, "isExpanded": false, "type": "text", @@ -459,7 +485,7 @@ "mYXFde3LuNR7" ], "title": "v0.96.0", - "notePosition": 160, + "notePosition": 170, "prefix": null, "isExpanded": false, "type": "text", @@ -485,7 +511,7 @@ "jthwbL0FdaeU" ], "title": "v0.95.0", - "notePosition": 170, + "notePosition": 180, "prefix": null, "isExpanded": false, "type": "text", @@ -511,7 +537,7 @@ "7HGYsJbLuhnv" ], "title": "v0.94.1", - "notePosition": 180, + "notePosition": 190, "prefix": null, "isExpanded": false, "type": "text", @@ -537,7 +563,7 @@ "Neq53ujRGBqv" ], "title": "v0.94.0", - "notePosition": 190, + "notePosition": 200, "prefix": null, "isExpanded": false, "type": "text", @@ -563,7 +589,7 @@ "VN3xnce1vLkX" ], "title": "v0.93.0", - "notePosition": 200, + "notePosition": 210, "prefix": null, "isExpanded": false, "type": "text", @@ -581,7 +607,7 @@ "WRaBfQqPr6qo" ], "title": "v0.92.7", - "notePosition": 210, + "notePosition": 220, "prefix": null, "isExpanded": false, "type": "text", @@ -607,7 +633,7 @@ "a2rwfKNmUFU1" ], "title": "v0.92.6", - "notePosition": 220, + "notePosition": 230, "prefix": null, "isExpanded": false, "type": "text", @@ -625,7 +651,7 @@ "fEJ8qErr0BKL" ], "title": "v0.92.5-beta", - "notePosition": 230, + "notePosition": 240, "prefix": null, "isExpanded": false, "type": "text", @@ -643,7 +669,7 @@ "kkkZQQGSXjwy" ], "title": "v0.92.4", - "notePosition": 240, + "notePosition": 250, "prefix": null, "isExpanded": false, "type": "text", @@ -661,7 +687,7 @@ "vAroNixiezaH" ], "title": "v0.92.3-beta", - "notePosition": 250, + "notePosition": 260, "prefix": null, "isExpanded": false, "type": "text", @@ -679,7 +705,7 @@ "mHEq1wxAKNZd" ], "title": "v0.92.2-beta", - "notePosition": 260, + "notePosition": 270, "prefix": null, "isExpanded": false, "type": "text", @@ -697,7 +723,7 @@ "IykjoAmBpc61" ], "title": "v0.92.1-beta", - "notePosition": 270, + "notePosition": 280, "prefix": null, "isExpanded": false, "type": "text", @@ -715,7 +741,7 @@ "dq2AJ9vSBX4Y" ], "title": "v0.92.0-beta", - "notePosition": 280, + "notePosition": 290, "prefix": null, "isExpanded": false, "type": "text", @@ -733,7 +759,7 @@ "3a8aMe4jz4yM" ], "title": "v0.91.6", - "notePosition": 290, + "notePosition": 300, "prefix": null, "isExpanded": false, "type": "text", @@ -751,7 +777,7 @@ "8djQjkiDGESe" ], "title": "v0.91.5", - "notePosition": 300, + "notePosition": 310, "prefix": null, "isExpanded": false, "type": "text", @@ -769,7 +795,7 @@ "OylxVoVJqNmr" ], "title": "v0.91.4-beta", - "notePosition": 310, + "notePosition": 320, "prefix": null, "isExpanded": false, "type": "text", @@ -787,7 +813,7 @@ "tANGQDvnyhrj" ], "title": "v0.91.3-beta", - "notePosition": 320, + "notePosition": 330, "prefix": null, "isExpanded": false, "type": "text", @@ -805,7 +831,7 @@ "hMoBfwSoj1SC" ], "title": "v0.91.2-beta", - "notePosition": 330, + "notePosition": 340, "prefix": null, "isExpanded": false, "type": "text", @@ -823,7 +849,7 @@ "a2XMSKROCl9z" ], "title": "v0.91.1-beta", - "notePosition": 340, + "notePosition": 350, "prefix": null, "isExpanded": false, "type": "text", @@ -841,7 +867,7 @@ "yqXFvWbLkuMD" ], "title": "v0.90.12", - "notePosition": 350, + "notePosition": 360, "prefix": null, "isExpanded": false, "type": "text", @@ -859,7 +885,7 @@ "veS7pg311yJP" ], "title": "v0.90.11-beta", - "notePosition": 360, + "notePosition": 370, "prefix": null, "isExpanded": false, "type": "text", @@ -877,7 +903,7 @@ "sq5W9TQxRqMq" ], "title": "v0.90.10-beta", - "notePosition": 370, + "notePosition": 380, "prefix": null, "isExpanded": false, "type": "text", @@ -895,7 +921,7 @@ "yFEGVCUM9tPx" ], "title": "v0.90.9-beta", - "notePosition": 380, + "notePosition": 390, "prefix": null, "isExpanded": false, "type": "text", @@ -913,7 +939,7 @@ "o4wAGqOQuJtV" ], "title": "v0.90.8", - "notePosition": 390, + "notePosition": 400, "prefix": null, "isExpanded": false, "type": "text", @@ -946,7 +972,7 @@ "i4A5g9iOg9I0" ], "title": "v0.90.7-beta", - "notePosition": 400, + "notePosition": 410, "prefix": null, "isExpanded": false, "type": "text", @@ -964,7 +990,7 @@ "ThNf2GaKgXUs" ], "title": "v0.90.6-beta", - "notePosition": 410, + "notePosition": 420, "prefix": null, "isExpanded": false, "type": "text", @@ -982,7 +1008,7 @@ "G4PAi554kQUr" ], "title": "v0.90.5-beta", - "notePosition": 420, + "notePosition": 430, "prefix": null, "isExpanded": false, "type": "text", @@ -1009,7 +1035,7 @@ "zATRobGRCmBn" ], "title": "v0.90.4", - "notePosition": 430, + "notePosition": 440, "prefix": null, "isExpanded": false, "type": "text", @@ -1027,7 +1053,7 @@ "sCDLf8IKn3Iz" ], "title": "v0.90.3", - "notePosition": 440, + "notePosition": 450, "prefix": null, "isExpanded": false, "type": "text", @@ -1045,7 +1071,7 @@ "VqqyBu4AuTjC" ], "title": "v0.90.2-beta", - "notePosition": 450, + "notePosition": 460, "prefix": null, "isExpanded": false, "type": "text", @@ -1063,7 +1089,7 @@ "RX3Nl7wInLsA" ], "title": "v0.90.1-beta", - "notePosition": 460, + "notePosition": 470, "prefix": null, "isExpanded": false, "type": "text", @@ -1081,7 +1107,7 @@ "GyueACukPWjk" ], "title": "v0.90.0-beta", - "notePosition": 470, + "notePosition": 480, "prefix": null, "isExpanded": false, "type": "text", @@ -1099,7 +1125,7 @@ "kzjHexDTTeVB" ], "title": "v0.48", - "notePosition": 480, + "notePosition": 490, "prefix": null, "isExpanded": false, "type": "text", @@ -1166,7 +1192,7 @@ "wyurrlcDl416" ], "title": "Release Template", - "notePosition": 490, + "notePosition": 500, "prefix": null, "isExpanded": false, "type": "text", diff --git a/docs/Release Notes/Release Notes/v0.101.3.md b/docs/Release Notes/Release Notes/v0.101.3.md new file mode 100644 index 000000000..243034045 --- /dev/null +++ b/docs/Release Notes/Release Notes/v0.101.3.md @@ -0,0 +1,24 @@ +# v0.101.3 +> [!NOTE] +> If you are interested in an [official mobile application](https://oss.issuehunt.io/r/TriliumNext/Trilium/issues/7447)  ([#7447](https://github.com/TriliumNext/Trilium/issues/7447)) or [multi-user support](https://oss.issuehunt.io/r/TriliumNext/Trilium/issues/4956) ([#4956](https://github.com/TriliumNext/Trilium/issues/4956)), consider offering financial support via IssueHunt (see links). + +> [!IMPORTANT] +> If you enjoyed this release, consider showing a token of appreciation by: +> +> * Pressing the “Star” button on [GitHub](https://github.com/TriliumNext/Trilium) (top-right). +> * Considering a one-time or recurrent donation to the [lead developer](https://github.com/eliandoran) via [GitHub Sponsors](https://github.com/sponsors/eliandoran) or [PayPal](https://paypal.me/eliandoran). + +This is a re-release of v0.101.2, which had a cache invalidation issue. + +## 🐞 Bugfixes + +* [SQL Console: cannot copy table data](https://github.com/TriliumNext/Trilium/pull/8268) by @SiriusXT +* [Title is not selected when creating a note via the launcher](https://github.com/TriliumNext/Trilium/pull/8292) by @SiriusXT +* [Popup editor closing after inserting a note link](https://github.com/TriliumNext/Trilium/pull/8224) by @SiriusXT +* [New Mermaid diagrams do not save content](https://github.com/TriliumNext/Trilium/pull/8220) by @lzinga +* [Can't scroll mermaid diagram code](https://github.com/TriliumNext/Trilium/issues/8299) +* [Max content width is not respected when switching between note types in the same tab](https://github.com/TriliumNext/Trilium/issues/8065) +* [Crash When a Note Includes Itself](https://github.com/TriliumNext/Trilium/issues/8294) +* [Severe Performance Degradation and Crash Issues Due to Recursive Inclusion in Included Notes](https://github.com/TriliumNext/Trilium/issues/8017) +* [is not a launcher even though it's in the launcher subtree](https://github.com/TriliumNext/Trilium/issues/8218) +* [Archived subnotes of direct children appear in grid view without #includeArchived](https://github.com/TriliumNext/Trilium/issues/8184) \ No newline at end of file diff --git a/package.json b/package.json index 537c05985..d3325cb9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@triliumnext/source", - "version": "0.101.1", + "version": "0.101.3", "description": "Build your personal knowledge base with Trilium Notes", "directories": { "doc": "docs" diff --git a/packages/commons/package.json b/packages/commons/package.json index bc854c279..0b30be811 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -1,6 +1,6 @@ { "name": "@triliumnext/commons", - "version": "0.101.1", + "version": "0.101.3", "description": "Shared library between the clients (e.g. browser, Electron) and the server, mostly for type definitions and utility methods.", "private": true, "type": "module", From 0b25b09040b6817f4bdda0d565054909b267156e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 19:49:29 +0200 Subject: [PATCH 22/68] feat(ci): check version consistency before releasing --- .github/workflows/release.yml | 8 +++++++ scripts/check-version-consistency.ts | 33 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 scripts/check-version-consistency.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 28d45245d..7d893979a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,6 +11,14 @@ concurrency: cancel-in-progress: true jobs: + sanity-check: + name: Sanity Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Check version consistency + run: pnpm tsx ${{ github.workspace }}/scripts/check-version-consistency.ts ${{ github.ref_name }} make-electron: name: Make Electron strategy: diff --git a/scripts/check-version-consistency.ts b/scripts/check-version-consistency.ts new file mode 100644 index 000000000..235167b2d --- /dev/null +++ b/scripts/check-version-consistency.ts @@ -0,0 +1,33 @@ +import { readFileSync } from "fs"; +import { join } from "path"; + +const projectRoot = join(__dirname, '..'); +const filesToCheck = [ + 'package.json', + 'apps/server/package.json', + 'apps/client/package.json', + 'apps/desktop/package.json', + 'packages/commons/package.json', +] + +function main() { + const expectedVersion = process.argv[2]; + if (!expectedVersion) { + console.error('Expected version argument is missing.'); + process.exit(1); + } + + for (const fileToCheck of filesToCheck) { + const packageJsonPath = join(projectRoot, fileToCheck); + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); + const version = packageJson.version; + if (version !== expectedVersion) { + console.error(`Version mismatch in ${fileToCheck}: expected ${expectedVersion}, found ${version}`); + process.exit(1); + } + } + + console.log('All versions are consistent:', expectedVersion); +} + +main(); From 66659d4786731535de2789a46eedbf702cb8675c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 20:11:21 +0200 Subject: [PATCH 23/68] e2e(server): flaky test in PDF --- apps/server-e2e/src/note_types/pdf.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/server-e2e/src/note_types/pdf.spec.ts b/apps/server-e2e/src/note_types/pdf.spec.ts index 4d5543fda..65bbeb1fb 100644 --- a/apps/server-e2e/src/note_types/pdf.spec.ts +++ b/apps/server-e2e/src/note_types/pdf.spec.ts @@ -73,13 +73,15 @@ test("Attachments listing works", async ({ page, context }) => { test("Download original PDF works", async ({ page, context }) => { const app = new App(page, context); await app.goto(); - await app.goToNoteInNewTab("Dacia Logan.pdf"); + await app.goToNoteInNewTab("Layers test.pdf"); const pdfHelper = new PdfHelper(app); await pdfHelper.toBeInitialized(); + const downloadButton = app.currentNoteSplit.locator(".icon-action.bx.bx-download"); + await expect(downloadButton).toBeVisible(); const [ download ] = await Promise.all([ page.waitForEvent("download"), - app.currentNoteSplit.locator(".icon-action.bx.bx-download").click() + downloadButton.click() ]); expect(download).toBeDefined(); }); @@ -125,5 +127,6 @@ class PdfHelper { async toBeInitialized() { await expect(this.contentFrame.locator("#pageNumber")).toBeVisible(); + await expect(this.contentFrame.locator(".page")).toBeVisible(); } } From fe3160e7a1b4e2eab328bfabaa96fb1bd84d2c78 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 20:22:50 +0200 Subject: [PATCH 24/68] e2e(server): adapt tests to new layout directly --- apps/server-e2e/src/note_types/pdf.spec.ts | 13 +++---------- apps/server-e2e/src/note_types/text.spec.ts | 7 ++++--- apps/server-e2e/src/support/app.ts | 2 +- apps/server/spec/db/document.db | Bin 8404992 -> 8404992 bytes 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/apps/server-e2e/src/note_types/pdf.spec.ts b/apps/server-e2e/src/note_types/pdf.spec.ts index 65bbeb1fb..d79ddb37e 100644 --- a/apps/server-e2e/src/note_types/pdf.spec.ts +++ b/apps/server-e2e/src/note_types/pdf.spec.ts @@ -1,12 +1,12 @@ -import test, { BrowserContext, expect, Page } from "@playwright/test"; +import test, { expect, Page } from "@playwright/test"; import App from "../support/app"; test.beforeEach(async ({ page, context }) => { - const app = await setLayout({ page, context }, true); + const app = new App(page, context); + await app.goto(); await app.setOption("rightPaneCollapsedItems", "[]"); }); -test.afterEach(async ({ page, context }) => await setLayout({ page, context }, false)); test("Table of contents works", async ({ page, context }) => { const app = new App(page, context); @@ -107,13 +107,6 @@ test("Layers listing works", async ({ page, context }) => { await expect(layersList.locator(".pdf-layer-item")).toHaveCount(0); }); -async function setLayout({ page, context}: { page: Page; context: BrowserContext }, newLayout: boolean) { - const app = new App(page, context); - await app.goto(); - await app.setOption("newLayout", newLayout ? "true" : "false"); - return app; -} - class PdfHelper { private contentFrame: ReturnType; diff --git a/apps/server-e2e/src/note_types/text.spec.ts b/apps/server-e2e/src/note_types/text.spec.ts index c99ad5d7d..25e45a551 100644 --- a/apps/server-e2e/src/note_types/text.spec.ts +++ b/apps/server-e2e/src/note_types/text.spec.ts @@ -1,4 +1,5 @@ -import { test, expect, Page } from "@playwright/test"; +import { expect, test } from "@playwright/test"; + import App from "../support/app"; test("Table of contents is displayed", async ({ page, context }) => { @@ -8,7 +9,7 @@ test("Table of contents is displayed", async ({ page, context }) => { await app.goToNoteInNewTab("Table of contents"); await expect(app.sidebar).toContainText("Table of Contents"); - const rootList = app.sidebar.locator(".toc-widget > span > ol"); + const rootList = app.sidebar.locator(".toc > ol"); // Heading 1.1 // Heading 1.1 @@ -42,7 +43,7 @@ test("Highlights list is displayed", async ({ page, context }) => { await app.closeAllTabs(); await app.goToNoteInNewTab("Highlights list"); - await expect(app.sidebar).toContainText("Highlights List"); + await expect(app.sidebar).toContainText("10 highlights"); const rootList = app.sidebar.locator(".highlights-list ol"); let index = 0; for (const highlightedEl of ["Bold 1", "Italic 1", "Underline 1", "Colored text 1", "Background text 1", "Bold 2", "Italic 2", "Underline 2", "Colored text 2", "Background text 2"]) { diff --git a/apps/server-e2e/src/support/app.ts b/apps/server-e2e/src/support/app.ts index 12097c656..9185a1918 100644 --- a/apps/server-e2e/src/support/app.ts +++ b/apps/server-e2e/src/support/app.ts @@ -37,7 +37,7 @@ export default class App { this.noteTreeHoistedNote = this.noteTree.locator(".fancytree-node", { has: page.locator(".unhoist-button") }); this.launcherBar = page.locator("#launcher-container"); this.currentNoteSplit = page.locator(".note-split:not(.hidden-ext)"); - this.currentNoteSplitTitle = this.currentNoteSplit.locator(".note-title"); + this.currentNoteSplitTitle = this.currentNoteSplit.locator(".note-title").first(); this.currentNoteSplitContent = this.currentNoteSplit.locator(".note-detail-printable.visible"); this.sidebar = page.locator("#right-pane"); } diff --git a/apps/server/spec/db/document.db b/apps/server/spec/db/document.db index c488fb1071205fcdb88a4ae6ab44be7fbec599f5..ad993226e079c01cfa121c0f83cb8c479a78efd9 100644 GIT binary patch delta 6602 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 delta 4517 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 NmRN^<->=`4|Gy$cyE^~? From ae4a3f10aeac7e21a1164c5a653f68a69fc7f114 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 8 Jan 2026 21:26:04 +0200 Subject: [PATCH 25/68] fix(toc): equations not rendered in new layout --- .../src/widgets/sidebar/TableOfContents.tsx | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/apps/client/src/widgets/sidebar/TableOfContents.tsx b/apps/client/src/widgets/sidebar/TableOfContents.tsx index c072c8599..b4414efe7 100644 --- a/apps/client/src/widgets/sidebar/TableOfContents.tsx +++ b/apps/client/src/widgets/sidebar/TableOfContents.tsx @@ -2,12 +2,14 @@ import "./TableOfContents.css"; import { CKTextEditor, ModelElement } from "@triliumnext/ckeditor5"; import clsx from "clsx"; -import { useCallback, useEffect, useState } from "preact/hooks"; +import { useCallback, useEffect, useRef, useState } from "preact/hooks"; import { t } from "../../services/i18n"; +import math from "../../services/math"; import { randomString } from "../../services/utils"; import { useActiveNoteContext, useContentElement, useGetContextData, useIsNoteReadOnly, useNoteProperty, useTextEditor } from "../react/hooks"; import Icon from "../react/Icon"; +import RawHtml from "../react/RawHtml"; import RightPanelWidget from "./RightPanelWidget"; //#region Generic impl. @@ -80,6 +82,22 @@ function TableOfContentsHeading({ heading, scrollToHeading, activeHeadingId }: { }) { const [ collapsed, setCollapsed ] = useState(false); const isActive = heading.id === activeHeadingId; + const contentRef = useRef(null); + + // Render math equations after component mounts/updates + useEffect(() => { + if (!contentRef.current) return; + const mathElements = contentRef.current.querySelectorAll(".ck-math-tex"); + + for (const mathEl of mathElements ?? []) { + try { + math.render(mathEl.textContent || "", mathEl as HTMLElement); + } catch (e) { + console.warn("Failed to render math in TOC:", e); + } + } + }, [heading.text]); + return ( <>
  • @@ -90,10 +108,12 @@ function TableOfContentsHeading({ heading, scrollToHeading, activeHeadingId }: { onClick={() => setCollapsed(!collapsed)} /> )} - scrollToHeading(heading)} - >{heading.text} + html={heading.text} + />
  • {heading.children && (
      @@ -189,9 +209,23 @@ function extractTocFromTextEditor(editor: CKTextEditor) { if (type !== "elementStart" || !item.is('element') || !item.name.startsWith('heading')) continue; const level = Number(item.name.replace( 'heading', '' )); - const text = Array.from( item.getChildren() ) - .map( c => c.is( '$text' ) ? c.data : '' ) - .join( '' ); + + // Convert model element to view, then to DOM to get HTML + const viewEl = editor.editing.mapper.toViewElement(item); + let text = ''; + if (viewEl) { + const domEl = editor.editing.view.domConverter.mapViewToDom(viewEl); + if (domEl instanceof HTMLElement) { + text = domEl.innerHTML; + } + } + + // Fallback to plain text if conversion fails + if (!text) { + text = Array.from( item.getChildren() ) + .map( c => c.is( '$text' ) ? c.data : '' ) + .join( '' ); + } // Assign a unique ID let tocId = item.getAttribute(TOC_ID) as string | undefined; From a54661fd0ac1bb43e8f179c20d3c46f86e383375 Mon Sep 17 00:00:00 2001 From: Ulices Date: Wed, 7 Jan 2026 22:29:18 +0100 Subject: [PATCH 26/68] Translated using Weblate (Spanish) Currently translated at 93.8% (1643 of 1751 strings) Translation: Trilium Notes/Client Translate-URL: https://hosted.weblate.org/projects/trilium/client/es/ --- .../src/translations/es/translation.json | 69 +++++++++++++++---- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json index 252cce17f..7e0ae3c69 100644 --- a/apps/client/src/translations/es/translation.json +++ b/apps/client/src/translations/es/translation.json @@ -21,7 +21,13 @@ }, "bundle-error": { "title": "Hubo un fallo al cargar un script personalizado", - "message": "El script de la nota con ID \"{{id}}\", titulado \"{{title}}\" no pudo ser ejecutado debido a:\n\n{{message}}" + "message": "El script no pudo ser ejecutado debido a:\n\n{{message}}" + }, + "widget-list-error": { + "title": "Hubo un fallo al obtener la lista de widgets del servidor" + }, + "widget-render-error": { + "title": "Hubo un fallo al renderizar un widget personalizado de React" } }, "add_link": { @@ -691,7 +697,7 @@ "convert_into_attachment_successful": "La nota '{{title}}' ha sido convertida a un archivo adjunto.", "convert_into_attachment_prompt": "¿Está seguro que desea convertir la nota '{{title}}' en un archivo adjunto de la nota padre?", "print_pdf": "Exportar como PDF...", - "open_note_on_server": "Abrir nota en el servidor" + "open_note_on_server": "Abrir nota en servidor" }, "onclick_button": { "no_click_handler": "El widget de botón '{{componentId}}' no tiene un controlador de clics definido" @@ -737,7 +743,7 @@ "zpetne_odkazy": { "relation": "relación", "backlink_one": "{{count}} Vínculo de retroceso", - "backlink_many": "", + "backlink_many": "{{count}} Vínculos de retroceso", "backlink_other": "{{count}} vínculos de retroceso" }, "mobile_detail_menu": { @@ -750,7 +756,10 @@ "note_icon": { "change_note_icon": "Cambiar icono de nota", "search": "Búsqueda:", - "reset-default": "Restablecer a icono por defecto" + "reset-default": "Restablecer a icono por defecto", + "search_placeholder_one": "Buscar {{number}} icono a través de {{count}} paquetes", + "search_placeholder_many": "Buscar {{number}} iconos a través de {{count}} paquetes", + "search_placeholder_other": "Buscar {{number}} iconos a través de {{count}} paquetes" }, "basic_properties": { "note_type": "Tipo de nota", @@ -790,7 +799,7 @@ "file_type": "Tipo de archivo", "file_size": "Tamaño del archivo", "download": "Descargar", - "open": "Abrir", + "open": "Abrir externamente", "upload_new_revision": "Subir nueva revisión", "upload_success": "Se ha subido una nueva revisión de archivo.", "upload_failed": "Error al cargar una nueva revisión de archivo.", @@ -1303,11 +1312,11 @@ "code_mime_types": { "title": "Tipos MIME disponibles en el menú desplegable", "tooltip_syntax_highlighting": "Resaltado de sintaxis", - "tooltip_code_block_syntax": "Bloques de código en notas de texto", - "tooltip_code_note_syntax": "Notas de código" + "tooltip_code_block_syntax": "Bloques de Código en notas de Texto", + "tooltip_code_note_syntax": "Notas de Código" }, "vim_key_bindings": { - "use_vim_keybindings_in_code_notes": "Atajos de teclas de Vim", + "use_vim_keybindings_in_code_notes": "Combinaciones de teclas Vim", "enable_vim_keybindings": "Habilitar los atajos de teclas de Vim en la notas de código (no es modo ex)" }, "wrap_lines": { @@ -1572,7 +1581,7 @@ "will_be_deleted_in": "Este archivo adjunto se eliminará automáticamente en {{time}}", "will_be_deleted_soon": "Este archivo adjunto se eliminará automáticamente pronto", "deletion_reason": ", porque el archivo adjunto no está vinculado en el contenido de la nota. Para evitar la eliminación, vuelva a agregar el enlace del archivo adjunto al contenido o convierta el archivo adjunto en una nota.", - "role_and_size": "Rol: {{role}}, Tamaño: {{size}}", + "role_and_size": "Rol: {{role}}, tamaño: {{size}}, MIME: {{- mimeType}}", "link_copied": "Enlace del archivo adjunto copiado al portapapeles.", "unrecognized_role": "Rol de archivo adjunto no reconocido '{{role}}'." }, @@ -1623,7 +1632,7 @@ "import-into-note": "Importar a nota", "apply-bulk-actions": "Aplicar acciones en lote", "converted-to-attachments": "{{count}} notas han sido convertidas en archivos adjuntos.", - "convert-to-attachment-confirm": "¿Está seguro que desea convertir las notas seleccionadas en archivos adjuntos de sus notas padres?", + "convert-to-attachment-confirm": "¿Está seguro que desea convertir las notas seleccionadas en archivos adjuntos de sus notas padres? Esta operación solo aplica a notas de Imagen, otras notas serán omitidas.", "open-in-popup": "Edición rápida", "archive": "Archivar", "unarchive": "Desarchivar" @@ -1718,7 +1727,10 @@ "note_detail": { "could_not_find_typewidget": "No se pudo encontrar typeWidget para el tipo '{{type}}'", "printing": "Impresión en curso...", - "printing_pdf": "Exportando a PDF en curso.." + "printing_pdf": "Exportando a PDF en curso..", + "print_report_collection_content_one": "{{count}} nota en la colección no se puede imprimir porque no son compatibles o está protegida.", + "print_report_collection_content_many": "{{count}} notas en la colección no se pueden imprimir porque no son compatibles o están protegidas.", + "print_report_collection_content_other": "{{count}} notas en la colección no se pueden imprimir porque no son compatibles o están protegidas." }, "note_title": { "placeholder": "escriba el título de la nota aquí..." @@ -1930,7 +1942,7 @@ "unknown_widget": "Widget desconocido para \"{{id}}\"." }, "note_language": { - "not_set": "No establecido", + "not_set": "Idioma no establecido", "configure-languages": "Configurar idiomas..." }, "content_language": { @@ -1969,7 +1981,7 @@ "hide-weekends": "Ocultar fines de semana", "show-scale": "Mostrar escala", "display-week-numbers": "Mostrar números de semana", - "map-style": "Estilo de mapa:", + "map-style": "Estilo de mapa", "max-nesting-depth": "Máxima profundidad de anidamiento:", "vector_light": "Vector (claro)", "vector_dark": "Vector (oscuro)", @@ -2098,5 +2110,36 @@ "clear-color": "Borrar color de nota", "set-color": "Asignar color de nota", "set-custom-color": "Asignar color de nota personalizado" + }, + "status_bar": { + "backlinks_one": "{{count}} vínculo de retroceso", + "backlinks_many": "{{count}} vínculos de retroceso", + "backlinks_other": "{{count}} vínculos de retroceso", + "backlinks_title_one": "Ver vínculo de retroceso", + "backlinks_title_many": "Ver vínculos de retroceso", + "backlinks_title_other": "Ver vínculos de retroceso", + "attachments_one": "{{count}} adjunto", + "attachments_many": "{{count}} adjuntos", + "attachments_other": "{{count}} adjuntos", + "attachments_title_one": "Ver adjunto en una nueva pestaña", + "attachments_title_many": "Ver adjuntos en una nueva pestaña", + "attachments_title_other": "Ver adjuntos en una nueva pestaña", + "attributes_one": "{{count}} atributo", + "attributes_many": "{{count}} atributos", + "attributes_other": "{{count}} atributos", + "note_paths_one": "{{count}} ruta", + "note_paths_many": "{{count}} rutas", + "note_paths_other": "{{count}} rutas" + }, + "pdf": { + "attachments_one": "{{count}} adjunto", + "attachments_many": "{{count}} adjuntos", + "attachments_other": "{{count}} adjuntos", + "layers_one": "{{count}} capa", + "layers_many": "{{count}} capas", + "layers_other": "{{count}} capas", + "pages_one": "{{count}} página", + "pages_many": "{{count}} páginas", + "pages_other": "{{count}} páginas" } } From 1481356d1fb93fd28c7cc8832d4065a8521b9788 Mon Sep 17 00:00:00 2001 From: Yatrik Patel Date: Thu, 8 Jan 2026 05:32:30 +0100 Subject: [PATCH 27/68] Translated using Weblate (Hindi) Currently translated at 36.8% (56 of 152 strings) Translation: Trilium Notes/Website Translate-URL: https://hosted.weblate.org/projects/trilium/website/hi/ --- .../src/translations/hi/translation.json | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/apps/website/src/translations/hi/translation.json b/apps/website/src/translations/hi/translation.json index 00e6ce62d..d67f0d026 100644 --- a/apps/website/src/translations/hi/translation.json +++ b/apps/website/src/translations/hi/translation.json @@ -6,7 +6,9 @@ }, "hero_section": { "title": "अपने विचारों को व्यवस्थित करें। अपना व्यक्तिगत नॉलेज बेस बनाएं।", - "screenshot_alt": "ट्रिलियम नोट्स डेस्कटॉप एप्लिकेशन का स्क्रीनशॉट" + "screenshot_alt": "ट्रिलियम नोट्स डेस्कटॉप एप्लिकेशन का स्क्रीनशॉट", + "get_started": "शुरू करें", + "github": "गिटहब" }, "organization_benefits": { "note_structure_title": "नोट संरचना", @@ -26,5 +28,67 @@ "collections": { "calendar_title": "कैलेंडर", "table_title": "टेबल" + }, + "download_now": { + "linux_small": "लिनक्स के लिए", + "more_platforms": "अधिक प्लेटफॉर्म और सर्वर सेटअप" + }, + "header": { + "get-started": "शुरू करें", + "support-us": "हमें सपोर्ट करें" + }, + "social_buttons": { + "github": "गिटहब", + "matrix": "मैट्रिक्स", + "reddit": "रेडिट" + }, + "support_us": { + "title": "हमें सपोर्ट करें" + }, + "404": { + "description": "आप जो पेज खोज रहे थे वह नहीं मिल पाया। शायद वह डिलीट हो चुका है या यूआरएल (URL) गलत है।" + }, + "download_helper_desktop_windows": { + "title_x64": "Windows 64-bit", + "title_arm64": "ARM पर Windows", + "quick_start": "Winget द्वारा इंस्टॉल करने के लिए:", + "download_exe": "इंस्टॉलर (.exe) डाउनलोड करें", + "download_zip": "पोर्टेबल (.zip)" + }, + "download_helper_desktop_linux": { + "title_x64": "Linux 64-bit", + "title_arm64": "ARM पर लिनक्स", + "download_deb": ".deb", + "download_rpm": ".rpm", + "download_flatpak": ".flatpak", + "download_zip": "पोर्टेबल (.zip)", + "download_nixpkgs": "nixpkgs" + }, + "download_helper_desktop_macos": { + "title_x64": "Intel के लिए macOS", + "title_arm64": "Apple Silicon के लिए macOS", + "description_x64": "macOS Monterey या उसके बाद के वर्ज़न पर चलने वाले Intel-आधारित Macs के लिए।", + "description_arm64": "Apple Silicon Macs के लिए, जैसे कि M1 और M2 चिप्स वाले मॉडल।", + "quick_start": "Homebrew द्वारा इंस्टॉल करने के लिए:", + "download_dmg": "इंस्टॉलर (.dmg) डाउनलोड करें", + "download_homebrew_cask": "Homebrew Cask", + "download_zip": "पोर्टेबल (.zip)" + }, + "download_helper_server_docker": { + "title": "Docker द्वारा सेल्फ-होस्टेड", + "description": "Docker कंटेनर का उपयोग करके Windows, Linux या macOS पर आसानी से डिप्लॉय करें।", + "download_ghcr": "ghcr.io" + }, + "download_helper_server_linux": { + "title": "Linux पर सेल्फ-होस्टेड", + "description": "ट्रिलियम नोट्स को अपने खुद के सर्वर या VPS पर डिप्लॉय करें, जो अधिकांश डिस्ट्रीब्यूशनो के साथ कम्पेटिबल है।", + "download_tar_x64": "x64 (.tar.xz)", + "download_tar_arm64": "ARM (.tar.xz)", + "download_nixos": "NixOS मॉड्यूल" + }, + "download_helper_server_hosted": { + "title": "पेड होस्टिंग", + "download_pikapod": "PikaPods पर सेटअप करें", + "download_triliumcc": "वैकल्पिक रूप से trilium.cc देखें" } } From d31c6b16277b225700daa45f7dc71195cb330137 Mon Sep 17 00:00:00 2001 From: Ulices Date: Wed, 7 Jan 2026 22:17:27 +0100 Subject: [PATCH 28/68] Translated using Weblate (Spanish) Currently translated at 100.0% (152 of 152 strings) Translation: Trilium Notes/Website Translate-URL: https://hosted.weblate.org/projects/trilium/website/es/ --- apps/website/src/translations/es/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/website/src/translations/es/translation.json b/apps/website/src/translations/es/translation.json index 112ba4ded..462078049 100644 --- a/apps/website/src/translations/es/translation.json +++ b/apps/website/src/translations/es/translation.json @@ -115,7 +115,7 @@ }, "social_buttons": { "github": "GitHub", - "github_discussions": "GitHub Discussions", + "github_discussions": "Discusiones de GitHub", "matrix": "Matrix", "reddit": "Reddit" }, From 8cff5917463df90b68958ef423ec0b26af90b20d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 11:25:24 +0200 Subject: [PATCH 29/68] fix(toc): unnecessary
        when no children --- apps/client/src/widgets/sidebar/TableOfContents.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/sidebar/TableOfContents.tsx b/apps/client/src/widgets/sidebar/TableOfContents.tsx index b4414efe7..483ddb179 100644 --- a/apps/client/src/widgets/sidebar/TableOfContents.tsx +++ b/apps/client/src/widgets/sidebar/TableOfContents.tsx @@ -115,7 +115,7 @@ function TableOfContentsHeading({ heading, scrollToHeading, activeHeadingId }: { html={heading.text} /> - {heading.children && ( + {heading.children.length > 0 && (
          {heading.children.map(heading => )}
        From e469af1ca54f33acc628c0e6198f00e4ff8c6d53 Mon Sep 17 00:00:00 2001 From: contributor Date: Fri, 9 Jan 2026 11:35:08 +0200 Subject: [PATCH 30/68] add logseq to supported protocols --- packages/commons/src/lib/shared_constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/commons/src/lib/shared_constants.ts b/packages/commons/src/lib/shared_constants.ts index bf13c92d3..c88044162 100644 --- a/packages/commons/src/lib/shared_constants.ts +++ b/packages/commons/src/lib/shared_constants.ts @@ -18,5 +18,5 @@ export const ALLOWED_PROTOCOLS = [ 'gopher', 'imap', 'irc', 'irc6', 'jabber', 'jar', 'lastfm', 'ldap', 'ldaps', 'magnet', 'message', 'mumble', 'nfs', 'onenote', 'pop', 'rmi', 's3', 'sftp', 'skype', 'sms', 'spotify', 'steam', 'svn', 'udp', 'view-source', 'vlc', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack', 'tel', 'smb', 'zotero', 'geo', - 'mid', 'obsidian' + 'logseq', 'mid', 'obsidian' ]; From 6b57ee56548121b867d3f3e7a3ca6b8c0c56c425 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 11:54:16 +0200 Subject: [PATCH 31/68] e2e(server): flakiness in tab_bar --- apps/server-e2e/src/layout/tab_bar.spec.ts | 15 ++++++++------- apps/server-e2e/src/support/app.ts | 8 +++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/server-e2e/src/layout/tab_bar.spec.ts b/apps/server-e2e/src/layout/tab_bar.spec.ts index 90e17afef..791064966 100644 --- a/apps/server-e2e/src/layout/tab_bar.spec.ts +++ b/apps/server-e2e/src/layout/tab_bar.spec.ts @@ -1,4 +1,5 @@ -import { test, expect } from "@playwright/test"; +import { expect,test } from "@playwright/test"; + import App from "../support/app"; const NOTE_TITLE = "Trilium Integration Test DB"; @@ -65,21 +66,21 @@ test("Tabs are restored in right order", async ({ page, context }) => { // Open three tabs. await app.closeAllTabs(); await app.goToNoteInNewTab("Code notes"); + await expect(app.getActiveTab()).toContainText("Code notes"); await app.addNewTab(); await app.goToNoteInNewTab("Text notes"); + await expect(app.getActiveTab()).toContainText("Text notes"); await app.addNewTab(); await app.goToNoteInNewTab("Mermaid"); + await expect(app.getActiveTab()).toContainText("Mermaid"); // Select the mid one. await app.getTab(1).click(); await expect(app.noteTreeActiveNote).toContainText("Text notes"); - await expect(app.getTab(0)).toContainText("Code notes"); - await expect(app.getTab(1)).toContainText("Text notes"); - await expect(app.getTab(2)).toContainText("Mermaid"); // Refresh the page and check the order. await app.goto( { preserveTabs: true }); - await expect(app.getTab(0)).toContainText("Code notes", { timeout: 15_000 }); + await expect(app.getTab(0)).toContainText("Code notes"); await expect(app.getTab(1)).toContainText("Text notes"); await expect(app.getTab(2)).toContainText("Mermaid"); @@ -128,8 +129,8 @@ test("New tab displays workspaces", async ({ page, context }) => { const workspaceNotesEl = app.currentNoteSplitContent.locator(".workspace-notes"); await expect(workspaceNotesEl).toBeVisible(); - expect(workspaceNotesEl).toContainText("Personal"); - expect(workspaceNotesEl).toContainText("Work"); + await expect(workspaceNotesEl).toContainText("Personal"); + await expect(workspaceNotesEl).toContainText("Work"); await expect(workspaceNotesEl.locator(".bx.bxs-user")).toBeVisible(); await expect(workspaceNotesEl.locator(".bx.bx-briefcase-alt")).toBeVisible(); diff --git a/apps/server-e2e/src/support/app.ts b/apps/server-e2e/src/support/app.ts index 9185a1918..86cbb855a 100644 --- a/apps/server-e2e/src/support/app.ts +++ b/apps/server-e2e/src/support/app.ts @@ -68,13 +68,15 @@ export default class App { async goToNoteInNewTab(noteTitle: string) { const autocomplete = this.currentNoteSplit.locator(".note-autocomplete"); + await expect(autocomplete).toBeVisible(); await autocomplete.fill(noteTitle); const resultsSelector = this.currentNoteSplit.locator(".note-detail-empty-results"); await expect(resultsSelector).toContainText(noteTitle); - await resultsSelector.locator(".aa-suggestion", { hasText: noteTitle }) - .nth(1) // Select the second one, as the first one is "Create a new note" - .click(); + const suggestionSelector = resultsSelector.locator(".aa-suggestion") + .nth(1); // Select the second one (best candidate), as the first one is "Create a new note" + await expect(suggestionSelector).toContainText(noteTitle); + suggestionSelector.click(); } async goToSettings() { From ed972d26015d605fc2c9c97ac4b94d02d08499b6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 12:13:39 +0200 Subject: [PATCH 32/68] e2e(server): math popup fails on CI --- apps/server-e2e/src/note_types/text.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/server-e2e/src/note_types/text.spec.ts b/apps/server-e2e/src/note_types/text.spec.ts index 25e45a551..052745ee2 100644 --- a/apps/server-e2e/src/note_types/text.spec.ts +++ b/apps/server-e2e/src/note_types/text.spec.ts @@ -65,6 +65,8 @@ test("Displays math popup", async ({ page, context }) => { await expect(mathForm).toBeVisible(); const input = mathForm.locator(".ck-input").first(); + await expect(input).toBeVisible(); + await expect(input).toBeEnabled(); await input.click(); await input.fill("e=mc^2"); await page.waitForTimeout(100); From 4f6c10d9959fff45718f226acfe5f1f286eb286b Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 16:32:55 +0200 Subject: [PATCH 33/68] feat(tree): allow hiding child notes via attribute --- apps/client/src/widgets/note_tree.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 9a2c61d22..a1f477993 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -803,6 +803,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { childBranches = childBranches.slice(0, MAX_SEARCH_RESULTS_IN_TREE); } + if (parentNote.hasLabel("subtreeHidden")) { + childBranches = []; + } + for (const branch of childBranches) { if (hideArchivedNotes) { const note = branch.getNoteFromCache(); From afefbe154be18bd83c0e97e64735bfc95f53302e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 16:37:11 +0200 Subject: [PATCH 34/68] feat(tree): hide arrow if children are hidden --- apps/client/src/entities/fnote.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts index 048d175db..644ea2228 100644 --- a/apps/client/src/entities/fnote.ts +++ b/apps/client/src/entities/fnote.ts @@ -616,7 +616,9 @@ export default class FNote { } isFolder() { - return this.type === "search" || this.getFilteredChildBranches().length > 0; + if (this.hasLabel("subtreeHidden")) return false; + if (this.type === "search") return true; + return this.getFilteredChildBranches().length > 0; } getFilteredChildBranches() { From 92e6a29e70f2e09ab89362e9b21eebe12c06513c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 16:54:04 +0200 Subject: [PATCH 35/68] feat(tree): display number of children if subtree is hidden --- apps/client/src/widgets/note_tree.css | 10 ++++++++++ apps/client/src/widgets/note_tree.ts | 8 +++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 apps/client/src/widgets/note_tree.css diff --git a/apps/client/src/widgets/note_tree.css b/apps/client/src/widgets/note_tree.css new file mode 100644 index 000000000..31e2b0142 --- /dev/null +++ b/apps/client/src/widgets/note_tree.css @@ -0,0 +1,10 @@ +.note-indicator-icon.subtree-hidden-badge { + font-family: inherit !important; + margin-inline-start: 0.5em; + background: var(--left-pane-item-action-button-background); + color: var(--left-pane-item-action-button-color); + padding: 0.25em; + border-radius: 0.5em; + font-size: 0.7rem; + font-weight: normal; +} diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 24e02286c..45c0a98c6 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -3,13 +3,13 @@ import "jquery.fancytree/dist/modules/jquery.fancytree.dnd5.js"; import "jquery.fancytree/dist/modules/jquery.fancytree.clones.js"; import "jquery.fancytree/dist/modules/jquery.fancytree.filter.js"; import "../stylesheets/tree.css"; +import "./note_tree.css"; import appContext, { type CommandListenerData, type EventData } from "../components/app_context.js"; import type { SetNoteOpts } from "../components/note_context.js"; import type { TouchBarItem } from "../components/touch_bar.js"; import type FBranch from "../entities/fbranch.js"; import type FNote from "../entities/fnote.js"; -import type { NoteType } from "../entities/fnote.js"; import contextMenu from "../menus/context_menu.js"; import type { TreeCommandNames } from "../menus/tree_context_menu.js"; import branchService from "../services/branches.js"; @@ -1887,5 +1887,11 @@ function buildEnhanceTitle() { $sharedIndicator.attr("title", tooltipText); $span.find(".fancytree-title").append($sharedIndicator); } + + // Add a badge with the number of items if it hides children. + if (note.hasLabel("subtreeHidden")) { + const $badge = $(`${note.getChildNoteIds().length}`); + $span.find(".fancytree-title").append($badge); + } }; } From b6a6e78d0166b838618f8622936b6784ce801823 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 16:59:55 +0200 Subject: [PATCH 36/68] feat(tree): hide add button if subtree is hidden --- apps/client/src/widgets/note_tree.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 45c0a98c6..29ddd1070 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1843,10 +1843,12 @@ function buildEnhanceTitle() { } // TODO: Deduplicate with server's notes.ts#getAndValidateParent + const isSubtreeHidden = note.hasLabel("subtreeHidden"); if (!["search", "launcher"].includes(note.type) && !note.isOptions() && !note.isLaunchBarConfig() && !note.noteId.startsWith("_help") + && !isSubtreeHidden ) { node.span.append(createChildTemplate.cloneNode()); } @@ -1889,7 +1891,7 @@ function buildEnhanceTitle() { } // Add a badge with the number of items if it hides children. - if (note.hasLabel("subtreeHidden")) { + if (isSubtreeHidden) { const $badge = $(`${note.getChildNoteIds().length}`); $span.find(".fancytree-title").append($badge); } From 0c27bd25fa6ce369f3f112599b4aa1acda8756a8 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 17:03:56 +0200 Subject: [PATCH 37/68] chore(tree): align child count to the right --- apps/client/src/widgets/note_tree.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/note_tree.css b/apps/client/src/widgets/note_tree.css index 31e2b0142..4ab73d6fe 100644 --- a/apps/client/src/widgets/note_tree.css +++ b/apps/client/src/widgets/note_tree.css @@ -3,8 +3,12 @@ margin-inline-start: 0.5em; background: var(--left-pane-item-action-button-background); color: var(--left-pane-item-action-button-color); - padding: 0.25em; + padding: 0.2em 0.4em; border-radius: 0.5em; font-size: 0.7rem; font-weight: normal; + position: absolute; + top: 50%; + right: 0.6em; + transform: translateY(-50%); } From ee52e16a75d4371b8511c5b2a40f9ecddee0261c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 19:15:57 +0200 Subject: [PATCH 38/68] feat(tree): add title for subnote count badge --- apps/client/src/translations/en/translation.json | 3 ++- apps/client/src/widgets/note_tree.ts | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 457f01eb6..33e69ba0f 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1772,7 +1772,8 @@ "clone-indicator-tooltip": "This note has {{- count}} parents: {{- parents}}", "clone-indicator-tooltip-single": "This note is cloned (1 additional parent: {{- parent}})", "shared-indicator-tooltip": "This note is shared publicly", - "shared-indicator-tooltip-with-url": "This note is shared publicly at: {{- url}}" + "shared-indicator-tooltip-with-url": "This note is shared publicly at: {{- url}}", + "subtree-hidden-tooltip": "{{count}} child notes that are hidden from the tree" }, "title_bar_buttons": { "window-on-top": "Keep Window on Top" diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 29ddd1070..470f19dd0 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1891,8 +1891,10 @@ function buildEnhanceTitle() { } // Add a badge with the number of items if it hides children. - if (isSubtreeHidden) { - const $badge = $(`${note.getChildNoteIds().length}`); + const count = note.getChildNoteIds().length; + if (isSubtreeHidden && count > 0) { + const $badge = $(`${count}`); + $badge.attr("title", t("note_tree.subtree-hidden-tooltip", { count })); $span.find(".fancytree-title").append($badge); } }; From 211d2dcf99caef2827c0ed9f574441024d91e862 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 9 Jan 2026 20:06:49 +0200 Subject: [PATCH 39/68] feat(collections): hide children by default for some collection types --- apps/server/src/services/hidden_subtree_templates.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/server/src/services/hidden_subtree_templates.ts b/apps/server/src/services/hidden_subtree_templates.ts index 7f5710776..55999824c 100644 --- a/apps/server/src/services/hidden_subtree_templates.ts +++ b/apps/server/src/services/hidden_subtree_templates.ts @@ -1,7 +1,12 @@ -import { HiddenSubtreeItem } from "@triliumnext/commons"; +import { HiddenSubtreeAttribute, HiddenSubtreeItem } from "@triliumnext/commons"; import { t } from "i18next"; export default function buildHiddenSubtreeTemplates() { + const hideSubtreeAttributes: HiddenSubtreeAttribute = { + name: "subtreeHidden", + type: "label" + }; + const templates: HiddenSubtreeItem = { id: "_templates", title: t("hidden_subtree_templates.built-in-templates"), @@ -93,6 +98,7 @@ export default function buildHiddenSubtreeTemplates() { name: "hidePromotedAttributes", type: "label" }, + hideSubtreeAttributes, { name: "label:startDate", type: "label", @@ -133,6 +139,7 @@ export default function buildHiddenSubtreeTemplates() { name: "collection", type: "label" }, + hideSubtreeAttributes, { name: "viewType", type: "label", @@ -163,6 +170,7 @@ export default function buildHiddenSubtreeTemplates() { name: "hidePromotedAttributes", type: "label" }, + hideSubtreeAttributes, { name: "label:geolocation", type: "label", @@ -194,6 +202,7 @@ export default function buildHiddenSubtreeTemplates() { name: "hidePromotedAttributes", type: "label" }, + hideSubtreeAttributes, { name: "label:status", type: "label", From b67ccc60918050c507d4111d1ae353cf646570ed Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 00:15:59 +0200 Subject: [PATCH 40/68] feat(tree): basic spotlight support for hidden child --- apps/client/src/widgets/note_tree.ts | 61 +++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 470f19dd0..5bc0b126c 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -201,6 +201,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { private treeName: "main"; private autoCollapseTimeoutId?: Timeout; private lastFilteredHoistedNotePath?: string | null; + private spotlightedNotePath?: string | null; private tree!: Fancytree.Fancytree; constructor() { @@ -710,7 +711,20 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } if (parentNote.hasLabel("subtreeHidden")) { - childBranches = []; + // If we have a spotlighted note path, show only the child that leads to it + if (this.spotlightedNotePath) { + const spotlightPathSegments = this.spotlightedNotePath.split('/'); + const parentIndex = spotlightPathSegments.indexOf(parentNote.noteId); + + if (parentIndex >= 0 && parentIndex < spotlightPathSegments.length - 1) { + const nextNoteIdInPath = spotlightPathSegments[parentIndex + 1]; + childBranches = childBranches.filter(branch => branch.noteId === nextNoteIdInPath); + } else { + childBranches = []; + } + } else { + childBranches = []; + } } for (const branch of childBranches) { @@ -992,18 +1006,42 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { foundChildNode = this.findChildNode(parentNode, childNoteId); if (!foundChildNode) { - if (logErrors) { - // besides real errors, this can be also caused by hiding of e.g. included images - // these are real notes with real notePath, user can display them in a detail, - // but they don't have a node in the tree + const childNote = await froca.getNote(childNoteId); - const childNote = await froca.getNote(childNoteId); + if (childNote?.type === "image") return; - if (!childNote || childNote.type !== "image") { - ws.logError( - `Can't find node for child node of noteId=${childNoteId} for parent of noteId=${parentNode.data.noteId} and hoistedNoteId=${hoistedNoteService.getHoistedNoteId()}, requested path is ${notePath}` - ); + // The child note can be part of a note with #subtreeHidden, case in which we need to "spotlight" it. + const parentNote = froca.getNoteFromCache(parentNode.data.noteId); + if (parentNote?.hasLabel("subtreeHidden")) { + // Enable spotlight mode and reload the parent to show only the path to this note + this.spotlightedNotePath = notePath; + await parentNode.load(true); + + // Try to find the child again after reload + foundChildNode = this.findChildNode(parentNode, childNoteId); + + if (!foundChildNode) { + if (logErrors || !childNote) { + ws.logError( + `Can't find node for child node of noteId=${childNoteId} for parent of noteId=${parentNode.data.noteId} and hoistedNoteId=${hoistedNoteService.getHoistedNoteId()}, requested path is ${notePath}` + ); + return; + } + return; } + + parentNode = foundChildNode; + continue; + } + + // besides real errors, this can be also caused by hiding of e.g. included images + // these are real notes with real notePath, user can display them in a detail, + // but they don't have a node in the tree + if (logErrors || !childNote) { + ws.logError( + `Can't find node for child node of noteId=${childNoteId} for parent of noteId=${parentNode.data.noteId} and hoistedNoteId=${hoistedNoteService.getHoistedNoteId()}, requested path is ${notePath}` + ); + return; } return; @@ -1050,6 +1088,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { this.toggleInt(this.isEnabled()); this.$treeSettingsPopup.hide(); + // Clear spotlight before refresh + this.spotlightedNotePath = null; + this.activityDetected(); const oldActiveNode = this.getActiveNode(); From 62996b11628ceffad9730597b7eeeab47db88234 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 00:38:28 +0200 Subject: [PATCH 41/68] feat(tree): remove spotlighted note after switching to another one --- apps/client/src/widgets/note_tree.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 5bc0b126c..8b43312f1 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -202,6 +202,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { private autoCollapseTimeoutId?: Timeout; private lastFilteredHoistedNotePath?: string | null; private spotlightedNotePath?: string | null; + private spotlightedNode: Fancytree.FancytreeNode | null = null; private tree!: Fancytree.Fancytree; constructor() { @@ -1019,6 +1020,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { // Try to find the child again after reload foundChildNode = this.findChildNode(parentNode, childNoteId); + this.spotlightedNode = foundChildNode ?? null; if (!foundChildNode) { if (logErrors || !childNote) { @@ -1088,9 +1090,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { this.toggleInt(this.isEnabled()); this.$treeSettingsPopup.hide(); - // Clear spotlight before refresh - this.spotlightedNotePath = null; - this.activityDetected(); const oldActiveNode = this.getActiveNode(); @@ -1100,12 +1099,23 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { (!treeService.isNotePathInHiddenSubtree(this.noteContext.notePath) || (await hoistedNoteService.isHoistedInHiddenSubtree())) && (await this.getNodeFromPath(this.noteContext.notePath)); + if (this.spotlightedNode && newActiveNode !== this.spotlightedNode) { + this.spotlightedNode.remove(); + this.spotlightedNode = null; + this.spotlightedNotePath = null; + } + if (newActiveNode !== oldActiveNode) { let oldActiveNodeFocused = false; if (oldActiveNode) { oldActiveNodeFocused = oldActiveNode.hasFocus(); + if (this.spotlightedNode === oldActiveNode) { + this.spotlightedNode.remove(); + this.spotlightedNode = null; + this.spotlightedNotePath = null; + } oldActiveNode.setActive(false); oldActiveNode.setFocus(false); } From 0b40b4231508e614995d3a0e023b7506f3277383 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 00:42:24 +0200 Subject: [PATCH 42/68] fix(tree): random exceptions when switching between two spotlighted notes --- apps/client/src/widgets/note_tree.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 8b43312f1..de73efeaa 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1058,7 +1058,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } findChildNode(parentNode: Fancytree.FancytreeNode, childNoteId: string) { - return parentNode.getChildren().find((childNode) => childNode.data.noteId === childNoteId); + return parentNode.getChildren()?.find((childNode) => childNode.data.noteId === childNoteId); } async expandToNote(notePath: string, logErrors = true) { @@ -1100,7 +1100,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { (await this.getNodeFromPath(this.noteContext.notePath)); if (this.spotlightedNode && newActiveNode !== this.spotlightedNode) { - this.spotlightedNode.remove(); + // Can get removed when switching to another note in a spotlighted subtree. + if (this.spotlightedNode.parent) { + this.spotlightedNode.remove(); + } this.spotlightedNode = null; this.spotlightedNotePath = null; } From 712e87b39f21ecd6cec6118cb0db6fd24a63622d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 00:52:11 +0200 Subject: [PATCH 43/68] feat(tree): distinguish spotlighted node --- apps/client/src/widgets/note_tree.css | 33 ++++++++++++++++----------- apps/client/src/widgets/note_tree.ts | 4 ++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/apps/client/src/widgets/note_tree.css b/apps/client/src/widgets/note_tree.css index 4ab73d6fe..6e3b90dca 100644 --- a/apps/client/src/widgets/note_tree.css +++ b/apps/client/src/widgets/note_tree.css @@ -1,14 +1,21 @@ -.note-indicator-icon.subtree-hidden-badge { - font-family: inherit !important; - margin-inline-start: 0.5em; - background: var(--left-pane-item-action-button-background); - color: var(--left-pane-item-action-button-color); - padding: 0.2em 0.4em; - border-radius: 0.5em; - font-size: 0.7rem; - font-weight: normal; - position: absolute; - top: 50%; - right: 0.6em; - transform: translateY(-50%); +#left-pane .tree-wrapper { + .note-indicator-icon.subtree-hidden-badge { + font-family: inherit !important; + margin-inline-start: 0.5em; + background: var(--left-pane-item-action-button-background); + color: var(--left-pane-item-action-button-color); + padding: 0.2em 0.4em; + border-radius: 0.5em; + font-size: 0.7rem; + font-weight: normal; + position: absolute; + top: 50%; + right: 0.6em; + transform: translateY(-50%); + } + + .spotlighted-node { + opacity: 0.8; + font-style: italic; + } } diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index de73efeaa..1e4866b0f 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -857,6 +857,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { extraClasses.push(...["tinted", colorClass]); } + if (this.spotlightedNotePath && this.spotlightedNotePath.endsWith(`/${note.noteId}`)) { + extraClasses.push("spotlighted-node"); + } + return extraClasses.join(" "); } From 968a17fbfb84492cdd160f00cbb4c923569a333f Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 00:55:33 +0200 Subject: [PATCH 44/68] fix(tree): alignment of note count badge --- apps/client/src/widgets/note_tree.css | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/client/src/widgets/note_tree.css b/apps/client/src/widgets/note_tree.css index 6e3b90dca..57e3460ac 100644 --- a/apps/client/src/widgets/note_tree.css +++ b/apps/client/src/widgets/note_tree.css @@ -1,17 +1,16 @@ #left-pane .tree-wrapper { .note-indicator-icon.subtree-hidden-badge { font-family: inherit !important; - margin-inline-start: 0.5em; + margin-inline: 0.5em; + margin-top: 0.3em; background: var(--left-pane-item-action-button-background); color: var(--left-pane-item-action-button-color); - padding: 0.2em 0.4em; + padding: 0.1em 0.6em; border-radius: 0.5em; font-size: 0.7rem; font-weight: normal; - position: absolute; - top: 50%; - right: 0.6em; - transform: translateY(-50%); + float: right; + vertical-align: middle; } .spotlighted-node { From 751b91e1b8e5d5d592b5a2b048adc62e92bc411d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Jan 2026 00:41:10 +0000 Subject: [PATCH 45/68] chore(deps): update pnpm to v10.28.0 --- apps/build-docs/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/build-docs/package.json b/apps/build-docs/package.json index ef5a0053b..68c7544f7 100644 --- a/apps/build-docs/package.json +++ b/apps/build-docs/package.json @@ -9,7 +9,7 @@ "keywords": [], "author": "Elian Doran ", "license": "AGPL-3.0-only", - "packageManager": "pnpm@10.27.0", + "packageManager": "pnpm@10.28.0", "devDependencies": { "@redocly/cli": "2.14.3", "archiver": "7.0.1", diff --git a/package.json b/package.json index 41cc758d3..cda34aeb2 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "url": "https://github.com/TriliumNext/Trilium/issues" }, "homepage": "https://triliumnotes.org", - "packageManager": "pnpm@10.27.0", + "packageManager": "pnpm@10.28.0", "pnpm": { "patchedDependencies": { "@ckeditor/ckeditor5-mention": "patches/@ckeditor__ckeditor5-mention.patch", From 0f77caad6963ac600bf08da3a2d9c3a9f89a3fe3 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 09:40:19 +0200 Subject: [PATCH 46/68] feat(tree): hide items dragged into a subtreeHidden --- apps/client/src/widgets/note_tree.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 1e4866b0f..a61c454b2 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -758,6 +758,16 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { return; } + const parentNote = froca.getNoteFromCache(branch.parentNoteId); + if (parentNote?.hasLabel("subtreeHidden")) { + const parentNode = node.getParent(); + if (parentNode) { + parentNode.setActive(true); + } + node.remove(); + return; + } + const title = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.title}`; node.data.isProtected = note.isProtected; From db57f3ff62cbfcde7b5f8bacb85d8132eeae9f1a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 09:53:23 +0200 Subject: [PATCH 47/68] feat(tree): add tooltip when moving into hidden subtree --- apps/client/src/translations/en/translation.json | 6 +++++- apps/client/src/widgets/note_tree.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 33e69ba0f..54c10275b 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1773,7 +1773,11 @@ "clone-indicator-tooltip-single": "This note is cloned (1 additional parent: {{- parent}})", "shared-indicator-tooltip": "This note is shared publicly", "shared-indicator-tooltip-with-url": "This note is shared publicly at: {{- url}}", - "subtree-hidden-tooltip": "{{count}} child notes that are hidden from the tree" + "subtree-hidden-tooltip": "{{count}} child notes that are hidden from the tree", + "subtree-hidden-moved-title_one": "{{count}} note moved to {{title}}", + "subtree-hidden-moved-title_other": "{{count}} notes moved to {{title}}", + "subtree-hidden-moved-description-collection": "Items in this collection are managed by its views.", + "subtree-hidden-moved-description-other": "Items in this note are intentionally hidden." }, "title_bar_buttons": { "window-on-top": "Keep Window on Top" diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index a61c454b2..f1ca96569 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -555,6 +555,19 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } else if (data.hitMode === "after") { branchService.moveAfterBranch(selectedBranchIds, node.data.branchId); } else if (data.hitMode === "over") { + const targetNote = froca.getNoteFromCache(node.data.noteId); + if (targetNote?.hasLabel("subtreeHidden")) { + toastService.showPersistent({ + id: `subtree-hidden-moved`, + title: t("note_tree.subtree-hidden-moved-title", { count: selectedBranchIds.length, title: targetNote.title }), + message: targetNote.type === "book" + ? t("note_tree.subtree-hidden-moved-description-collection") + : t("note_tree.subtree-hidden-moved-description-other"), + icon: "bx bx-hide", + timeout: 5_000, + }); + } + branchService.moveToParentNote(selectedBranchIds, node.data.branchId); } else { throw new Error(`Unknown hitMode '${data.hitMode}'`); From faf37976637514fb9c42209257bb0c1148a8b409 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 09:57:09 +0200 Subject: [PATCH 48/68] feat(tree): disable insert child note if subnotes are hidden --- apps/client/src/menus/tree_context_menu.ts | 57 +++++++++++----------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index 51f9912b3..3f5aa9480 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -1,21 +1,21 @@ -import NoteColorPicker from "./custom-items/NoteColorPicker.jsx"; -import treeService from "../services/tree.js"; -import froca from "../services/froca.js"; -import clipboard from "../services/clipboard.js"; -import noteCreateService from "../services/note_create.js"; -import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js"; import appContext, { type ContextMenuCommandData, type FilteredCommandNames } from "../components/app_context.js"; +import type { SelectMenuItemEventListener } from "../components/events.js"; +import type FAttachment from "../entities/fattachment.js"; +import attributes from "../services/attributes.js"; +import { executeBulkActions } from "../services/bulk_action.js"; +import clipboard from "../services/clipboard.js"; +import dialogService from "../services/dialog.js"; +import froca from "../services/froca.js"; +import { t } from "../services/i18n.js"; +import noteCreateService from "../services/note_create.js"; import noteTypesService from "../services/note_types.js"; import server from "../services/server.js"; import toastService from "../services/toast.js"; -import dialogService from "../services/dialog.js"; -import { t } from "../services/i18n.js"; -import type NoteTreeWidget from "../widgets/note_tree.js"; -import type FAttachment from "../entities/fattachment.js"; -import type { SelectMenuItemEventListener } from "../components/events.js"; +import treeService from "../services/tree.js"; import utils from "../services/utils.js"; -import attributes from "../services/attributes.js"; -import { executeBulkActions } from "../services/bulk_action.js"; +import type NoteTreeWidget from "../widgets/note_tree.js"; +import contextMenu, { type MenuCommandItem, type MenuItem } from "./context_menu.js"; +import NoteColorPicker from "./custom-items/NoteColorPicker.jsx"; // TODO: Deduplicate once client/server is well split. interface ConvertToAttachmentResponse { @@ -72,6 +72,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener(command, { node: this.node, - notePath: notePath, + notePath, noteId: this.node.data.noteId, selectedOrActiveBranchIds: this.treeWidget.getSelectedOrActiveBranchIds(this.node), selectedOrActiveNoteIds: this.treeWidget.getSelectedOrActiveNoteIds(this.node) From af537e6a48ed90b8ba0a7d35987db8b1bb360c49 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 10:08:50 +0200 Subject: [PATCH 49/68] feat(tree): add option to hide or show subtree --- apps/client/src/menus/tree_context_menu.ts | 15 +++++-- apps/client/src/services/attributes.ts | 43 ++++++++++++++----- .../src/translations/en/translation.json | 2 + 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index 3f5aa9480..d7ff4b777 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -151,8 +151,17 @@ export default class TreeContextMenu implements SelectMenuItemEventListener { + const note = await froca.getNote(this.node.data.noteId); + if (!note) return; + attributes.toggleBooleanWithInheritance(note, "subtreeHidden"); + } + }, { title: t("tree-context-menu.sort-by"), command: "sortChildNotes", @@ -165,7 +174,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener[] }, { kind: "separator" }, diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts index 4559c8f8b..359e7ab43 100644 --- a/apps/client/src/services/attributes.ts +++ b/apps/client/src/services/attributes.ts @@ -1,14 +1,15 @@ -import server from "./server.js"; -import froca from "./froca.js"; -import type FNote from "../entities/fnote.js"; -import type { AttributeRow } from "./load_results.js"; import { AttributeType } from "@triliumnext/commons"; +import type FNote from "../entities/fnote.js"; +import froca from "./froca.js"; +import type { AttributeRow } from "./load_results.js"; +import server from "./server.js"; + async function addLabel(noteId: string, name: string, value: string = "", isInheritable = false) { await server.put(`notes/${noteId}/attribute`, { type: "label", - name: name, - value: value, + name, + value, isInheritable }); } @@ -16,8 +17,8 @@ async function addLabel(noteId: string, name: string, value: string = "", isInhe export async function setLabel(noteId: string, name: string, value: string = "", isInheritable = false) { await server.put(`notes/${noteId}/set-attribute`, { type: "label", - name: name, - value: value, + name, + value, isInheritable }); } @@ -25,12 +26,33 @@ export async function setLabel(noteId: string, name: string, value: string = "", export async function setRelation(noteId: string, name: string, value: string = "", isInheritable = false) { await server.put(`notes/${noteId}/set-attribute`, { type: "relation", - name: name, - value: value, + name, + value, isInheritable }); } +/** + * Toggles a boolean label on the given note, taking inheritance into account. If the label is owned by the note, it + * will be removed. If the label is inherited from a parent note, it will be overridden to `false`. If the label does + * not exist, it will be added with an empty value. + * + * @param note the note on which to toggle the label. + * @param labelName the name of the label to toggle. + */ +export async function toggleBooleanWithInheritance(note: FNote, labelName: string) { + if (note.hasLabel(labelName)) { + // Can either be owned by us or inherited from parent. + if (note.hasOwnedLabel(labelName)) { + removeOwnedLabelByName(note, labelName); + } else { + setLabel(note.noteId, labelName, "false"); + } + } else { + addLabel(note.noteId, labelName); + } +} + async function removeAttributeById(noteId: string, attributeId: string) { await server.remove(`notes/${noteId}/attributes/${attributeId}`); } @@ -142,6 +164,7 @@ export default { setLabel, setRelation, setAttribute, + toggleBooleanWithInheritance, removeAttributeById, removeOwnedLabelByName, removeOwnedRelationByName, diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 54c10275b..25499ecbf 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1655,6 +1655,8 @@ "advanced": "Advanced", "expand-subtree": "Expand subtree", "collapse-subtree": "Collapse subtree", + "hide-subtree": "Hide subtree", + "show-subtree": "Show subtree", "sort-by": "Sort by...", "recent-changes-in-subtree": "Recent changes in subtree", "convert-to-attachment": "Convert to attachment", From 5cabc6379d32ee050f46ed77a5f549c4977890ab Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 10:14:15 +0200 Subject: [PATCH 50/68] fix(note_tree): not reacting to changes in subtreeHidden --- apps/client/src/widgets/note_tree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index f1ca96569..d541e6deb 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -1271,7 +1271,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } else { refreshCtx.noteIdsToUpdate.add(attrRow.noteId); } - } else if (attrRow.type === "label" && attrRow.name === "archived" && attrRow.noteId) { + } else if (attrRow.type === "label" && (attrRow.name === "archived" || attrRow.name === "subtreeHidden") && attrRow.noteId) { const note = froca.getNoteFromCache(attrRow.noteId); if (note) { From d77d30f29e074e1893817618eb86f72d14465a31 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 10:17:09 +0200 Subject: [PATCH 51/68] fix(note_tree): subtree hidden cannot be overridden through inheritance --- apps/client/src/entities/fnote.ts | 2 +- apps/client/src/menus/tree_context_menu.ts | 2 +- apps/client/src/services/attributes.ts | 2 ++ apps/client/src/widgets/note_tree.ts | 10 +++++----- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts index 644ea2228..83255e488 100644 --- a/apps/client/src/entities/fnote.ts +++ b/apps/client/src/entities/fnote.ts @@ -616,7 +616,7 @@ export default class FNote { } isFolder() { - if (this.hasLabel("subtreeHidden")) return false; + if (this.isLabelTruthy("subtreeHidden")) return false; if (this.type === "search") return true; return this.getFilteredChildBranches().length > 0; } diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index d7ff4b777..551732e9d 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -72,7 +72,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener Date: Sat, 10 Jan 2026 10:34:06 +0200 Subject: [PATCH 52/68] feat(collections): add setting to hide subtree --- .../src/translations/en/translation.json | 3 ++- .../note_bars/CollectionProperties.tsx | 7 +++++++ apps/client/src/widgets/react/hooks.tsx | 19 +++++++++++++++---- packages/commons/src/lib/attribute_names.ts | 3 +++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 25499ecbf..ab07ad188 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -800,7 +800,8 @@ "geo-map": "Geo Map", "board": "Board", "presentation": "Presentation", - "include_archived_notes": "Show archived notes" + "include_archived_notes": "Show archived notes", + "hide_child_notes": "Hide child notes in tree" }, "edited_notes": { "no_edited_notes_found": "No edited notes on this day yet...", diff --git a/apps/client/src/widgets/note_bars/CollectionProperties.tsx b/apps/client/src/widgets/note_bars/CollectionProperties.tsx index 6fc510fc7..d5c9ffb42 100644 --- a/apps/client/src/widgets/note_bars/CollectionProperties.tsx +++ b/apps/client/src/widgets/note_bars/CollectionProperties.tsx @@ -82,6 +82,13 @@ function ViewOptions({ note, viewType }: { note: FNote, viewType: ViewTypeOption ))} {properties.length > 0 && } + + { if (note) { + const actualValue = note.isLabelTruthy(labelName); + if (actualValue === value) return; + if (value) { - attributes.setLabel(note.noteId, labelName, ""); - } else { + if (note.getLabelValue(labelName) === "false") { + // Remove the override so that the inherited true takes effect. + attributes.removeOwnedLabelByName(note, labelName); + } else { + attributes.setLabel(note.noteId, labelName, ""); + } + } else if (note.hasOwnedLabel(labelName)) { attributes.removeOwnedLabelByName(note, labelName); + } else { + // Label is inherited - override to false. + attributes.setLabel(note.noteId, labelName, "false"); } } - }, [note]); + }, [note, labelName]); useDebugValue(labelName); - const labelValue = !!note?.hasLabel(labelName); + const labelValue = !!note?.isLabelTruthy(labelName); return [ labelValue, setter ] as const; } diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts index afb45fcb3..b524b409f 100644 --- a/packages/commons/src/lib/attribute_names.ts +++ b/packages/commons/src/lib/attribute_names.ts @@ -22,6 +22,9 @@ type Labels = { pageUrl: string; dateNote: string; + // Tree specific + subtreeHidden: boolean; + // Search searchString: string; ancestorDepth: string; From 0b065063f20038c995ea196c913c977005478da0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 10:37:19 +0200 Subject: [PATCH 53/68] refactor(client): use same logic for setting boolean with inheritance --- apps/client/src/menus/tree_context_menu.ts | 2 +- apps/client/src/services/attributes.ts | 31 +++++++++++++--------- apps/client/src/widgets/react/hooks.tsx | 17 +----------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index 551732e9d..4f28cc1f4 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -159,7 +159,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener { const note = await froca.getNote(this.node.data.noteId); if (!note) return; - attributes.toggleBooleanWithInheritance(note, "subtreeHidden"); + attributes.setBooleanWithInheritance(note, "subtreeHidden", !hasSubtreeHidden); } }, { diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts index 3b45d2d46..34c07d638 100644 --- a/apps/client/src/services/attributes.ts +++ b/apps/client/src/services/attributes.ts @@ -33,25 +33,32 @@ export async function setRelation(noteId: string, name: string, value: string = } /** - * Toggles a boolean label on the given note, taking inheritance into account. If the label is owned by the note, it - * will be removed. If the label is inherited from a parent note, it will be overridden to `false`. If the label does - * not exist, it will be added with an empty value. + * Sets a boolean label on the given note, taking inheritance into account. If the desired value matches the inherited + * value, any owned label will be removed to allow the inherited value to take effect. If the desired value differs + * from the inherited value, an owned label will be created or updated to reflect the desired value. * * When checking if the boolean value is set, don't use `note.hasLabel`; instead use `note.isLabelTruthy`. * - * @param note the note on which to toggle the label. - * @param labelName the name of the label to toggle. + * @param note the note on which to set the boolean label. + * @param labelName the name of the label to set. + * @param value the boolean value to set for the label. */ -export async function toggleBooleanWithInheritance(note: FNote, labelName: string) { - if (note.hasLabel(labelName)) { - // Can either be owned by us or inherited from parent. - if (note.hasOwnedLabel(labelName)) { +export async function setBooleanWithInheritance(note: FNote, labelName: string, value: boolean) { + const actualValue = note.isLabelTruthy(labelName); + if (actualValue === value) return; + + if (value) { + if (note.getLabelValue(labelName) === "false") { + // Remove the override so that the inherited true takes effect. removeOwnedLabelByName(note, labelName); } else { - setLabel(note.noteId, labelName, "false"); + setLabel(note.noteId, labelName, ""); } + } else if (note.hasOwnedLabel(labelName)) { + removeOwnedLabelByName(note, labelName); } else { - addLabel(note.noteId, labelName); + // Label is inherited - override to false. + setLabel(note.noteId, labelName, "false"); } } @@ -166,7 +173,7 @@ export default { setLabel, setRelation, setAttribute, - toggleBooleanWithInheritance, + setBooleanWithInheritance, removeAttributeById, removeOwnedLabelByName, removeOwnedRelationByName, diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 610f65ded..8eb2cc0ef 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -646,22 +646,7 @@ export function useNoteLabelBoolean(note: FNote | undefined | null, labelName: F const setter = useCallback((value: boolean) => { if (note) { - const actualValue = note.isLabelTruthy(labelName); - if (actualValue === value) return; - - if (value) { - if (note.getLabelValue(labelName) === "false") { - // Remove the override so that the inherited true takes effect. - attributes.removeOwnedLabelByName(note, labelName); - } else { - attributes.setLabel(note.noteId, labelName, ""); - } - } else if (note.hasOwnedLabel(labelName)) { - attributes.removeOwnedLabelByName(note, labelName); - } else { - // Label is inherited - override to false. - attributes.setLabel(note.noteId, labelName, "false"); - } + attributes.setBooleanWithInheritance(note, labelName, value); } }, [note, labelName]); From 6398830c2d1095f9baa9df864ce389cdb1afc43a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 11:15:21 +0200 Subject: [PATCH 54/68] test(client): attribute toggling --- apps/client/src/services/attributes.spec.ts | 113 ++++++++++++++++++++ apps/client/src/test/easy-froca.ts | 38 +++---- 2 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 apps/client/src/services/attributes.spec.ts diff --git a/apps/client/src/services/attributes.spec.ts b/apps/client/src/services/attributes.spec.ts new file mode 100644 index 000000000..cd52cb6d5 --- /dev/null +++ b/apps/client/src/services/attributes.spec.ts @@ -0,0 +1,113 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { buildNote } from "../test/easy-froca"; +import { setBooleanWithInheritance } from "./attributes"; +import froca from "./froca"; +import server from "./server.js"; + +// Spy on server methods to track calls +// @ts-expect-error the generic typing is causing issues here +server.put = vi.fn(async (url: string, data?: T) => ({} as T)); +// @ts-expect-error the generic typing is causing issues here +server.remove = vi.fn(async (url: string) => ({} as T)); + +describe("Set boolean with inheritance", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("doesn't call server if value matches directly", async () => { + const noteWithLabel = buildNote({ + title: "New note", + "#foo": "" + }); + const noteWithoutLabel = buildNote({ + title: "New note" + }); + + await setBooleanWithInheritance(noteWithLabel, "foo", true); + await setBooleanWithInheritance(noteWithoutLabel, "foo", false); + expect(server.put).not.toHaveBeenCalled(); + expect(server.remove).not.toHaveBeenCalled(); + }); + + it("sets boolean normally without inheritance", async () => { + const standaloneNote = buildNote({ + title: "New note" + }); + + await setBooleanWithInheritance(standaloneNote, "foo", true); + expect(server.put).toHaveBeenCalledWith(`notes/${standaloneNote.noteId}/set-attribute`, { + type: "label", + name: "foo", + value: "", + isInheritable: false + }); + }); + + it("removes boolean normally without inheritance", async () => { + const standaloneNote = buildNote({ + title: "New note", + "#foo": "" + }); + + const attributeId = standaloneNote.getLabel("foo")!.attributeId; + await setBooleanWithInheritance(standaloneNote, "foo", false); + expect(server.remove).toHaveBeenCalledWith(`notes/${standaloneNote.noteId}/attributes/${attributeId}`); + }); + + it("doesn't call server if value matches inherited", async () => { + const parentNote = buildNote({ + title: "Parent note", + "#foo(inheritable)": "", + "children": [ + { + title: "Child note" + } + ] + }); + const childNote = froca.getNoteFromCache(parentNote.children[0])!; + expect(childNote.isLabelTruthy("foo")).toBe(true); + await setBooleanWithInheritance(childNote, "foo", true); + expect(server.put).not.toHaveBeenCalled(); + expect(server.remove).not.toHaveBeenCalled(); + }); + + it("overrides boolean with inheritance", async () => { + const parentNote = buildNote({ + title: "Parent note", + "#foo(inheritable)": "", + "children": [ + { + title: "Child note" + } + ] + }); + const childNote = froca.getNoteFromCache(parentNote.children[0])!; + expect(childNote.isLabelTruthy("foo")).toBe(true); + await setBooleanWithInheritance(childNote, "foo", false); + expect(server.put).toHaveBeenCalledWith(`notes/${childNote.noteId}/set-attribute`, { + type: "label", + name: "foo", + value: "false", + isInheritable: false + }); + }); + + it("deletes override boolean with inheritance", async () => { + const parentNote = buildNote({ + title: "Parent note", + "#foo(inheritable)": "", + "children": [ + { + title: "Child note", + "#foo": "false", + } + ] + }); + const childNote = froca.getNoteFromCache(parentNote.children[0])!; + expect(childNote.isLabelTruthy("foo")).toBe(false); + await setBooleanWithInheritance(childNote, "foo", true); + expect(server.remove).toHaveBeenCalledWith(`notes/${childNote.noteId}/attributes/${childNote.getLabel("foo")!.attributeId}`); + }); +}); diff --git a/apps/client/src/test/easy-froca.ts b/apps/client/src/test/easy-froca.ts index 59d4c2e4a..d3ae75718 100644 --- a/apps/client/src/test/easy-froca.ts +++ b/apps/client/src/test/easy-froca.ts @@ -69,24 +69,6 @@ export function buildNote(noteDef: NoteDefinition) { }); note.getBlob = async () => blob; - // Manage children. - if (noteDef.children) { - for (const childDef of noteDef.children) { - const childNote = buildNote(childDef); - const branchId = `${note.noteId}_${childNote.noteId}`; - const branch = new FBranch(froca, { - branchId, - noteId: childNote.noteId, - parentNoteId: note.noteId, - notePosition: childNotePosition, - fromSearchNote: false - }); - froca.branches[branchId] = branch; - note.addChild(childNote.noteId, branchId, false); - childNotePosition += 10; - } - } - let position = 0; for (const [ key, value ] of Object.entries(noteDef)) { const attributeId = utils.randomString(12); @@ -136,5 +118,25 @@ export function buildNote(noteDef: NoteDefinition) { } noteAttributeCache.attributes[note.noteId].push(attribute); } + + // Manage children. + if (noteDef.children) { + for (const childDef of noteDef.children) { + const childNote = buildNote(childDef); + const branchId = `${note.noteId}_${childNote.noteId}`; + const branch = new FBranch(froca, { + branchId, + noteId: childNote.noteId, + parentNoteId: note.noteId, + notePosition: childNotePosition, + fromSearchNote: false + }); + froca.branches[branchId] = branch; + note.addChild(childNote.noteId, branchId, false); + childNote.addParent(note.noteId, branchId, false); + childNotePosition += 10; + } + } + return note; } From 26c25cd4cdbfe6767d9631434f2e8cf88818eae2 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 11:25:00 +0200 Subject: [PATCH 55/68] fix(client): edge case not handled when parent note overrides to false --- apps/client/src/services/attributes.spec.ts | 38 +++++++++++++++++++++ apps/client/src/services/attributes.ts | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/apps/client/src/services/attributes.spec.ts b/apps/client/src/services/attributes.spec.ts index cd52cb6d5..13be02765 100644 --- a/apps/client/src/services/attributes.spec.ts +++ b/apps/client/src/services/attributes.spec.ts @@ -110,4 +110,42 @@ describe("Set boolean with inheritance", () => { await setBooleanWithInheritance(childNote, "foo", true); expect(server.remove).toHaveBeenCalledWith(`notes/${childNote.noteId}/attributes/${childNote.getLabel("foo")!.attributeId}`); }); + + it("overrides boolean with inherited false", async () => { + const parentNote = buildNote({ + title: "Parent note", + "#foo(inheritable)": "false", + "children": [ + { + title: "Child note" + } + ] + }); + const childNote = froca.getNoteFromCache(parentNote.children[0])!; + expect(childNote.isLabelTruthy("foo")).toBe(false); + await setBooleanWithInheritance(childNote, "foo", true); + expect(server.put).toHaveBeenCalledWith(`notes/${childNote.noteId}/set-attribute`, { + type: "label", + name: "foo", + value: "", + isInheritable: false + }); + }); + + it("deletes override boolean with inherited false with already existing value", async () => { + const parentNote = buildNote({ + title: "Parent note", + "#foo(inheritable)": "false", + "children": [ + { + title: "Child note", + "#foo": "false", + } + ] + }); + const childNote = froca.getNoteFromCache(parentNote.children[0])!; + expect(childNote.isLabelTruthy("foo")).toBe(false); + await setBooleanWithInheritance(childNote, "foo", true); + expect(server.remove).toHaveBeenCalledWith(`notes/${childNote.noteId}/attributes/${childNote.getLabel("foo")!.attributeId}`); + }); }); diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts index 34c07d638..2f0a1202d 100644 --- a/apps/client/src/services/attributes.ts +++ b/apps/client/src/services/attributes.ts @@ -48,7 +48,7 @@ export async function setBooleanWithInheritance(note: FNote, labelName: string, if (actualValue === value) return; if (value) { - if (note.getLabelValue(labelName) === "false") { + if (note.getOwnedLabelValue(labelName) === "false") { // Remove the override so that the inherited true takes effect. removeOwnedLabelByName(note, labelName); } else { From f462034868103c704109558e005109c6f7ab7091 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 11:55:49 +0200 Subject: [PATCH 56/68] fix(client/tree): toast not appearing when inserted via paste --- .../src/translations/en/translation.json | 7 ++-- apps/client/src/widgets/note_tree.ts | 40 ++++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index ab07ad188..f2ecc8f32 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1777,10 +1777,9 @@ "shared-indicator-tooltip": "This note is shared publicly", "shared-indicator-tooltip-with-url": "This note is shared publicly at: {{- url}}", "subtree-hidden-tooltip": "{{count}} child notes that are hidden from the tree", - "subtree-hidden-moved-title_one": "{{count}} note moved to {{title}}", - "subtree-hidden-moved-title_other": "{{count}} notes moved to {{title}}", - "subtree-hidden-moved-description-collection": "Items in this collection are managed by its views.", - "subtree-hidden-moved-description-other": "Items in this note are intentionally hidden." + "subtree-hidden-moved-title": "Added to {{title}}", + "subtree-hidden-moved-description-collection": "This collection hides its child notes in the tree.", + "subtree-hidden-moved-description-other": "Child notes are hidden in the tree for this note." }, "title_bar_buttons": { "window-on-top": "Keep Window on Top" diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 34aa0548c..f6af3c374 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -555,19 +555,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } else if (data.hitMode === "after") { branchService.moveAfterBranch(selectedBranchIds, node.data.branchId); } else if (data.hitMode === "over") { - const targetNote = froca.getNoteFromCache(node.data.noteId); - if (targetNote?.isLabelTruthy("subtreeHidden")) { - toastService.showPersistent({ - id: `subtree-hidden-moved`, - title: t("note_tree.subtree-hidden-moved-title", { count: selectedBranchIds.length, title: targetNote.title }), - message: targetNote.type === "book" - ? t("note_tree.subtree-hidden-moved-description-collection") - : t("note_tree.subtree-hidden-moved-description-other"), - icon: "bx bx-hide", - timeout: 5_000, - }); - } - branchService.moveToParentNote(selectedBranchIds, node.data.branchId); } else { throw new Error(`Unknown hitMode '${data.hitMode}'`); @@ -773,12 +760,8 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const parentNote = froca.getNoteFromCache(branch.parentNoteId); if (parentNote?.isLabelTruthy("subtreeHidden")) { - const parentNode = node.getParent(); - if (parentNode) { - parentNode.setActive(true); - } node.remove(); - return; + return "removed-due-to-subtree-hidden"; } const title = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.title}`; @@ -1402,11 +1385,30 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { }); // for some reason, node update cannot be in the batchUpdate() block (node is not re-rendered) + let removedFromSubtreeParent: Fancytree.FancytreeNode | null = null; for (const noteId of refreshCtx.noteIdsToUpdate) { for (const node of this.getNodesByNoteId(noteId)) { - await this.updateNode(node); + const parent = node.parent; + const result = await this.updateNode(node); + if (result === "removed-due-to-subtree-hidden") { + removedFromSubtreeParent = parent; + } } } + + if (removedFromSubtreeParent) { + removedFromSubtreeParent.setActive(true, { noEvents: true }); + const targetNote = froca.getNoteFromCache(removedFromSubtreeParent.data.noteId || ""); + toastService.showPersistent({ + id: `subtree-hidden-moved`, + title: t("note_tree.subtree-hidden-moved-title", { title: targetNote?.title }), + message: targetNote?.type === "book" + ? t("note_tree.subtree-hidden-moved-description-collection") + : t("note_tree.subtree-hidden-moved-description-other"), + icon: "bx bx-hide", + timeout: 5_000, + }); + } } async #setActiveNode(activeNotePath: string | null, activeNodeFocused: boolean, movedActiveNode: Fancytree.FancytreeNode | null, parentsOfAddedNodes: Fancytree.FancytreeNode[]) { From 8ad779be66f80284bbcfafc381191e7d398142ad Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 12:26:08 +0200 Subject: [PATCH 57/68] fix(client/load_results): component ID not preserved for attributes & branches --- apps/client/src/services/load_results.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/load_results.ts b/apps/client/src/services/load_results.ts index 899560de4..5b6589c65 100644 --- a/apps/client/src/services/load_results.ts +++ b/apps/client/src/services/load_results.ts @@ -1,4 +1,5 @@ import type { AttachmentRow, EtapiTokenRow, NoteType, OptionNames } from "@triliumnext/commons"; + import type { AttributeType } from "../entities/fattribute.js"; import type { EntityChange } from "../server_types.js"; @@ -131,11 +132,19 @@ export default class LoadResults { } addBranch(branchId: string, componentId: string) { + console.log("Got branch with ", branchId, componentId); this.branchRows.push({ branchId, componentId }); } getBranchRows() { - return this.branchRows.map((row) => this.getEntityRow("branches", row.branchId)).filter((branch) => !!branch); + return this.branchRows.map((row) => { + const branch = this.getEntityRow("branches", row.branchId); + if (branch) { + // Merge the componentId from the tracked row with the entity data + return { ...branch, componentId: row.componentId }; + } + return null; + }).filter((branch) => !!branch) as BranchRow[]; } addNoteReordering(parentNoteId: string, componentId: string) { @@ -153,7 +162,14 @@ export default class LoadResults { getAttributeRows(componentId = "none"): AttributeRow[] { return this.attributeRows .filter((row) => row.componentId !== componentId) - .map((row) => this.getEntityRow("attributes", row.attributeId)) + .map((row) => { + const attr = this.getEntityRow("attributes", row.attributeId); + if (attr) { + // Merge the componentId from the tracked row with the entity data + return { ...attr, componentId: row.componentId }; + } + return null; + }) .filter((attr) => !!attr) as AttributeRow[]; } From 3354bd669f0d75ff90753749b031d7c4e47a5b16 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 12:26:51 +0200 Subject: [PATCH 58/68] fix(client/tree): toast displayed when doing operations outside of tree --- apps/client/src/services/branches.ts | 16 +++--- apps/client/src/services/load_results.ts | 1 - apps/client/src/types-fancytree.d.ts | 21 ++++---- apps/client/src/widgets/note_tree.ts | 68 +++++++++++------------- 4 files changed, 49 insertions(+), 57 deletions(-) diff --git a/apps/client/src/services/branches.ts b/apps/client/src/services/branches.ts index e5a5158e1..cd1f5c6e7 100644 --- a/apps/client/src/services/branches.ts +++ b/apps/client/src/services/branches.ts @@ -1,12 +1,12 @@ -import utils from "./utils.js"; -import server from "./server.js"; -import toastService, { type ToastOptionsWithRequiredId } from "./toast.js"; +import appContext from "../components/app_context.js"; +import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js"; import froca from "./froca.js"; import hoistedNoteService from "./hoisted_note.js"; -import ws from "./ws.js"; -import appContext from "../components/app_context.js"; import { t } from "./i18n.js"; -import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js"; +import server from "./server.js"; +import toastService, { type ToastOptionsWithRequiredId } from "./toast.js"; +import utils from "./utils.js"; +import ws from "./ws.js"; // TODO: Deduplicate type with server interface Response { @@ -66,7 +66,7 @@ async function moveAfterBranch(branchIdsToMove: string[], afterBranchId: string) } } -async function moveToParentNote(branchIdsToMove: string[], newParentBranchId: string) { +async function moveToParentNote(branchIdsToMove: string[], newParentBranchId: string, componentId?: string) { const newParentBranch = froca.getBranch(newParentBranchId); if (!newParentBranch) { return; @@ -86,7 +86,7 @@ async function moveToParentNote(branchIdsToMove: string[], newParentBranchId: st continue; } - const resp = await server.put(`branches/${branchIdToMove}/move-to/${newParentBranchId}`); + const resp = await server.put(`branches/${branchIdToMove}/move-to/${newParentBranchId}`, undefined, componentId); if (!resp.success) { toastService.showError(resp.message); diff --git a/apps/client/src/services/load_results.ts b/apps/client/src/services/load_results.ts index 5b6589c65..b5706705b 100644 --- a/apps/client/src/services/load_results.ts +++ b/apps/client/src/services/load_results.ts @@ -132,7 +132,6 @@ export default class LoadResults { } addBranch(branchId: string, componentId: string) { - console.log("Got branch with ", branchId, componentId); this.branchRows.push({ branchId, componentId }); } diff --git a/apps/client/src/types-fancytree.d.ts b/apps/client/src/types-fancytree.d.ts index cb709ea07..a8a151b55 100644 --- a/apps/client/src/types-fancytree.d.ts +++ b/apps/client/src/types-fancytree.d.ts @@ -69,7 +69,7 @@ declare namespace Fancytree { debug(msg: any): void; /** Expand (or collapse) all parent nodes. */ - expandAll(flag?: boolean, options?: Object): void; + expandAll(flag?: boolean, options?: object): void; /** [ext-filter] Dimm or hide whole branches. * @returns {integer} count @@ -221,6 +221,7 @@ declare namespace Fancytree { branchId: string; isProtected: boolean; noteType: NoteType; + subtreeHidden: boolean; } interface FancytreeNewNode extends FancytreeNodeData { @@ -369,7 +370,7 @@ declare namespace Fancytree { * @param mode 'before', 'after', or 'child' (default='child') * @param init NodeData (or simple title string) */ - editCreateNode(mode?: string, init?: Object): void; + editCreateNode(mode?: string, init?: object): void; /** [ext-edit] Stop inline editing. * @@ -526,7 +527,7 @@ declare namespace Fancytree { * * @param opts passed to `setExpanded()`. Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true} */ - makeVisible(opts?: Object): JQueryPromise; + makeVisible(opts?: object): JQueryPromise; /** Move this node to targetNode. * @@ -589,25 +590,25 @@ declare namespace Fancytree { * @param effects animation options. * @param options {topNode: null, effects: ..., parent: ...} this node will remain visible in any case, even if `this` is outside the scroll pane. */ - scrollIntoView(effects?: boolean, options?: Object): JQueryPromise; + scrollIntoView(effects?: boolean, options?: object): JQueryPromise; /** * @param effects animation options. * @param options {topNode: null, effects: ..., parent: ...} this node will remain visible in any case, even if `this` is outside the scroll pane. */ - scrollIntoView(effects?: Object, options?: Object): JQueryPromise; + scrollIntoView(effects?: object, options?: object): JQueryPromise; /** * @param flag pass false to deactivate * @param opts additional options. Defaults to {noEvents: false} */ - setActive(flag?: boolean, opts?: Object): JQueryPromise; + setActive(flag?: boolean, opts?: object): JQueryPromise; /** * @param flag pass false to collapse. * @param opts additional options. Defaults to {noAnimation:false, noEvents:false} */ - setExpanded(flag?: boolean, opts?: Object): JQueryPromise; + setExpanded(flag?: boolean, opts?: object): JQueryPromise; /** * Set keyboard focus to this node. @@ -1109,7 +1110,7 @@ declare namespace Fancytree { /** class names added to the node markup (separate with space) */ extraClasses?: string | undefined; /** all properties from will be copied to `node.data` */ - data?: Object | undefined; + data?: object | undefined; /** Will be added as title attribute of the node's icon span,thus enabling a tooltip. */ iconTooltip?: string | undefined; @@ -1160,7 +1161,7 @@ declare namespace Fancytree { escapeHtml(s: string): string; - getEventTarget(event: Event): Object; + getEventTarget(event: Event): object; getEventTargetType(event: Event): string; @@ -1179,7 +1180,7 @@ declare namespace Fancytree { parseHtml($ul: JQuery): NodeData[]; /** Add Fancytree extension definition to the list of globally available extensions. */ - registerExtension(definition: Object): void; + registerExtension(definition: object): void; unescapeHtml(s: string): string; diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index f6af3c374..c7078b874 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -555,7 +555,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } else if (data.hitMode === "after") { branchService.moveAfterBranch(selectedBranchIds, node.data.branchId); } else if (data.hitMode === "over") { - branchService.moveToParentNote(selectedBranchIds, node.data.branchId); + branchService.moveToParentNote(selectedBranchIds, node.data.branchId, this.componentId); } else { throw new Error(`Unknown hitMode '${data.hitMode}'`); } @@ -758,12 +758,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { return; } - const parentNote = froca.getNoteFromCache(branch.parentNoteId); - if (parentNote?.isLabelTruthy("subtreeHidden")) { - node.remove(); - return "removed-due-to-subtree-hidden"; - } - const title = `${branch.prefix ? `${branch.prefix} - ` : ""}${note.title}`; node.data.isProtected = note.isProtected; @@ -805,6 +799,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { lazy: true, folder: isFolder, expanded: !!branch.isExpanded && note.type !== "search", + subtreeHidden: note.isLabelTruthy("subtreeHidden"), key: utils.randomString(12) // this should prevent some "duplicate key" errors }; @@ -1339,18 +1334,34 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } else if (frocaBranch) { // make sure it's loaded // we're forcing lazy since it's not clear if the whole required subtree is in froca - const newNode = this.prepareNode(frocaBranch, true); - if (newNode) { - parentNode.addChildren([newNode]); - } + if (!parentNode.data.subtreeHidden) { + const newNode = this.prepareNode(frocaBranch, true); + if (newNode) { + parentNode.addChildren([newNode]); + } - if (frocaBranch?.isExpanded && note && note.hasChildren()) { - refreshCtx.noteIdsToReload.add(frocaBranch.noteId); - } + if (frocaBranch?.isExpanded && note && note.hasChildren()) { + refreshCtx.noteIdsToReload.add(frocaBranch.noteId); + } - this.sortChildren(parentNode); + this.sortChildren(parentNode); + } else if (branchRow.componentId === this.componentId) { + // Display the toast and focus to parent note only if we know for sure that the operation comes from the tree. + const parentNote = froca.getNoteFromCache(parentNode.data.noteId || ""); + toastService.showPersistent({ + id: `subtree-hidden-moved`, + title: t("note_tree.subtree-hidden-moved-title", { title: parentNote?.title }), + message: parentNote?.type === "book" + ? t("note_tree.subtree-hidden-moved-description-collection") + : t("note_tree.subtree-hidden-moved-description-other"), + icon: "bx bx-hide", + timeout: 5_000, + }); + parentNode.setActive(true); + } // this might be a first child which would force an icon change + // also update the count if the subtree is hidden. if (branchRow.parentNoteId) { refreshCtx.noteIdsToUpdate.add(branchRow.parentNoteId); } @@ -1385,30 +1396,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { }); // for some reason, node update cannot be in the batchUpdate() block (node is not re-rendered) - let removedFromSubtreeParent: Fancytree.FancytreeNode | null = null; for (const noteId of refreshCtx.noteIdsToUpdate) { for (const node of this.getNodesByNoteId(noteId)) { - const parent = node.parent; - const result = await this.updateNode(node); - if (result === "removed-due-to-subtree-hidden") { - removedFromSubtreeParent = parent; - } + await this.updateNode(node); } } - - if (removedFromSubtreeParent) { - removedFromSubtreeParent.setActive(true, { noEvents: true }); - const targetNote = froca.getNoteFromCache(removedFromSubtreeParent.data.noteId || ""); - toastService.showPersistent({ - id: `subtree-hidden-moved`, - title: t("note_tree.subtree-hidden-moved-title", { title: targetNote?.title }), - message: targetNote?.type === "book" - ? t("note_tree.subtree-hidden-moved-description-collection") - : t("note_tree.subtree-hidden-moved-description-other"), - icon: "bx bx-hide", - timeout: 5_000, - }); - } } async #setActiveNode(activeNotePath: string | null, activeNodeFocused: boolean, movedActiveNode: Fancytree.FancytreeNode | null, parentsOfAddedNodes: Fancytree.FancytreeNode[]) { @@ -1665,7 +1657,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const toNode = node.getPrevSibling(); if (toNode !== null) { - branchService.moveToParentNote([node.data.branchId], toNode.data.branchId); + branchService.moveToParentNote([node.data.branchId], toNode.data.branchId, this.componentId); } } @@ -1802,12 +1794,12 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { #moveLaunchers(selectedOrActiveBranchIds: string[], desktopParent: string, mobileParent: string) { const desktopLaunchersToMove = selectedOrActiveBranchIds.filter((branchId) => !branchId.startsWith("_lbMobile")); if (desktopLaunchersToMove) { - branchService.moveToParentNote(desktopLaunchersToMove, `_lbRoot_${ desktopParent}`); + branchService.moveToParentNote(desktopLaunchersToMove, `_lbRoot_${ desktopParent}`, this.componentId); } const mobileLaunchersToMove = selectedOrActiveBranchIds.filter((branchId) => branchId.startsWith("_lbMobile")); if (mobileLaunchersToMove) { - branchService.moveToParentNote(mobileLaunchersToMove, `_lbMobileRoot_${ mobileParent}`); + branchService.moveToParentNote(mobileLaunchersToMove, `_lbMobileRoot_${mobileParent}`, this.componentId); } } From cb02198c6f5512b45f0cbced528d897f68b43aa6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 10 Jan 2026 12:57:11 +0200 Subject: [PATCH 59/68] docs(user): document hiding the subtree --- .../doc_notes/en/User Guide/!!!meta.json | 2 +- .../Note Tree/1_Hiding the subtree_image.png | Bin 0 -> 3219 bytes .../Note Tree/Hiding the subtree.html | 95 ++++++++++++++ .../Note Tree/Hiding the subtree_image.png | Bin 0 -> 4992 bytes .../en/User Guide/User Guide/FAQ.html | 2 +- docs/User Guide/!!!meta.json | 118 +++++++++++++++++- .../Note Tree/1_Hiding the subtree_image.png | Bin 0 -> 3219 bytes .../Note Tree/Hiding the subtree.md | 55 ++++++++ .../Note Tree/Hiding the subtree_image.png | Bin 0 -> 4992 bytes docs/User Guide/User Guide/FAQ.md | 2 +- 10 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/1_Hiding the subtree_image.png create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.html create mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree_image.png create mode 100644 docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/1_Hiding the subtree_image.png create mode 100644 docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.md create mode 100644 docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree_image.png 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 56643ced6..8abbb2f5c 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_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"}]},{"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_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"}]},{"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_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/UI Elements/Note Tree/1_Hiding the subtree_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/1_Hiding the subtree_image.png new file mode 100644 index 0000000000000000000000000000000000000000..44f0bbe515df66dfe322fb3ff289a9829d515ca6 GIT binary patch literal 3219 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 FV1f_^H|GEV literal 0 HcmV?d00001 diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.html new file mode 100644 index 000000000..c818f26dd --- /dev/null +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.html @@ -0,0 +1,95 @@ +
        + +
        An example of a collection with a relatively large number of children + that are hidden from the tree.
        +
        +

        The tree works well when the notes are structured in a hierarchy so that + the number of items stays small. When a note has a large number of notes + (in the order of thousands or tens of thousands), two problems arise:

        +
          +
        • Navigating between notes becomes cumbersome and the tree itself gets cluttered + with a large amount of notes.
        • +
        • The large amount of notes can slow down the application considerably.
        • +
        +

        Since v0.102.0, Trilium allows the tree to hide the child notes of particular + notes. This works for both Collections and + normal notes.

        +

        Interaction

        +

        When the subtree of a note is hidden, there are a few subtle changes:

        +
          +
        • To indicate that the subtree is hidden, the note will not have an expand + button and it will display the number of children to the right.
        • +
        • It's not possible to add a new note directly from the tree. +
            +
          • For Collections, + it's best to use the built-in mechanism to create notes (for example by + creating a new point on a geo-map, or by adding a new row in a table).
          • +
          • For normal notes, it's still possible to create children via other means + such as using the Internal (reference) links system.
          • +
          +
        • +
        • Notes can be dragged from outside the note, case in which they will be + cloned into it. +
            +
          • Instead of switching to the child notes that were copied, the parent note + is highlighted instead.
          • +
          • A notification will indicate this behavior.
          • +
          +
        • +
        • Similarly, features such as cut/copy and then paste into the note will + also work.
        • +
        +

        Spotlighting

        +
        + +
        +

        Even if the subtree of a note is hidden, if a child note manages to become + active, it will still appear inside the tree in a special state called spotlighted.

        +

        During this state, the note remains under its normal hierarchy, so that + its easy to tell its location. In addition, this means that:

        +
          +
        • The note position is clearly visible when using the Search.
        • +
        • The note can still be operated on from the tree, such as adding a  + Branch prefix or moving it outside the collection.
        • +
        +

        The note appears in italics to indicate its temporary display. When switching + to another note, the spotlighted note will disappear.

        + +

        Working with collections

        +

        By default, some of the Collections will + automatically hide their child notes, for example the Kanban Board or + the Table.

        +

        The reasoning behind this is that collections are generally opaque to + the rest of the notes and they can generate a large amount of sub-notes + since they intentionally lack structure (in order to allow easy swapping + between views).

        +

        Some types of collections have the child notes intentionally shown, for + example the legacy ones (Grid and List), but also the Presentation which + requires the tree structure in order to organize and edit the slides.

        +

        To toggle this behavior:

        +
          +
        • In the New Layout, + press the Options button underneath the title and uncheck Hide child notes in tree.
        • +
        • Right click the collection note in the Note Tree and + select AdvancedShow subtree.
        • +
        +

        Working with normal notes

        +

        It's possible to hide the subtree for normal notes as well, not just collections. + To do so, right click the note in the Note Tree and + select AdvancedHide subtree. +

        \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree_image.png new file mode 100644 index 0000000000000000000000000000000000000000..7b75834e96169c66e784fb2e36fdeb2f151899f2 GIT binary patch literal 4992 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 directoryin the TRILIUM_DATA_DIR environment variable and separate port on TRILIUM_PORT environment variable. How to do that depends on the platform, in Unix-based systems - you can achieve that by running command such as this:

        TRILIUM_DATA_DIR=/home/me/path/to/data/dir TRILIUM_PORT=12345 trilium 
        + you can achieve that by running command such as this:

        TRILIUM_DATA_DIR=/home/me/path/to/data/dir TRILIUM_PORT=12345 trilium 

        You can save this command into a .sh script file or make an alias. Do this similarly for a second instance with different data directory and port.

        diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json index 50b9abb02..da318029b 100644 --- a/docs/User Guide/!!!meta.json +++ b/docs/User Guide/!!!meta.json @@ -1,6 +1,6 @@ { "formatVersion": 2, - "appVersion": "0.101.1", + "appVersion": "0.101.3", "files": [ { "isClone": false, @@ -2426,6 +2426,122 @@ "format": "markdown", "dataFileName": "Keyboard shortcuts.md", "attachments": [] + }, + { + "isClone": false, + "noteId": "wyaGBBQrl4i3", + "notePath": [ + "pOsGYCXsbNQG", + "gh7bpGYxajRS", + "Vc8PjrjAGuOp", + "oPVyFC7WL2Lp", + "wyaGBBQrl4i3" + ], + "title": "Hiding the subtree", + "notePosition": 40, + "prefix": null, + "isExpanded": false, + "type": "text", + "mime": "text/html", + "attributes": [ + { + "type": "label", + "name": "shareAlias", + "value": "hiding-subtree", + "isInheritable": false, + "position": 30 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bx-hide", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "GTwFsgaA0lCt", + "isInheritable": false, + "position": 50 + }, + { + "type": "relation", + "name": "internalLink", + "value": "CtBQqbwXDx1w", + "isInheritable": false, + "position": 60 + }, + { + "type": "relation", + "name": "internalLink", + "value": "2FvYrpmOXm29", + "isInheritable": false, + "position": 70 + }, + { + "type": "relation", + "name": "internalLink", + "value": "IjZS7iK5EXtb", + "isInheritable": false, + "position": 80 + }, + { + "type": "relation", + "name": "internalLink", + "value": "oPVyFC7WL2Lp", + "isInheritable": false, + "position": 90 + }, + { + "type": "relation", + "name": "internalLink", + "value": "zP3PMqaG71Ct", + "isInheritable": false, + "position": 100 + }, + { + "type": "relation", + "name": "internalLink", + "value": "hrZ1D00cLbal", + "isInheritable": false, + "position": 110 + }, + { + "type": "relation", + "name": "internalLink", + "value": "eIg8jdvaoNNd", + "isInheritable": false, + "position": 120 + }, + { + "type": "relation", + "name": "internalLink", + "value": "TBwsyfadTA18", + "isInheritable": false, + "position": 130 + } + ], + "format": "markdown", + "dataFileName": "Hiding the subtree.md", + "attachments": [ + { + "attachmentId": "d9Sq9avKDptU", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "Hiding the subtree_image.png" + }, + { + "attachmentId": "VmRtx3vS97v1", + "title": "image.png", + "role": "image", + "mime": "image/png", + "position": 10, + "dataFileName": "1_Hiding the subtree_image.png" + } + ] } ] }, diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/1_Hiding the subtree_image.png b/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/1_Hiding the subtree_image.png new file mode 100644 index 0000000000000000000000000000000000000000..44f0bbe515df66dfe322fb3ff289a9829d515ca6 GIT binary patch literal 3219 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 FV1f_^H|GEV literal 0 HcmV?d00001 diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.md b/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.md new file mode 100644 index 000000000..9c9b75455 --- /dev/null +++ b/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree.md @@ -0,0 +1,55 @@ +# Hiding the subtree +
        An example of a collection with a relatively large number of children that are hidden from the tree.
        + +The tree works well when the notes are structured in a hierarchy so that the number of items stays small. When a note has a large number of notes (in the order of thousands or tens of thousands), two problems arise: + +* Navigating between notes becomes cumbersome and the tree itself gets cluttered with a large amount of notes. +* The large amount of notes can slow down the application considerably. + +Since v0.102.0, Trilium allows the tree to hide the child notes of particular notes. This works for both Collections and normal notes. + +## Interaction + +When the subtree of a note is hidden, there are a few subtle changes: + +* To indicate that the subtree is hidden, the note will not have an expand button and it will display the number of children to the right. +* It's not possible to add a new note directly from the tree. + * For Collections, it's best to use the built-in mechanism to create notes (for example by creating a new point on a geo-map, or by adding a new row in a table). + * For normal notes, it's still possible to create children via other means such as using the Internal (reference) links system. +* Notes can be dragged from outside the note, case in which they will be cloned into it. + * Instead of switching to the child notes that were copied, the parent note is highlighted instead. + * A notification will indicate this behavior. +* Similarly, features such as cut/copy and then paste into the note will also work. + +## Spotlighting + +
        + +Even if the subtree of a note is hidden, if a child note manages to become active, it will still appear inside the tree in a special state called _spotlighted_. + +During this state, the note remains under its normal hierarchy, so that its easy to tell its location. In addition, this means that: + +* The note position is clearly visible when using the Search. +* The note can still be operated on from the tree, such as adding a Branch prefix or moving it outside the collection. + +The note appears in italics to indicate its temporary display. When switching to another note, the spotlighted note will disappear. + +> [!NOTE] +> Only one note can be highlighted at the time. When working with multiple notes such as dragging them into the collection, no note will be spotlighted. This is intentional to avoid displaying a partial state of the subtree. + +## Working with collections + +By default, some of the Collections will automatically hide their child notes, for example the Kanban Board or the Table. + +The reasoning behind this is that collections are generally opaque to the rest of the notes and they can generate a large amount of sub-notes since they intentionally lack structure (in order to allow easy swapping between views). + +Some types of collections have the child notes intentionally shown, for example the legacy ones (Grid and List), but also the Presentation which requires the tree structure in order to organize and edit the slides. + +To toggle this behavior: + +* In the New Layout, press the Options button underneath the title and uncheck _Hide child notes in tree_. +* Right click the collection note in the Note Tree and select _Advanced_ → _Show subtree_. + +## Working with normal notes + +It's possible to hide the subtree for normal notes as well, not just collections. To do so, right click the note in the Note Tree and select _Advanced_ → _Hide subtree._ \ No newline at end of file diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree_image.png b/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Note Tree/Hiding the subtree_image.png new file mode 100644 index 0000000000000000000000000000000000000000..7b75834e96169c66e784fb2e36fdeb2f151899f2 GIT binary patch literal 4992 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 Date: Sat, 10 Jan 2026 13:06:13 +0200 Subject: [PATCH 60/68] chore(client): address a few requested changes --- apps/client/src/services/content_renderer_text.ts | 2 -- apps/client/src/translations/en/translation.json | 3 ++- apps/client/src/widgets/note_tree.ts | 6 ------ 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/client/src/services/content_renderer_text.ts b/apps/client/src/services/content_renderer_text.ts index 090aa7f03..e72f9e178 100644 --- a/apps/client/src/services/content_renderer_text.ts +++ b/apps/client/src/services/content_renderer_text.ts @@ -83,8 +83,6 @@ async function renderIncludedNotes(contentEl: HTMLElement, seenNoteIds: Set